Comparing version 2.4.0-beta.1 to 2.4.0-beta.2
# Changelog | ||
All notable changes to this project will be documented in this file. | ||
## [2.3.6] - 2020-02-19 | ||
- Emergency version to fix a push made to `npm` in error. | ||
## [2.3.5] - 2020-09-14 | ||
@@ -5,0 +8,0 @@ ### Fixed |
@@ -93,3 +93,8 @@ const { Statement } = require('./Statement'); | ||
} else { | ||
if (options && options.hasOwnProperty('fetchSize')) | ||
if (options && | ||
( | ||
options.hasOwnProperty('fetchSize') || | ||
options.hasOwnProperty('cursor') | ||
) | ||
) | ||
{ | ||
@@ -109,3 +114,8 @@ const cursor = new Cursor(result); | ||
process.nextTick(() => { | ||
if (options && options.hasOwnProperty('fetchSize')) | ||
if (options && | ||
( | ||
options.hasOwnProperty('fetchSize') || | ||
options.hasOwnProperty('cursor') | ||
) | ||
) | ||
{ | ||
@@ -112,0 +122,0 @@ this.odbcConnection.query(sql, parameters, options, (error, result) => { |
@@ -5,3 +5,6 @@ declare namespace odbc { | ||
name: string; | ||
datayType: number; | ||
dataType: number; | ||
columnSize: number; | ||
decimalDigits: number; | ||
nullable: boolean; | ||
} | ||
@@ -54,2 +57,17 @@ | ||
interface ConnectionParameters { | ||
connectionString: string; | ||
connectionTimeout?: number; | ||
loginTimeout?: number; | ||
} | ||
interface PoolParameters { | ||
connectionString: string; | ||
connectionTimeout?: number; | ||
loginTimeout?: number; | ||
initialSize?: number; | ||
incrementSize?: number; | ||
maxSize?: number; | ||
shrink?: boolean; | ||
} | ||
class Connection { | ||
@@ -131,15 +149,15 @@ | ||
function connect(connectionString: string, callback: (error: NodeOdbcError, connection: Connection) => undefined): undefined; | ||
function connect(connectionObject: object, callback: (error: NodeOdbcError, connection: Connection) => undefined): undefined; | ||
function connect(connectionObject: ConnectionParameters, callback: (error: NodeOdbcError, connection: Connection) => undefined): undefined; | ||
function connect(connectionString: string): Promise<Connection>; | ||
function connect(connectionObject: object): Promise<Connection>; | ||
function connect(connectionObject: ConnectionParameters): Promise<Connection>; | ||
function pool(connectionString: string, callback: (error: NodeOdbcError, pool: Pool) => undefined): undefined; | ||
function pool(connectionObject: object, callback: (error: NodeOdbcError, pool: Pool) => undefined): undefined; | ||
function pool(connectionObject: PoolParameters, callback: (error: NodeOdbcError, pool: Pool) => undefined): undefined; | ||
function pool(connectionString: string): Promise<Pool>; | ||
function pool(connectionObject: object): Promise<Pool>; | ||
function pool(connectionObject: PoolParameters): Promise<Pool>; | ||
} | ||
export = odbc; | ||
export = odbc; |
@@ -1,2 +0,2 @@ | ||
const binary = require('node-pre-gyp'); | ||
const binary = require('@mapbox/node-pre-gyp'); | ||
const path = require('path'); | ||
@@ -38,4 +38,8 @@ | ||
return new Promise(async (resolve, reject) => { | ||
await poolObj.init(); | ||
resolve(poolObj); | ||
try { | ||
await poolObj.init(); | ||
resolve(poolObj); | ||
} catch(e) { | ||
reject(e); | ||
} | ||
}); | ||
@@ -42,0 +46,0 @@ } |
259
lib/Pool.js
const EventEmitter = require('events'); | ||
const async = require('async'); | ||
const binary = require('node-pre-gyp'); | ||
const binary = require('@mapbox/node-pre-gyp'); | ||
const path = require('path'); | ||
@@ -19,3 +19,43 @@ | ||
const LOGIN_TIMEOUT_DEFAULT = 0; | ||
const MAX_ACTIVELY_CONNECTING = 1; | ||
// A queue for tracking the connections | ||
class ConnectionQueue | ||
{ | ||
static queuedConnections = []; | ||
static activelyConnectingCount = 0; | ||
static maxActivelyConnecting = MAX_ACTIVELY_CONNECTING; | ||
static enqueue = async function(pool, promiseGenerator, configObject) | ||
{ | ||
ConnectionQueue.queuedConnections.push({ | ||
pool, | ||
promiseGenerator, | ||
configObject | ||
}); | ||
ConnectionQueue.dequeue(); | ||
return; | ||
} | ||
static dequeue = async function() | ||
{ | ||
if (this.activelyConnectingCount >= this.maxActivelyConnecting) { | ||
return; | ||
} | ||
const item = this.queuedConnections.shift(); | ||
if (!item) { | ||
return; | ||
} | ||
ConnectionQueue.activelyConnectingCount++; | ||
try { | ||
await item.promiseGenerator(item.configObject); | ||
} catch (error) { | ||
// We don't actually do anything with errors here | ||
} | ||
ConnectionQueue.activelyConnectingCount--; | ||
ConnectionQueue.dequeue(); | ||
return; | ||
} | ||
} | ||
class Pool { | ||
@@ -25,11 +65,32 @@ | ||
this.connectionConfig = {}; | ||
this.waitingConnectionWork = []; | ||
this.isOpen = false; | ||
this.freeConnections = []; | ||
this.connectionsBeingCreatedCount = 0; | ||
this.poolSize = 0; | ||
// Keeps track of when connections have sucessfully connected | ||
this.connectionEmitter = new EventEmitter(); | ||
// Fires when a connection has been made | ||
this.connectionEmitter.on('connected', (connection) => { | ||
if (this.connectionQueue.length > 0) | ||
// A connection has finished connecting, but there is some waiting call | ||
// to connect() that is waiting for a connection. shift() that work from | ||
// the front of the waitingConnectionWork queue, then either call the | ||
// callback function provided by the user, or resolve the Promise that was | ||
// returned to the user. | ||
if (this.waitingConnectionWork.length > 0) | ||
{ | ||
let connectionWork = this.connectionQueue.pop(); | ||
let connectionWork = this.waitingConnectionWork.shift(); | ||
// If the user passed a callback function, then call the function with | ||
// no error in the first parameter and the connection in the second | ||
// parameter | ||
if (typeof connectionWork == 'function') | ||
{ | ||
// callback function | ||
return connectionWork(null, connection); | ||
// If the user didn't pass a callback function, we returned a promise to | ||
// them. Resolve that promise with the connection that was just created. | ||
} else { | ||
@@ -39,2 +100,7 @@ // Promise (stored resolve function) | ||
} | ||
// A connection finished connecting, and there was no work waiting for | ||
// that connection to be made. Simply add it to the array of | ||
// freeConnections, and the next time work comes in it can simply be | ||
// retrieved from there. | ||
} else { | ||
@@ -45,11 +111,6 @@ this.freeConnections.push(connection); | ||
this.isOpen = false; | ||
this.freeConnections = []; | ||
this.connectingCount = 0; | ||
this.poolSize = 0; | ||
this.connectionQueue = []; | ||
// connectionString is a... | ||
// connectionString is a string, so use defaults for all of the | ||
// configuration options. | ||
if (typeof connectionString === 'string') { | ||
this.connectionString = connectionString; | ||
this.connectionConfig.connectionString = connectionString; | ||
@@ -61,6 +122,9 @@ this.reuseConnections = REUSE_CONNECTIONS_DEFAULT; | ||
this.shrink = SHRINK_DEFAULT; | ||
this.connectionTimeout = CONNECTION_TIMEOUT_DEFAULT; | ||
this.loginTimeout = LOGIN_TIMEOUT_DEFAULT; | ||
} else if (typeof connectionString === 'object') { | ||
this.connectionConfig.connectionTimeout = CONNECTION_TIMEOUT_DEFAULT; | ||
this.connectionConfig.loginTimeout = LOGIN_TIMEOUT_DEFAULT; | ||
} | ||
// connectionString is an object, so ensure that connectionString is a | ||
// property on that object and then copy over any configuration options. | ||
else if (typeof connectionString === 'object') | ||
{ | ||
const configObject = connectionString; | ||
@@ -70,3 +134,3 @@ if (!Object.prototype.hasOwnProperty.call(configObject, 'connectionString')) { | ||
} | ||
this.connectionString = configObject.connectionString; | ||
this.connectionConfig.connectionString = configObject.connectionString; | ||
@@ -89,7 +153,18 @@ // reuseConnections | ||
// connectionTimeout | ||
this.connectionTimeout = configObject.connectionTimeout !== undefined ? configObject.connectionTimeout : CONNECTION_TIMEOUT_DEFAULT; | ||
this.connectionConfig.connectionTimeout = configObject.connectionTimeout !== undefined ? configObject.connectionTimeout : CONNECTION_TIMEOUT_DEFAULT; | ||
// loginTimeout | ||
this.loginTimeout = configObject.loginTimeout !== undefined ? configObject.loginTimeout : LOGIN_TIMEOUT_DEFAULT; | ||
} else { | ||
this.connectionConfig.loginTimeout = configObject.loginTimeout !== undefined ? configObject.loginTimeout : LOGIN_TIMEOUT_DEFAULT; | ||
// connectingQueueMax | ||
// unlike other configuration values, this one is set statically on the | ||
// ConnectionQueue object and not on the Pool intance | ||
if (configObject.maxActivelyConnecting !== undefined) | ||
{ | ||
ConnectionQueue.maxActivelyConnecting = configObject.maxActivelyConnecting | ||
} | ||
} | ||
// connectionString was neither a string nor and object, so throw an error. | ||
else | ||
{ | ||
throw TypeError('Pool constructor must passed a connection string or a configuration object'); | ||
@@ -99,5 +174,2 @@ } | ||
// TODO: Documentation | ||
// TODO: Does this need to be async? | ||
// returns a open connection, ready to use. | ||
@@ -114,7 +186,7 @@ // should overwrite the 'close' function of the connection, and rename it is 'nativeClose', so | ||
// equal to the number of connections connecting, and the number of | ||
// connections in the pool, in the process of connecting and that will be | ||
// connections in the pool, in the process of connecting, and that will be | ||
// added is less than the maximum number of allowable connections, then | ||
// we will need to create MORE connections. | ||
if (this.connectingCount <= this.connectionQueue.length && | ||
this.poolSize + this.connectingCount + this.incrementSize <= this.maxSize) | ||
if (this.connectionsBeingCreatedCount <= this.waitingConnectionWork.length && | ||
this.poolSize + this.connectionsBeingCreatedCount + this.incrementSize <= this.maxSize) | ||
{ | ||
@@ -124,2 +196,6 @@ this.increasePoolSize(this.incrementSize); | ||
// If no callback was provided when connect was called, we need to create | ||
// a promise to return back. We also need to save off the resolve function | ||
// of that promises callback, so that we can call it to resolve the | ||
// function we returned | ||
if (typeof callback == 'undefined') { | ||
@@ -135,9 +211,21 @@ let resolveConnectionPromise; | ||
} | ||
this.connectionQueue.unshift(promiseObj); | ||
// push the promise onto the waitingConnectionWork queue, then return | ||
// it to the user | ||
this.waitingConnectionWork.push(promiseObj); | ||
return promise; | ||
} else { | ||
this.connectionQueue.unshift(callback) | ||
} | ||
// If a callback was provided, we can just add that to the | ||
// waitingConnectionWork queue, then return undefined to the user. Their | ||
// callback will execute when a connection is ready | ||
else | ||
{ | ||
this.waitingConnectionWork.push(callback) | ||
return undefined; | ||
} | ||
} else { | ||
} | ||
// Else, there was a free connection available for the user, so either | ||
// return an immediately resolved promise, or call their callback | ||
// immediately. | ||
else | ||
{ | ||
connection = this.freeConnections.pop(); | ||
@@ -261,74 +349,63 @@ | ||
// odbc.connect runs on an AsyncWorker, so this is truly non-blocking | ||
async increasePoolSize(count) { | ||
this.connectingCount += count; | ||
const connectionConfig = { | ||
connectionString: this.connectionString, | ||
connectionTimeout: this.connectionTimeout, | ||
loginTimeout: this.loginTimeout, | ||
}; | ||
return new Promise(async (resolve, reject) => { | ||
const connectArray = []; | ||
for (let i = 0; i < count; i += 1) { | ||
let promise = new Promise((resolve, reject) => { | ||
odbc.connect(connectionConfig, (error, nativeConnection) => { | ||
this.connectingCount--; | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
generateConnectPromise = function(connectionConfig) { | ||
return new Promise((resolve, reject) => { | ||
odbc.connect(connectionConfig, (error, nativeConnection) => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
this.poolSize++; | ||
let connection = new Connection(nativeConnection); | ||
connection.nativeClose = connection.close; | ||
this.pool.connectionsBeingCreatedCount--; | ||
this.pool.poolSize++; | ||
let connection = new Connection(nativeConnection); | ||
connection.nativeClose = connection.close; | ||
if (this.reuseConnections) { | ||
connection.close = async (closeCallback = undefined) => { | ||
this.connectionEmitter.emit('connected', connection); | ||
if (this.pool.reuseConnections) { | ||
connection.close = async (closeCallback = undefined) => { | ||
this.pool.connectionEmitter.emit('connected', connection); | ||
if (typeof closeCallback === 'undefined') { | ||
return new Promise((resolve, reject) => { | ||
resolve(); | ||
}) | ||
} | ||
if (typeof closeCallback === 'undefined') { | ||
return new Promise((resolve, reject) => { | ||
resolve(); | ||
}) | ||
} | ||
return closeCallback(null); | ||
}; | ||
} else { | ||
connection.close = async (closeCallback = undefined) => { | ||
this.increasePoolSize(1); | ||
if (typeof closeCallback === 'undefined') { | ||
return new Promise((resolve, reject) => { | ||
connection.nativeClose((error, result) => { | ||
this.poolSize--; | ||
if (error) { | ||
reject(error); | ||
} else { | ||
resolve(result); | ||
} | ||
}); | ||
}); | ||
} | ||
connection.nativeClose(closeCallback); | ||
} | ||
return closeCallback(null); | ||
}; | ||
} else { | ||
connection.close = async (closeCallback = undefined) => { | ||
this.pool.increasePoolSize(1); | ||
if (typeof closeCallback === 'undefined') { | ||
return new Promise((resolve, reject) => { | ||
connection.nativeClose((error, result) => { | ||
this.pool.poolSize--; | ||
if (error) { | ||
reject(error); | ||
} else { | ||
resolve(result); | ||
} | ||
}); | ||
}); | ||
} | ||
connection.nativeClose(closeCallback); | ||
} | ||
} | ||
this.connectionEmitter.emit('connected', connection); | ||
resolve(); | ||
}); | ||
}); | ||
connectArray.push(promise); | ||
} | ||
if (connectArray.length > 0) | ||
{ | ||
await Promise.race(connectArray); | ||
this.pool.connectionEmitter.emit('connected', connection); | ||
resolve(); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
} | ||
// odbc.connect runs on an AsyncWorker, so this is truly non-blocking | ||
async increasePoolSize(count) { | ||
this.connectionsBeingCreatedCount += count; | ||
for (let i = 0; i < count; i++) | ||
{ | ||
ConnectionQueue.enqueue(this, this.generateConnectPromise, this.connectionConfig); | ||
} | ||
} | ||
} | ||
module.exports.Pool = Pool; |
{ | ||
"name": "odbc", | ||
"description": "unixodbc bindings for node", | ||
"version": "2.4.0-beta.1", | ||
"version": "2.4.0-beta.2", | ||
"homepage": "http://github.com/markdirish/node-odbc/", | ||
@@ -41,5 +41,5 @@ "main": "lib/odbc.js", | ||
"dependencies": { | ||
"@mapbox/node-pre-gyp": "^1.0.5", | ||
"async": "^3.0.1", | ||
"node-addon-api": "^3.0.2", | ||
"node-pre-gyp": "^0.15.0" | ||
"node-addon-api": "^3.0.2" | ||
}, | ||
@@ -52,3 +52,3 @@ "gypfile": true, | ||
"eslint-plugin-import": "^2.16.0", | ||
"mocha": "^5.2.0" | ||
"mocha": "^8.4.0" | ||
}, | ||
@@ -55,0 +55,0 @@ "binary": { |
@@ -1,9 +0,11 @@ | ||
node-odbc | ||
--------- | ||
# odbc | ||
An asynchronous interface for Node.js to unixODBC and its supported drivers. | ||
requirements | ||
------------ | ||
--- | ||
## Requirements | ||
* unixODBC binaries and development libraries for module compilation | ||
@@ -16,10 +18,16 @@ * on Ubuntu/Debian `sudo apt-get install unixodbc unixodbc-dev` | ||
* on IBM i `yum install unixODBC unixODBC-devel` (requires [yum](http://ibm.biz/ibmi-rpms)) | ||
* odbc drivers for target database | ||
* ODBC drivers for target database | ||
* properly configured odbc.ini and odbcinst.ini. | ||
install | ||
------- | ||
--- | ||
`node-odbc` is an ODBC database interface for Node.js. It allows connecting to any database management system if the system has been correctly configured, including installing of unixODBC and unixODBC-devel packages, installing an ODBC driver for your desired database, and configuring your odbc.ini and odbcinst.ini files. By using an ODBC, and it makes remote development a breeze through the use of ODBC data sources, and switching between DBMS systems is as easy as modifying your queries, as all your code can stay the same. | ||
## Node.js Version Support | ||
This package is a native addon, built with C++ code using `node-addon-api`, a C++ wrapper for [N-API](https://nodejs.org/api/n-api.html). `node-addon-api` only supports versions of Node.js that are in LTS or newer. [A list of supported versions can be found on the Node.js website](https://nodejs.org/en/about/releases/). Current versions supported include: | ||
* Node.js 10 | ||
* Node.js 12 | ||
* Node.js 14 | ||
* Node.js 15 | ||
--- | ||
@@ -50,2 +58,23 @@ | ||
## Debugging | ||
This package used to contain its own method of tracing ODBC calls, which was enabled by recompiling the package with `DEBUG` defined. Because this information was almost wholly redundant with existing methods of tracing available through ODBC driver managers, it was removed in v2.4.0. | ||
Instead, tracing should be enabled through your driver manager, and that information can be analyzed and included with the description of issues encountered. | ||
* **unixODBC (Linux, MacOS, IBM i):** | ||
In your `odbcinst.ini` file, add the following entry: | ||
``` | ||
[ODBC] | ||
Trace=yes | ||
TraceFile=/tmp/odbc.log | ||
``` | ||
Debug information will be appended to the trace file. | ||
* **ODBC Data Source Administrator (Windows):** | ||
Open up ODBC Data Source Administrator and select the "Tracing" tab. Enter the location where you want the log file to go in **"Log File Path"**, then click **"Start Tracing Now"**. | ||
--- | ||
## Drivers | ||
@@ -1057,3 +1086,3 @@ | ||
* Copyright (c) 2019 IBM | ||
* Copyright (c) 2019, 2021 IBM | ||
* Copyright (c) 2013 Dan VerWeire <dverweire@gmail.com> | ||
@@ -1060,0 +1089,0 @@ * Copyright (c) 2010 Lee Smith <notwink@gmail.com> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1001
1093
305079
+ Added@mapbox/node-pre-gyp@^1.0.5
+ Added@mapbox/node-pre-gyp@1.0.11(transitive)
+ Addedagent-base@6.0.2(transitive)
+ Addedansi-regex@5.0.1(transitive)
+ Addedaproba@2.0.0(transitive)
+ Addedare-we-there-yet@2.0.0(transitive)
+ Addedchownr@2.0.0(transitive)
+ Addedcolor-support@1.1.3(transitive)
+ Addeddebug@4.4.0(transitive)
+ Addeddetect-libc@2.0.3(transitive)
+ Addedemoji-regex@8.0.0(transitive)
+ Addedfs-minipass@2.1.0(transitive)
+ Addedgauge@3.0.2(transitive)
+ Addedhttps-proxy-agent@5.0.1(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedmake-dir@3.1.0(transitive)
+ Addedminipass@3.3.65.0.0(transitive)
+ Addedminizlib@2.1.2(transitive)
+ Addedmkdirp@1.0.4(transitive)
+ Addednode-fetch@2.7.0(transitive)
+ Addednopt@5.0.0(transitive)
+ Addednpmlog@5.0.1(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedrimraf@3.0.2(transitive)
+ Addedsemver@6.3.17.7.1(transitive)
+ Addedstring-width@4.2.3(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedstrip-ansi@6.0.1(transitive)
+ Addedtar@6.2.1(transitive)
+ Addedtr46@0.0.3(transitive)
+ Addedwebidl-conversions@3.0.1(transitive)
+ Addedwhatwg-url@5.0.0(transitive)
+ Addedyallist@4.0.0(transitive)
- Removednode-pre-gyp@^0.15.0
- Removedansi-regex@2.1.1(transitive)
- Removedaproba@1.2.0(transitive)
- Removedare-we-there-yet@1.1.7(transitive)
- Removedchownr@1.1.4(transitive)
- Removedcode-point-at@1.1.0(transitive)
- Removedcore-util-is@1.0.3(transitive)
- Removeddebug@3.2.7(transitive)
- Removeddeep-extend@0.6.0(transitive)
- Removeddetect-libc@1.0.3(transitive)
- Removedfs-minipass@1.2.7(transitive)
- Removedgauge@2.7.4(transitive)
- Removediconv-lite@0.4.24(transitive)
- Removedignore-walk@3.0.4(transitive)
- Removedini@1.3.8(transitive)
- Removedis-fullwidth-code-point@1.0.0(transitive)
- Removedisarray@1.0.0(transitive)
- Removedminimist@1.2.8(transitive)
- Removedminipass@2.9.0(transitive)
- Removedminizlib@1.3.3(transitive)
- Removedmkdirp@0.5.6(transitive)
- Removedneedle@2.9.1(transitive)
- Removednode-pre-gyp@0.15.0(transitive)
- Removednopt@4.0.3(transitive)
- Removednpm-bundled@1.1.2(transitive)
- Removednpm-normalize-package-bin@1.0.1(transitive)
- Removednpm-packlist@1.4.8(transitive)
- Removednpmlog@4.1.2(transitive)
- Removednumber-is-nan@1.0.1(transitive)
- Removedos-homedir@1.0.2(transitive)
- Removedos-tmpdir@1.0.2(transitive)
- Removedosenv@0.1.5(transitive)
- Removedprocess-nextick-args@2.0.1(transitive)
- Removedrc@1.2.8(transitive)
- Removedreadable-stream@2.3.8(transitive)
- Removedrimraf@2.7.1(transitive)
- Removedsafe-buffer@5.1.2(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedsax@1.4.1(transitive)
- Removedsemver@5.7.2(transitive)
- Removedstring-width@1.0.2(transitive)
- Removedstring_decoder@1.1.1(transitive)
- Removedstrip-ansi@3.0.1(transitive)
- Removedstrip-json-comments@2.0.1(transitive)
- Removedtar@4.4.19(transitive)
- Removedyallist@3.1.1(transitive)