Skip to content

Releases: TimelordUK/node-sqlserver-v8

v4.1.0

22 May 20:06
46b0c00

Choose a tag to compare

typescript users note types are now split into their own module

import { Error, PoolOptions, Query, SqlClient, QueryDescription, Pool, PoolStatusRecord } from 'msnodesqlv8/types'
const sql: SqlClient = require('msnodesqlv8')
  1. #284

proven solution for Debian, Ubuntu running node 18, 20 experiencing SEGV core dump - install from src openssl 3.2

see appveyor and tool/openssl.sh

  1. #285

tested platforms

OS Node Version ODBC Version SSL version action
Windows 10 12, 14, 16, 18, 20 17, 18 leave default
Windows 11 18, 20 18 leave default
Ubuntu 22.04.2 18, 20 17, 18 openssl 3.2 e.g. tool/openssl.sh
Ubuntu 20.04 18, 20 18 openssl 3.2 e.g. tool/openssl.sh
Ubuntu 20.04 12, 14, 16 17, 18 openssl 1.1 leave default
Lubuntu 20.04 18, 20 18 openssl 3.2 e.g. tool/openssl.sh
Debian 18, 20 17, 18 openssl 3.2 e.g tool/openssl.sh
Alpine 3.16 18 18 openssl 1.1.1t leave default
MacOS Big Sur 18, 20 18 libressl 2.8.3 leave default
  1. #287 - unicode 16 bit col names

  2. electron 25 binaries

  3. electron 26 binaries

v4.0.0

08 May 11:52
a9732a0

Choose a tag to compare

v4.0.0 Pre-release
Pre-release

typescript users note types are now split into their own module

import { Error, PoolOptions, Query, SqlClient, QueryDescription, Pool, PoolStatusRecord } from 'msnodesqlv8/types'
const sql: SqlClient = require('msnodesqlv8')
  1. #284

proven solution for Debian, Ubuntu running node 18, 20 experiencing SEGV core dump - install from src openssl 3.2

see appveyor and tool/openssl.sh

  1. #285

tested platforms

OS Node Version ODBC Version SSL version action
Windows 10 12, 14, 16, 18, 20 17, 18 leave default
Windows 11 18, 20 18 leave default
Ubuntu 22.04.2 18, 20 17, 18 openssl 3.2 e.g. tool/openssl.sh
Ubuntu 20.04 18, 20 18 openssl 3.2 e.g. tool/openssl.sh
Ubuntu 20.04 12, 14, 16 17, 18 openssl 1.1 leave default
Lubuntu 20.04 18, 20 18 openssl 3.2 e.g. tool/openssl.sh
Debian 18, 20 17, 18 openssl 3.2 e.g tool/openssl.sh
Alpine 3.16 18 18 openssl 1.1.1t leave default
MacOS Big Sur 18, 20 18 libressl 2.8.3 leave default

v3.1.0

12 Mar 17:18

Choose a tag to compare

What's Changed

  1. Bump qs from 6.5.2 to 6.5.3 by @dependabot in #269

  2. Bump json5 from 1.0.1 to 1.0.2 by @dependabot in #272

  3. allow more than 1 installed MS driver in linux and pick up latest for gyp build
    Gyp by @TimelordUK in #274

  4. missing results in sproc see #276 PR #280

  5. binaries for node v20

  6. binaries for latest electron including preview v24

  7. continued modernisation of JS into classes for better maintenance

v3.0.1

20 Dec 17:49

Choose a tag to compare

This is a release containing a lot of reworking under the covers and should hence be tested thoroughly before deployment into production systems.

the focus has mainly been on precise binding specifically for stored procedures and bound tables such that always on encryption will work with all ODBC supported types that are compatible with this feature. datetimeoffset, time(n), date, real, datetime2, numeric, binary(n), varbinary(n), decimal, nvarchar(n), char(n), bit, bigint, tinyint, smallint, int all work with table binding and procedure calls.

float via encryption works only for a single parameter value in stored proc it does not work for bulk table binding.

money types do not work with ODBC and always on encryption

when using general queries conn.query('select * from mytable', ...) there will be no change in way the driver works - the type bound in driver on a parameter is essentilly 'guessed' based on presented JS type. This works fine as the server will translate over to exact column type - this will not work with always on encryption and you will likely receive errors stating there is a type mismatch between the guess made and actual column definition.

tvp is also not supported.

new promise to cancel a query from returned Query

  it('use promise on query to cancel', async function handler () {
    const conn = env.theConnection
    const waitsql = env.waitForSql(20)
    const q = conn.query(waitsql)
    const err = await q.promises.cancel()
    assert(err.message.includes('Operation canceled'))
  })

new promise to call on a fetched procedure

    await createProc(spName)
    const msg = 'hello world'
    const proc = await env.theConnection.promises.getProc(spName)
    const res = await proc.promises.call({
      param: msg
    })

image

improved typings and JSDoc in index.d.ts - part 1 this will be continued in future releases, any contributions from TS typings experts very much welcome.

any changes must be compatible with current typescript test code

i.e. npm run build must compile

npm run build
> msnodesqlv8@3.0.0 build
> tsc


msnodesqlv8 git(master) node .\dist\samples\typescript\builder.js
IF OBJECT_ID('dbo.tmpTableBuilder', 'U') IS NOT NULL DROP TABLE dbo.tmpTableBuilder;
CREATE TABLE dbo.tmpTableBuilder ([id] int , [col_a] int NOT NULL, [col_b] varchar (100) NOT NULL, [col_c] int NOT NULL, [col_d] int NOT NULL, [col_e] varchar (100) NOT NULL)
[
    {
        "id": 0,
        "col_a": 0,
        "col_b": "str_0",
        "col_c": 1,
        "col_d": -1,
        "col_e": "str2_0"
    },
    {
        "id": 1,
        "col_a": 5,
        "col_b": "str_1",
        "col_c": 2,
        "col_d": 0,
        "col_e": "str2_1"
    },
    {
        "id": 2,
        "col_a": 10,
        "col_b": "str_2",
        "col_c": 3,
        "col_d": 1,
        "col_e": "str2_2"
    },
    {
        "id": 3,
        "col_a": 15,
        "col_b": "str_3",
        "col_c": 4,
        "col_d": 2,
        "col_e": "str2_3"
    },
    {
        "id": 4,
        "col_a": 20,
        "col_b": "str_4",
        "col_c": 5,
        "col_d": 3,
        "col_e": "str2_4"
    }
]
CREATE TYPE dbo.tmpTableBuilderType AS TABLE ([id] int , [col_a] int , [col_b] varchar (100) , [col_c] int , [col_d] int , [col_e] varchar (100) )
create or alter procedure dbo.tmpTableBuilder_tvp_inserter
    (
      @tvp dbo.tmpTableBuilderType READONLY
    )
    AS
    BEGIN
      set nocount on
      INSERT INTO dbo.tmpTableBuilder
      (
        [id],
                [col_a],
                [col_b],
                [col_c],
                [col_d],
                [col_e]
      )
      SELECT
        [id],
                [col_a],
                [col_b],
                [col_c],
                [col_d],
                [col_e]
      n FROM @tvp tvp
    END
id 0 equals true
id 1 equals true
id 2 equals true
id 3 equals true
id 4 equals true
done

#265

always on encryption now working on stored proc and bound table calls - please see tests\encrypt.js

e.g with table below

CREATE TABLE node.dbo.test_encrpted_table (
[id] int IDENTITY(1,1) NOT NULL, [field] int ENCRYPTED WITH 
     (COLUMN_ENCRYPTION_KEY = [CEK_Auto1],   
     ENCRYPTION_TYPE = Deterministic, 
    ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'))
    const table = await conn.promises.getTable('test_encrpted_table')
    const v = 
[
  {
    field: 12345,
  },
]
    await table.promises.insert(v)
   

tables bound with numeric columns now encode correctly into ODBC rather than using doubles which had been case previously.

electron v22 binaries included

refactored table binding and stored procedures code.

Full Changelog: v3.0.0...v3.0.1

always on encryption

30 Nov 23:01
d32dd51

Choose a tag to compare

new promise to cancel a query from returned Query

  it('use promise on query to cancel', async function handler () {
    const conn = env.theConnection
    const waitsql = env.waitForSql(20)
    const q = conn.query(waitsql)
    const err = await q.promises.cancel()
    assert(err.message.includes('Operation canceled'))
  })

new promise to call on a fetched procedure

    await createProc(spName)
    const msg = 'hello world'
    const proc = await env.theConnection.promises.getProc(spName)
    const res = await proc.promises.call({
      param: msg
    })

image

improved typings and JSDoc in index.d.ts - part 1 this will be continued in future releases, any contributions from TS typings experts very much welcome.

any changes must be compatible with current typescript test code

i.e. npm run build must compile

npm run build
> msnodesqlv8@3.0.0 build
> tsc


msnodesqlv8 git(master) node .\dist\samples\typescript\builder.js
IF OBJECT_ID('dbo.tmpTableBuilder', 'U') IS NOT NULL DROP TABLE dbo.tmpTableBuilder;
CREATE TABLE dbo.tmpTableBuilder ([id] int , [col_a] int NOT NULL, [col_b] varchar (100) NOT NULL, [col_c] int NOT NULL, [col_d] int NOT NULL, [col_e] varchar (100) NOT NULL)
[
    {
        "id": 0,
        "col_a": 0,
        "col_b": "str_0",
        "col_c": 1,
        "col_d": -1,
        "col_e": "str2_0"
    },
    {
        "id": 1,
        "col_a": 5,
        "col_b": "str_1",
        "col_c": 2,
        "col_d": 0,
        "col_e": "str2_1"
    },
    {
        "id": 2,
        "col_a": 10,
        "col_b": "str_2",
        "col_c": 3,
        "col_d": 1,
        "col_e": "str2_2"
    },
    {
        "id": 3,
        "col_a": 15,
        "col_b": "str_3",
        "col_c": 4,
        "col_d": 2,
        "col_e": "str2_3"
    },
    {
        "id": 4,
        "col_a": 20,
        "col_b": "str_4",
        "col_c": 5,
        "col_d": 3,
        "col_e": "str2_4"
    }
]
CREATE TYPE dbo.tmpTableBuilderType AS TABLE ([id] int , [col_a] int , [col_b] varchar (100) , [col_c] int , [col_d] int , [col_e] varchar (100) )
create or alter procedure dbo.tmpTableBuilder_tvp_inserter
    (
      @tvp dbo.tmpTableBuilderType READONLY
    )
    AS
    BEGIN
      set nocount on
      INSERT INTO dbo.tmpTableBuilder
      (
        [id],
                [col_a],
                [col_b],
                [col_c],
                [col_d],
                [col_e]
      )
      SELECT
        [id],
                [col_a],
                [col_b],
                [col_c],
                [col_d],
                [col_e]
      n FROM @tvp tvp
    END
id 0 equals true
id 1 equals true
id 2 equals true
id 3 equals true
id 4 equals true
done

#265

always on encryption now working on stored proc and bound table calls - please see tests\encrypt.js

e.g with table below

CREATE TABLE node.dbo.test_encrpted_table (
[id] int IDENTITY(1,1) NOT NULL, [field] int ENCRYPTED WITH 
     (COLUMN_ENCRYPTION_KEY = [CEK_Auto1],   
     ENCRYPTION_TYPE = Deterministic, 
    ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'))
    const table = await conn.promises.getTable('test_encrpted_table')
    const v = 
[
  {
    field: 12345,
  },
]
    await table.promises.insert(v)
   

electron v22 binaries included

v2.7.0

09 Oct 17:49

Choose a tag to compare

  1. Node 19 binaries.
  2. better handling of blocked long running queries #264
const timeoutSproc = `CREATE OR ALTER PROCEDURE <name>
AS
BEGIN
\tSET NOCOUNT ON;
\tSET XACT_ABORT ON;
\tWAITFOR DELAY '00:10'; -- 10 minutes
END;
`


    it('pool: sproc with timeout early terminates - check pool', async function handler () {
      const spName = 'timeoutTest'
      const size = 4
      const pool = env.pool(size)
      await pool.promises.open()
      await env.promisedCreate(spName, timeoutSproc)
      try {
        await pool.promises.callProc(spName, {}, { timeoutMs: 2000 })
        throw new Error('expected exception')
      } catch (err) {
        assert(err)
        assert(err.message.includes('Query cancelled'))
        const res = await pool.promises.query('select 1 as n')
        expect(res.first[0].n).equals(1)
      } finally {
        await pool.close()
      }
    })

v2.6.0

10 Jul 18:50

Choose a tag to compare

  1. #254 Segmentation Fault while attempting to use Table Value Paramaters
  2. #255 Bulk insert not possible for tables that contain timestamp
  3. leak when using promise query on prepared statements.
  4. Driver={ODBC Driver 18 for SQL Server} support on all platforms
  5. electron v19 binaries
  6. linting cpp reduce build warnings
  7. upload node 18 32 bit windows
  8. add windows electron v20, v21

v2.5.0

12 Jun 18:36

Choose a tag to compare

  1. PR fix - thanks #251

  2. pool extended with additional promises to match connection #246

export interface AggregatorPromises {
    query(sql: string, params?: any[], options?: QueryAggregatorOptions): Promise<QueryAggregatorResults>
    callProc(name: string, params?: any, options?: QueryAggregatorOptions): Promise<QueryAggregatorResults>
}

export interface PoolPromises extends AggregatorPromises {
    open(): Promise<Pool>
    close(): Promise<any>
    getUserTypeTable(name: string): Promise<Table>
    getTable(name: string): Promise<BulkTableMgr>
    getProc(name: string): Promise<ProcedureDefinition>
}
  1. prepared statements with nvarchar(max) #248

  2. prepared statement may cause node crash due to stack corruption.

  3. use promise query to add missing column names

  it('select promise query with no column name', async function handler () {
    const r1 = await env.theConnection.promises.query('select 1,2 union select 3,4', [],
      { replaceEmptyColumnNames: true })
    console.log(JSON.stringify(r1.first, null, 0))
    expect(r1.first[0].Column0).is.equal(1)
    expect(r1.first[0].Column1).is.equal(2)
    expect(r1.first[1].Column0).is.equal(3)
    expect(r1.first[1].Column1).is.equal(4)
  })
  1. significant refactor of test framework using bdd mocha, chai

call proc from connection pool (like connection)

 it('use pool to call proc', async function handler () {
    const pool = env.pool()
    await pool.open()
    const spName = 'test_sp_get_optional_p'
    const a = 10
    const b = 20
    const def = `alter PROCEDURE <name> (
      @a INT = ${a},
      @b INT = ${b},
      @plus INT out
      )
    AS begin
      -- SET XACT_ABORT ON;
      SET NOCOUNT ON;
      set @plus = @a + @b;
    end;
`
    await env.promisedCreate(spName, def)
    const expected = [
      0,
      a + b
    ]
    const o = {}
    const res = await pool.promises.callProc(spName, o)
    expect(res.output).to.be.deep.equal(expected)
    await pool.close()
  })

use bulk table manager on pool (like connection)

  it('use tableMgr on pool bulk insert varchar vector - exactly 4000 chars', async function handler () {
    const pool = env.pool(4)
    await pool.promises.open()
    const b = env.repeat('z', 4000)
    const helper = env.typeTableHelper('NVARCHAR(MAX)', pool)
    const expected = helper.getVec(10, i => b)
    const table = await helper.create()
    const promisedInsert = table.promises.insert
    const promisedSelect = table.promises.select

    await promisedInsert(expected)
    const res = await promisedSelect(expected)
    expect(res).to.be.deep.equals(expected)
    await pool.close()
  })

use tvp on pool (like connection)

  it('use pool for tvp insert', async function handler () {
    const pool = env.pool(4)
    await pool.promises.open()
    const tableName = 'TestTvp'
    const helper = env.tvpHelper(tableName, pool)
    const vec = helper.getExtendedVec(1)
    const table = await helper.create(tableName)
    table.addRowsFromObjects(vec)
    const tp = env.sql.TvpFromTable(table)
    const insertSql = helper.callProcWithTVpSql
    // 'exec insertTestTvp @tvp = ?;'
    await pool.promises.query(insertSql, [tp])
    // use a connection having inserted with pool
    const res = await pool.promises.query(`select * from ${tableName}`)
    expect(res.first).to.be.deep.equal(vec)
    await pool.close()
  })

v2.4.8

08 May 15:43

Choose a tag to compare

  1. pool PR fix- thanks #244
  2. electron v18
  3. query that returns BigInt (and all numeric) as strings rather than numbers
test('query a bigint implicit - configure query to return as string', testDone => {
    async function runner () {
      const num = '9223372036854775807'
      const q = `SELECT ${num} as number`
      const res = await theConnection.promises.query({
        query_str: q,
        numeric_string: true
      })
      try {
        assert.deepStrictEqual(res.first[0].number, num)
      } catch (e) {
        assert.ifError(e)
      }
    }
    runner().then(() => {
      testDone()
    })
  })

v2.4.7

13 Mar 18:21

Choose a tag to compare

  1. manually register a table - works with Sybase or SqlServer
  2. #240
  3. #237
  4. v18 binaries
import { SqlClient, TableBuilder, Connection, TableManager } from 'msnodesqlv8';

// require the module so it can be used in your node JS code.
export const sql : SqlClient = require('msnodesqlv8');
const path = require('path')
const { GetConnection } = require(path.join(__dirname, '..\\javascript\\', '../javascript/get-connection'))

const connectionString: string = new GetConnection().connectionString

async function builder () {
    function makeOne (i: number): any {
      return {
        id: i,
        col_a: i * 5,
        col_b: `str_${i}`,
        col_c: i + 1,
        col_d: i - 1,
        col_e: `str2_${i}`
      }
    }
  
    try {
      const rows = 5
      const connection: Connection = await sql.promises.open(connectionString)
      const tableName = 'tmpTableBuilder'
      const mgr: TableManager = connection.tableMgr()
      const builder: TableBuilder = mgr.makeBuilder(tableName)
      builder.setDialect(mgr.ServerDialect.SqlServer)

      builder.addColumn('id').asInt().isPrimaryKey(1)
      builder.addColumn('col_a').asInt()
      builder.addColumn('col_b').asVarChar(100)
      builder.addColumn('col_c').asInt()
      builder.addColumn('col_d').asInt()
      builder.addColumn('col_e').asVarChar(100)
      
      const vec = []
      for (let i = 0; i < rows; ++i) {
        vec.push(makeOne(i))
      }
      const t = builder.toTable()
      const create: string = builder.createTableSql
      const drop: string = builder.dropTableSql
      console.log(drop)
      await builder.drop()
      console.log(create)
      await builder.create()
      await t.promises.insert(vec)
      const keys: any[] = t.keys(vec)
      const res: any[] = await t.promises.select(keys)
      console.log(JSON.stringify(res, null, 4))
      await builder.drop()
      await connection.promises.close()
    } catch (e) {
      console.log(e)
    }
  }

  async function run () {
    await builder()
  }
  
  run().then(() => {
    console.log('done')
  })