@soinlabs/sybase
Version:
This library provides a Node.js bridge to connect to a Sybase database. It uses a Java bridge to facilitate the connection and query execution.
137 lines (107 loc) • 4.09 kB
JavaScript
const spawn = require('child_process').spawn;
const JSONStream = require('JSONStream');
const path = require('path');
function Sybase(host, port, dbname, username, password, logTiming, pathToJavaBridge, { encoding = 'utf8', extraLogs = false } = {}) {
this.connected = false;
this.host = host;
this.port = port;
this.dbname = dbname;
this.username = username;
this.password = password;
this.logTiming = (logTiming == true);
this.encoding = encoding;
this.extraLogs = extraLogs;
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.
this.jsonParser = JSONStream.parse();
}
Sybase.prototype.log = function(msg) {
if (this.extraLogs) {
console.log(msg);
}
};
Sybase.prototype.connect = function(callback) {
const that = this;
this.javaDB = spawn('java', ['-jar', this.pathToJavaBridge, this.host, this.port, this.dbname, this.username, this.password]);
const hrstart = process.hrtime();
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(that.encoding).pipe(that.jsonParser).on('data', function(jsonMsg) { that.onSQLResponse.call(that, jsonMsg); });
that.javaDB.stderr.on('data', function(err) { that.onSQLError.call(that, err); });
callback(null, data);
});
// handle connection issues.
this.javaDB.stderr.once('data', function(data) {
that.javaDB.stdout.removeAllListeners('data');
that.javaDB.kill();
callback(new Error(data));
});
};
Sybase.prototype.disconnect = function() {
this.javaDB.kill();
this.connected = false;
};
Sybase.prototype.isConnected = function() {
return this.connected;
};
Sybase.prototype.query = function(sql, callback) {
if (this.isConnected() === false) {
callback(new Error('database isn\'t connected.'));
return;
}
const hrstart = process.hrtime();
this.queryCount++;
const msg = {};
msg.msgId = this.queryCount;
msg.sql = sql;
msg.sentTime = (new Date()).getTime();
const strMsg = JSON.stringify(msg).replace(/[\n]/g, '\\n');
msg.callback = callback;
msg.hrstart = hrstart;
this.log('this: ' + this + ' currentMessages: ' + this.currentMessages + ' this.queryCount: ' + this.queryCount);
this.currentMessages[msg.msgId] = msg;
this.javaDB.stdin.write(strMsg + '\n');
this.log('sql request written: ' + strMsg);
};
Sybase.prototype.onSQLResponse = function(jsonMsg) {
let err = null;
const request = this.currentMessages[jsonMsg.msgId];
delete this.currentMessages[jsonMsg.msgId];
let 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
const currentTime = (new Date()).getTime();
const sendTimeMS = currentTime - jsonMsg.javaEndTime;
hrend = process.hrtime(request.hrstart);
const 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) {
const error = new Error(data);
const callBackFuncitons = [];
for (const k in this.currentMessages) {
if (this.currentMessages.hasOwnProperty(k)) {
callBackFuncitons.push(this.currentMessages[k].callback);
}
}
// clear the current messages before calling back with the error.
this.currentMessages = [];
callBackFuncitons.forEach(function(cb) {
cb(error);
});
};
module.exports = Sybase;