UNPKG

@zakodium/sybase

Version:

A simple node.js wrapper around a java/jconnect app that provides easy access to Sybase Databases without having to install odbc or freetds.

200 lines (172 loc) 4.89 kB
var spawn = require('child_process').spawn; var JSONStream = require('JSONStream'); var fs = require('fs'); var path = require('path'); const util = require('util'); function Sybase( host, port, dbname, username, password, timeout, logTiming, pathToJavaBridge ) { this.connected = false; this.host = host; this.port = port; this.dbname = dbname; this.username = username; this.password = password; this.timeout = timeout; this.logTiming = logTiming == true; this.readyTimeout = 3000; this.pathToJavaBridge = pathToJavaBridge; if (this.pathToJavaBridge === undefined) { this.pathToJavaBridge = path.resolve( __dirname, '..', 'JavaSybaseLink', 'dist', 'JavaSybaseLink.jar' ); } this.queryCount = 0; this.currentMessages = {}; // look up msgId to message sent and call back details. } Sybase.prototype.connect = function (callback) { var that = this; if (this.connected) { callback(null); return; } this.javaDB = spawn('java', [ '-jar', this.pathToJavaBridge, this.host, this.port, this.dbname, this.username, this.password, ]); this.javaDB.stdout.once('data', function (data) { if ((data + '').trim() != 'connected') { callback(new Error('Error connecting ' + data)); return; } that.javaDB.stderr.removeAllListeners('data'); that.connected = true; // set up normal listeners. that.javaDB.stdout .setEncoding('utf8') .pipe(JSONStream.parse()) .on('data', function (jsonMsg) { that.onSQLResponse.call(that, jsonMsg); }); that.javaDB.stderr.on('data', function (err) { that.onSQLError.call(that, err); }); callback(null); }); // handle connection issues. this.javaDB.stderr.once('data', function (data) { that.disconnect(); callback(new Error(data)); }); }; Sybase.prototype.disconnect = function () { if (this.javaDB) { this.javaDB.stdout.removeAllListeners('data'); this.javaDB.kill(); } this.javaDB = null; this.connected = false; }; Sybase.prototype._query = function (sql, callback) { const that = this; if (this.connected === false) { callback(new Error("database isn't connected.")); return; } var hrstart = process.hrtime(); this.queryCount++; var msg = {}; msg.msgId = this.queryCount; msg.sql = sql; msg.sentTime = new Date().getTime(); var strMsg = JSON.stringify(msg).replace(/[\n]/g, '\\n'); msg.callback = callback; msg.hrstart = hrstart; this.currentMessages[msg.msgId] = msg; this.javaDB.stdin.write(strMsg + '\n'); if (this.timeout) { setTimeout(() => { const request = that.currentMessages[msg.msgId]; if (request) { request.callback(new Error('timeout')); delete that.currentMessages[msg.msgId]; } }, this.timeout); } }; Sybase.prototype.query = async function (queryString, autoConnect) { if (autoConnect && !this.connected) { await this.connect(); } try { const result = await this._query(queryString); return result; } catch (err) { if (err && err.message.includes('JZ0C0')) { // this code means database was disconnected // We attempt to reconnect this.disconnect(); await this.connect(); const result = await this._query(queryString); return result; } else { throw err; } } }; Sybase.prototype.onSQLResponse = function (jsonMsg) { var err = null; var request = this.currentMessages[jsonMsg.msgId]; if (!request) return; delete this.currentMessages[jsonMsg.msgId]; var result = jsonMsg.result; if (result.length === 1) result = result[0]; //if there is only one just return the first RS not a set of RS's var currentTime = new Date().getTime(); var sendTimeMS = currentTime - jsonMsg.javaEndTime; hrend = process.hrtime(request.hrstart); var javaDuration = jsonMsg.javaEndTime - jsonMsg.javaStartTime; if (jsonMsg.error !== undefined) err = new Error(jsonMsg.error); if (this.logTiming) console.log( 'Execution time (hr): %ds %dms dbTime: %dms dbSendTime: %d sql=%s', hrend[0], hrend[1] / 1000000, javaDuration, sendTimeMS, request.sql ); request.callback(err, result); }; Sybase.prototype.onSQLError = function (data) { var error = new Error(data); var callBackFunctions = []; for (var k in this.currentMessages) { if (this.currentMessages.hasOwnProperty(k)) { callBackFunctions.push(this.currentMessages[k].callback); } } // clear the current messages before calling back with the error. this.currentMessages = []; callBackFunctions.forEach(function (cb) { cb(error); }); }; Sybase.prototype._query = util.promisify(Sybase.prototype._query); Sybase.prototype.connect = util.promisify(Sybase.prototype.connect); module.exports = Sybase;