Releases: TimelordUK/node-sqlserver-v8
Releases · TimelordUK/node-sqlserver-v8
v2.7.0
- Node 19 binaries.
- 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
- #254 Segmentation Fault while attempting to use Table Value Paramaters
- #255 Bulk insert not possible for tables that contain timestamp
- leak when using promise query on prepared statements.
- Driver={ODBC Driver 18 for SQL Server} support on all platforms
- electron v19 binaries
- linting cpp reduce build warnings
- upload node 18 32 bit windows
- add windows electron v20, v21
v2.5.0
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>
}
-
prepared statements with nvarchar(max) #248
-
prepared statement may cause node crash due to stack corruption.
-
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)
})
- 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
- pool PR fix- thanks #244
- electron v18
- 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
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')
})
v2.4.6
- manually register a stored procedure (See example below)
- Sybase Adaptive Sever support
const sql = require('msnodesqlv8')
const { GetConnection } = require('./get-connection')
const connectionString = new GetConnection().connectionString
const query = 'SELECT top 5 * FROM syscomments'
// "Driver={Adaptive Server Enterprise}; app=myAppName; server=localhost port=5000; db=pubs3; uid=sa; pwd=ooooo;"
function legacyQuery () {
return new Promise((resolve, reject) => {
sql.open(connectionString, (err, con) => {
if (err) {
reject(err)
}
con.query(query, (err, rows) => {
if (err) {
reject(err)
}
con.close(() => {
resolve(rows)
})
})
})
})
}
async function promised () {
const connection = await sql.promises.open(connectionString)
const res = await connection.promises.query(query)
console.log(`promised ${JSON.stringify(res, null, 4)}`)
await connection.promises.close()
return res
}
async function q1 () {
const d = new Date()
try {
const rows = await legacyQuery()
const elapsed = new Date() - d
console.log(`legacyQuery rows.length ${rows.length} elapsed ${elapsed}`)
console.log(`legacyQuery ${JSON.stringify(rows, null, 4)}`)
} catch (err) {
console.error(err)
}
}
async function runProcWith (connection, spName, p) {
console.log(`call proc ${spName} with params ${JSON.stringify(p, null, 4)}`)
const res = await connection.promises.callProc(spName, p)
const returns = res.first[0]['']
console.log(`proc with params returns ${returns}`)
}
async function makeProc (connection, spName) {
try {
const pm = connection.procedureMgr()
const def = `create or replace proc tmp_name_concat
@last_name varchar(30) = "knowles",
@first_name varchar(18) = "beyonce" as
select @first_name + " " + @last_name `
await connection.promises.query(def)
const params = [
pm.makeParam(spName, '@last_name', 'varchar', 30, false),
pm.makeParam(spName, '@first_name', 'varchar', 18, false)
]
const proc = pm.addProc(spName, params)
proc.setDialect(pm.ServerDialect.Sybase)
return proc
} catch (err) {
console.error(err)
}
}
async function proc () {
const connection = await sql.promises.open(connectionString)
const spName = 'tmp_name_concat'
await makeProc(connection, spName)
try {
await runProcWith(connection, spName, {
first_name: 'Baby'
})
await runProcWith(connection, spName, {})
await runProcWith(connection, spName, {
first_name: 'Miley',
last_name: 'Cyrus'
})
await connection.promises.close()
} catch (err) {
console.error(err)
}
}
async function runOutputProcWith (connection, spName, p) {
console.log(`call output proc ${spName} with params ${JSON.stringify(p, null, 4)}`)
const res = await connection.promises.callProc(spName, p)
console.log(`output proc with params returns ${JSON.stringify(res, null, 4)}`)
}
async function makeOutputProc (connection, spName) {
try {
const pm = connection.procedureMgr()
const def = `create or replace proc tmp_square
@num decimal,
@square decimal output as
select @square=@num* @num`
await connection.promises.query(def)
const params = [
pm.makeParam(spName, '@num', 'decimal', 17, false),
pm.makeParam(spName, '@square', 'decimal', 17, true)
]
const proc = pm.addProc(spName, params)
proc.setDialect(pm.ServerDialect.Sybase)
return proc
} catch (err) {
console.error(err)
}
}
async function procOuput () {
const connection = await sql.promises.open(connectionString)
const spName = 'tmp_square'
await makeOutputProc(connection, spName)
try {
await runOutputProcWith(connection, spName, {
num: 15
})
await connection.promises.close()
} catch (err) {
console.error(err)
}
}
async function makeSelectProc (connection, spName) {
try {
const pm = connection.procedureMgr()
const def = `create or replace proc tmp_input_output
@len_last int output,
@len_first int output,
@first_last varchar(48) output,
@last_name varchar(30) = 'knowles',
@first_name varchar(18) = 'beyonce'
as begin
select @first_last = @first_name + " " + @last_name
select @len_first = len(@first_name)
select @len_last = len(@last_name)
select len(@first_last)
end`
await connection.promises.query(def)
const params = [
pm.makeParam(spName, '@len_last', 'int', 4, true),
pm.makeParam(spName, '@len_first', 'int', 4, true),
pm.makeParam(spName, '@first_last', 'varchar', 48, true),
pm.makeParam(spName, '@last_name', 'varchar', 30, false),
pm.makeParam(spName, '@first_name', 'varchar', 18, false)
]
const proc = pm.addProc(spName, params)
proc.setDialect(pm.ServerDialect.Sybase)
return proc
} catch (err) {
console.error(err)
}
}
async function procAsSelect () {
const connection = await sql.promises.open(connectionString)
const spName = 'tmp_input_output'
try {
const proc = await makeSelectProc(connection, spName)
const meta = proc.getMeta()
const select = meta.select
console.log(select)
const res = await connection.promises.query(select, ['Miley', 'Cyrus'])
console.log(JSON.stringify(res, null, 4))
await connection.promises.close()
} catch (err) {
console.error(err)
}
}
async function run () {
await procAsSelect()
await procOuput()
await proc()
await q1()
await promised()
}
run().then(() => {
console.log('done')
})
v2.4.5
-
electron v17 binaries
-
support for thread workers #228
master
const path = require('path')
const filePath = path.resolve(__dirname, './worker-item.js')
const { Worker } = require('worker_threads')
const worker1 = new Worker(filePath)
const worker2 = new Worker(filePath)
function dispatch (worker) {
worker.on('message', msg => {
switch (msg.command) {
case 'task_result': {
console.log(JSON.stringify(msg, null, 4))
}
}
})
worker.on('error', error => {
console.log(error)
})
}
dispatch(worker1)
dispatch(worker2)
function sendTask (worker, num) {
worker.postMessage(
{
command: 'task',
num: num
})
}
function clean () {
setTimeout(async () => {
console.log('exit.')
await Promise.all([
worker1.terminate(),
worker2.terminate()
])
}, 5000)
}
for (let i = 0; i < 40; i += 2) {
sendTask(worker1, i)
sendTask(worker2, i + 1)
}
clean()
worker
const { parentPort } = require('worker_threads')
const sql = require('msnodesqlv8')
const { GetConnection } = require('./get-connection')
const connectionString = new GetConnection().connectionString
async function compute (msg) {
try {
console.log(`worker receive task ${msg.num}`)
const conn = await sql.promises.open(connectionString)
const query = `select ${msg.num} as i, @@SPID as spid`
const res = await conn.promises.query(query)
await conn.promises.close()
parentPort.postMessage(
{
command: 'task_result',
data: `spid ${res.first[0].spid}`,
num: msg.num,
fib: getFib(msg.num)
})
} catch (e) {
parentPort.emit('error', e)
}
}
parentPort.on('message', async msg => {
switch (msg.command) {
case 'task': {
await compute(msg)
break
}
default: {
console.log(`unknown command ${msg.command}`)
break
}
}
})
function getFib (num) {
if (num === 0) {
return 0
} else if (num === 1) {
return 1
} else {
return getFib(num - 1) + getFib(num - 2)
}
}
v2.4.4
v2.4.3
patch node version to reload binaries