ibm_db
Version:
IBM DB2 and IBM Informix bindings for node
1,956 lines (1,751 loc) • 86.8 kB
JavaScript
/*
Copyright (c) 2013, Dan VerWeire <dverweire@gmail.com>
Copyright (c) 2010, Lee Smith <notwink@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Setting SQLLIB bin path to the path env before load for windows
var os = require('os'),
path = require('path');
var platform = os.platform();
// Set PATH and LIB env vars for clidriver.
var clidriver = path.resolve(__dirname, '../installer/clidriver');
if (process.env.IBM_DB_HOME) {
clidriver = process.env.IBM_DB_HOME;
}
if(platform == 'win32')
{
process.env.PATH = path.resolve(clidriver, 'bin') + ';' +
path.resolve(clidriver, 'bin/amd64.VC12.CRT') + ';' +
path.resolve(clidriver, 'bin/icc64') + ';' +
path.resolve(clidriver, 'lib') + ';' + process.env.PATH;
process.env.LIB = path.resolve(clidriver, 'bin') + ';' +
path.resolve(clidriver, 'bin/icc64') + ';' +
path.resolve(clidriver, 'lib') + ';' + process.env.LIB;
}
else if(platform == 'linux') {
process.env.PATH = path.resolve(clidriver, 'bin') + ':' +
path.resolve(clidriver, 'lib') + ':' + process.env.PATH;
process.env.LD_LIBRARY_PATH = path.resolve(clidriver, 'lib') + ':' +
path.resolve(clidriver, 'lib/icc') + ':' + process.env.LD_LIBRARY_PATH;
}
else if(platform == 'darwin') {
process.env.PATH = path.resolve(clidriver, 'bin') + ':' +
path.resolve(clidriver, 'lib') + ':' + process.env.PATH;
process.env.DYLD_LIBRARY_PATH = path.resolve(clidriver, 'lib') + ':' +
path.resolve(clidriver, 'lib/icc') + ':' + process.env.DYLD_LIBRARY_PATH;
}
else if(platform == 'aix') {
process.env.PATH = path.resolve(clidriver, 'bin') + ':' +
path.resolve(clidriver, 'lib') + ':' + process.env.PATH;
process.env.LIBPATH = path.resolve(clidriver, 'lib') + ':' +
path.resolve(clidriver, 'lib/icc') + ':' + process.env.LIBPATH;
}
var odbc = require("bindings")("odbc_bindings")
, SimpleQueue = require("./simple-queue")
, macro = require("./climacros")
, util = require("util")
, Readable = require('stream').Readable
, Q = require('q');
// Call of odbc.ODBC() loads odbc library and allocate environment handle.
// All calls of new Database() should use this same odbc unless passed as
// options.odbc. ENV will keep value of this odbc after first call of Database.
var ENV;
module.exports = function (options)
{
return new Database(options);
};
module.exports.Database = Database;
module.exports.ODBC = odbc.ODBC;
module.exports.ODBCConnection = odbc.ODBCConnection;
module.exports.ODBCStatement = odbc.ODBCStatement;
module.exports.ODBCResult = odbc.ODBCResult;
exports.debug = false;
var logParams = false;
var ibmdbStartTime = new Date();
var getElapsedTime = function(){
var tstamp = (new Date() - ibmdbStartTime)/1000.0;
//process.stdout.write(tstamp + " :: ");
return (tstamp + " :: ");
};
module.exports.getElapsedTime = getElapsedTime;
module.exports.debug = function(x) {
if(x) {
exports.debug = true;
console.log("node-ibm_db logs enabled.");
if(x == 2) {
logParams = true;
}
}
else {
exports.debug = false;
console.log("node-ibm_db logs disabled.");
}
};
var connError = {
message: "Connection not open.",
sqlstate: "08001",
sqlcode: -30081
};
module.exports.open = function (connStr, options, cb)
{
var db, deferred;
var DBFactory = function(options) {
return new Database(options);
};
if (!cb && typeof options !== 'function')
{
if(!options)
{
options = null;
}
db = DBFactory(options);
deferred = Q.defer();
db.open(connStr, function(err) {
if (err)
{
deferred.reject(err);
}
else
{
deferred.resolve(db);
}
});
return deferred.promise;
}
else if (typeof options === 'function')
{
cb = options;
options = null;
}
db = DBFactory(options);
db.open(connStr, function (err) {
cb && cb(err, db);
});
}; // ibmdb.open
module.exports.openSync = function (connStr, options)
{
var db = new Database(options);
db.openSync(connStr);
return db;
}; // ibmdb.openSync
/*
* Create Database.
* (createDbSync)
* args: dbName, codeSet, mode.
* (codeSet, mode are optional and default values will be null)
*
* return: TRUE on success or FALSE on failure.
*/
module.exports.createDbSync = function (dbName, connStr, options)
{
// createDbSync API is not supported on z/OS:
// Databases are implemented/used in Db2 for z/OS differently than
// and Db2 for LUW. A database in z/OS is simply a logical collection
// of table/index spaces that you create using the "CREATE DATABASE"
// SQL statement, while a database in LUW is conceptually equivalent
// to a subsystem in z/OS. Connecting to a Db2 on z/OS subsystem
// entails a "database" is created already. As such, this API is
// not applicable.
if (os.type() === "OS/390")
{
throw new Error("[node-ibm_db] createDbSync API is not supported on z/OS");
}
var result;
var db = new Database(options);
if (typeof(connStr) === "object")
{
var obj = connStr;
connStr = "";
Object.keys(obj).forEach(function (key) {
connStr += key + "=" + obj[key] + ";";
});
}
var conStr = connStr + ";" + "ATTACH=true";
var connectionOpen = db.openSync(conStr);
if (connectionOpen)
{
db.connected = true;
}
if (!db.connected)
{
throw ({ message : "Connection not open."});
}
else
{
if(dbName == null )
{
throw ({ message : "Database Name <db_Name> is required."});
}
else
{
// IF: argument "options" is not passed while calling.
// THEN: db.codeSet and db.mode will be "null" (default value).
result = db.conn.createDbSync(dbName, db.codeSet, db.mode);
return result;
}
}
}; // ibmdb.createDbSync
/*
* Drop Database.
* (dropDbSync)
* args: dbName
*
* return: TRUE on success or FALSE on failure.
*/
module.exports.dropDbSync = function (dbName, connStr, options)
{
// dropDbSync API is not supported on z/OS:
// Databases are implemented/used in Db2 for z/OS differently than
// and Db2 for LUW. A database in z/OS is simply a logical collection
// of table/index spaces that you create using the "CREATE DATABASE"
// SQL statement, while a database in LUW is conceptually equivalent
// to a subsystem in z/OS. Connecting to a Db2 on z/OS subsystem
// entails a "database" is created already. As such, this API is
// not applicable for Db2 on z/OS servers.
if (os.type() === "OS/390")
{
throw new Error("[node-ibm_db] dropDbSync API is not supported on z/OS");
}
var result;
var db = new Database(options);
if (typeof(connStr) === "object")
{
var obj = connStr;
connStr = "";
Object.keys(obj).forEach(function (key) {
connStr += key + "=" + obj[key] + ";";
});
}
var conStr = connStr + ";" + "ATTACH=true";
var connectionOpen = db.openSync(conStr);
if (connectionOpen)
{
db.connected = true;
}
if (!db.connected)
{
throw ({ message : "Connection not open."});
}
else
{
if(dbName == null )
{
throw ({ message : "Database Name <db_Name> is required."});
}
else
{
result = db.conn.dropDbSync(dbName);
return result;
}
}
}; // ibmdb.dropDbSync
module.exports.close = function(db)
{
if(db && typeof(db) === "object")
{
for(var key in db)
{
delete db[key];
}
db = undefined;
}
}; // ibmdb.close
function Database(options)
{
var self = this;
options = options || {};
self.odbc = (options.odbc) ? options.odbc : ((ENV) ? ENV : new odbc.ODBC());
if(!ENV) ENV = self.odbc;
self.queue = new SimpleQueue();
self.fetchMode = options.fetchMode || null;
self.connected = false;
self.connectTimeout = options.connectTimeout || null;
self.systemNaming = options.systemNaming;
self.codeSet = options.codeSet || null;
self.mode = options.mode || null;
self.pool = options.pool || null;
if( self.pool ) { // Its a pooled connection
if( !self.realClose ) {
self.realClose = self.close;
self.close = self.poolClose;
self.realCloseSync = self.closeSync;
self.closeSync = self.poolCloseSync;
}
self.connStr = options.connStr || null;
}
} // Database()
//Expose constants
Object.keys(odbc.ODBC).forEach(function (key) {
if (typeof odbc.ODBC[key] !== "function")
{
//On the database prototype
Database.prototype[key] = odbc.ODBC[key];
//On the exports
module.exports[key] = odbc.ODBC[key];
}
});
Database.prototype.open = function (connStr, cb) {
var self = this, deferred;
if (typeof(connStr) === "object")
{
var obj = connStr;
connStr = "";
Object.keys(obj).forEach(function (key) {
connStr += key + "=" + obj[key] + ";";
});
}
if (!cb)
{
deferred = Q.defer();
}
self.odbc.createConnection(function (err, conn) {
if(!cb)
{
if (err) deferred.reject(err);
} else
{
if (err) return cb(err);
}
self.conn = conn;
if (self.connectTimeout || self.connectTimeout === 0)
{
self.conn.connectTimeout = self.connectTimeout;
}
if (typeof(self.systemNaming) !== 'undefined')
{
self.conn.systemNaming = self.systemNaming;
}
self.conn.open(connStr, function (err, result)
{
if(cb)
{
if (err) cb(err);
else {
self.connected = true;
cb(err, result);
}
}
else
{
if(err) deferred.reject(err);
self.connected = true;
deferred.resolve(result);
}
}); //conn.open
}); // odbc.createConnection
return deferred ? deferred.promise : null;
}; // Database.open function
Database.prototype.openSync = function (connStr)
{
var self = this;
self.conn = self.odbc.createConnectionSync();
if (self.connectTimeout || self.connectTimeout === 0)
{
self.conn.connectTimeout = self.connectTimeout;
}
if (typeof(self.systemNaming) !== 'undefined')
{
self.conn.systemNaming = self.systemNaming;
}
if (typeof(connStr) === "object")
{
var obj = connStr;
connStr = "";
Object.keys(obj).forEach(function (key) {
connStr += key + "=" + obj[key] + ";";
});
}
var result = self.conn.openSync(connStr);
if (result)
{
self.connected = true;
}
return result;
}; // Database.openSync
Database.prototype.close = function (cb)
{
var self = this, deferred;
if(!cb)
{
deferred = Q.defer();
}
self.queue.push(function (next) {
if(self.conn)
{
self.conn.close(function (err) {
self.connected = false;
delete self.conn;
if (err) {
deferred ? deferred.reject(err) : cb(err);
} else {
deferred ? deferred.resolve(true) : cb(null);
}
return next();
});
}
else
{
self.connected = false;
deferred ? deferred.resolve(true) : cb(null);
return next();
}
}); // self.queue.push
return deferred ? deferred.promise : false;
}; // Database.close
Database.prototype.closeSync = function ()
{
var self = this;
var result;
if(self.conn) result = self.conn.closeSync();
self.connected = false;
delete self.conn;
return result;
}; // closeSync
Database.prototype.checkConnectionError = function (err)
{
// For pooled connection, if we get SQL30081N, then close
// the connection now only and then proceed.
var self = this;
if(self.realClose){
if((err && err['message'] && err['message'].search("SQL30081N") != -1))
{
// Close all connections in availablePool as all are invalid now
// It will be removed from available pool on next pool.open for connected=false
self.closeSync(); //Move connection from usedPool to availablePool
var availablePool = self.pool.availablePool[self.connStr];
if (availablePool && availablePool.length) {
for (var i = 0, len = availablePool.length; i < len; i++) {
availablePool[i].realCloseSync();
}
}
self.realCloseSync();
}
}
}
Database.prototype.query = function (query, params, cb)
{
var self = this, deferred, sql, resultset = [], multipleResultSet = false;
var sqlca = {sqlstate:"0", sqlcode: 0};
exports.debug && console.log(getElapsedTime(), "odbc.js:query() => Entry");
//support for promises
if (!cb && typeof params !== 'function')
{
deferred = Q.defer();
!params ? params = null : '';
}
if (typeof(params) === 'function')
{
cb = params;
params = null;
}
self.queue.push(function (next) {
function cbQuery (initialErr, result, outparams)
{
if(outparams) {
resultset = outparams;
multipleResultSet = true;
}
if (result && typeof(result) === 'object') {
fetchMore();
} else {
if (initialErr) {
if (initialErr.sqlstate) {
sqlca.sqlstate = initialErr.sqlstate;
}
if (initialErr.sqlcode) {
sqlca.sqlcode = initialErr.sqlcode;
}
self.checkConnectionError(initialErr);
if(deferred) {
let error = {};
if(typeof initialErr === 'object') {
error = initialErr;
} else {
error["message"] = initialErr;
}
error["resultset"] = resultset;
error.sqlcode = sqlca.sqlcode;
error.sqlstate = sqlca.sqlstate;
deferred.reject(error);
} else {
cb(initialErr, resultset, sqlca);
}
}
if (result) { result.closeSync(); }
initialErr = null;
return next();
}
function fetchMore()
{
if (self.fetchMode)
{
result.fetchMode = self.fetchMode;
}
result._fetchAll(function (err, data, colcount) {
var moreResults = false, moreResultsError = null;
// If there is any error, return it now only.
if( err || initialErr )
{
self.checkConnectionError(err || initialErr);
if(multipleResultSet) resultset.push(data);
else resultset = data;
err = initialErr || err;
sqlca.sqlstate = err.sqlstate || sqlca.sqlstate;
sqlca.sqlcode = err.sqlcode || sqlca.sqlcode;
console.log(err, sqlca);
if(deferred) {
err["resultset"] = resultset;
err.sqlcode = sqlca.sqlcode;
err.sqlstate = sqlca.sqlstate;
deferred.reject(err);
} else {
cb(err, resultset, sqlca);
}
if (result) { result.closeSync(); }
initialErr = null;
err = null;
return next();
}
// Get the result data
try
{
moreResults = result.moreResultsSync();
}
catch (e)
{
moreResultsError = e;
moreResults = false;
sqlca.sqlstate = e.sqlstate || sqlca.sqlstate;
sqlca.sqlcode = e.sqlcode || sqlca.sqlcode;
}
//close the result before calling back
//if there are not more result sets
if (moreResults)
{
resultset.push(data);
multipleResultSet = true;
fetchMore();
}
else
{
result.closeSync();
if( colcount ) {
if(multipleResultSet) resultset.push(data);
else resultset = data;
}
exports.debug && console.log(getElapsedTime(), "odbc.js:query() => Done.");
// send exception error and/or data to callback function.
// only once with all the results.
if( !sqlca.sqlcode && colcount && !resultset.length ) {
sqlca.sqlcode = 100;
}
if(deferred) {
if(moreResultsError) {
moreResultsError["resultset"] = resultset;
moreResultsError.sqlcode = sqlca.sqlcode;
moreResultsError.sqlstate = sqlca.sqlstate;
deferred.reject(moreResultsError);
} else {
deferred.resolve(resultset);
}
} else {
cb(moreResultsError, resultset, sqlca);
}
}
moreResultsError = null;
return next();
});
} // function fetchMore
} //function cbQuery
if (!self.connected)
{
sqlca = {sqlstate: "08001", sqlcode : -30081};
deferred ? deferred.reject(connError) : cb(connError, [], sqlca);
return next();
}
if(typeof query === "object")
{
sql = query.sql;
if(query.params) params = query.params;
}
else
{
sql = query;
}
exports.debug && console.log(getElapsedTime(), "odbc.js:query() => ", sql);
try {
if (params && params.length > 0)
{
if(Array.isArray(params))
{
var err = parseParams(params);
if(err) deferred ? deferred.reject(err) : cb(err);
}
if(exports.debug && logParams) {
console.log("%s odbc.js:query() params = ", getElapsedTime(), params);
}
if(typeof query === 'object')
{
query.params = params;
self.conn.query(query, cbQuery);
}
else
self.conn.query(query, params, cbQuery);
}
else
{
self.conn.query(query, cbQuery);
}
} catch (err)
{
self.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err);
return next();
}
}); //self.queue.push
return deferred ? deferred.promise : false;
}; // Database.query
Database.prototype.queryResult = function (query, params, cb)
{
var self = this, deferred, sql;
//support for promises
if (!cb && typeof params !== 'function')
{
deferred = Q.defer();
!params ? params = null : '';
}
if (typeof(params) === 'function')
{
cb = params;
params = null;
}
if(typeof query === "object")
{
sql = query.sql;
if(query.params) params = query.params;
}
else
{
sql = query;
}
exports.debug && console.log(getElapsedTime(), "odbc.js:queryResult() => ", sql);
self.queue.push(function (next) {
//ODBCConnection.query() is the fastest-path querying mechanism.
if (!self.connected)
{
deferred ? deferred.reject(connError) : cb(connError);
return next();
}
if (params && params.length > 0)
{
if(Array.isArray(params))
{
var err = parseParams(params);
if(err) deferred ? deferred.reject(err) : ( cb && cb(err) );
}
if(typeof query === 'object')
{
query.params = params;
self.conn.query(query, cbQuery);
}
else
self.conn.query(sql, params, cbQuery);
}
else
{
self.conn.query(sql, cbQuery);
}
function cbQuery (err, result, outparams)
{
if (err)
{
self.checkConnectionError(err);
deferred ? deferred.reject(err) : ( cb && cb(err, null, outparams) );
return next();
}
if (result && typeof(result) === 'object') {
if (self.fetchMode)
{
result.fetchMode = self.fetchMode;
}
}
else if( result ) // noResults = true is passed by application.
{
outparams = result;
result = null;
}
!cb ? deferred.resolve([result, outparams]) : cb(err, result, outparams);
return next();
} // function cbQuery
}); //self.queue.push
return deferred ? deferred.promise : false;
}; // Database.queryResult
Database.prototype.queryResultSync = function (query, params)
{
var self = this, sql, outparams;
var result; // Must get closed by caller for success case.
if (!self.connected)
{
throw ({ message : "Connection not open."});
}
if(typeof query === "object")
{
sql = query.sql;
if(query.params) params = query.params;
}
else
{
sql = query;
}
exports.debug && console.log(getElapsedTime(), "odbc.js:queryResultSync() => ", sql);
try {
if (params)
{
if(Array.isArray(params))
{
var err = parseParams(params);
if(err) return err;
}
if(typeof query === 'object')
{
query.params = params;
result = self.conn.querySync(query);
}
else
{
result = self.conn.querySync(sql, params);
}
}
else
{
result = self.conn.querySync(sql); // Always returns [stmt, outparms]
}
} catch (e)
{
self.checkConnectionError(e);
return e;
}
if(Array.isArray(result)) // Its a CALL stmt with OUT params.
{
if(result[1]) {
outparams = result[1]; // INOUT and OUTPUT param values for SP.
}
result = result[0];
}
if (result && typeof(result) === 'object' && self.fetchMode)
{
result.fetchMode = self.fetchMode;
}
if( outparams ) return [result, outparams];
else return result;
}; // Database.queryResultSync
Database.prototype.querySync = function (query, params)
{
var self = this, result, sql, outparams = null;
if (!self.connected)
{
throw ({ message : "Connection not open."});
}
if(typeof query === "object")
{
sql = query.sql;
if(query.params) params = query.params;
}
else
{
sql = query;
}
exports.debug && console.log(getElapsedTime(), "odbc.js:querySync() => ", sql);
try {
if (params)
{
if(Array.isArray(params))
{
var err = parseParams(params);
if(err) return err;
if(logParams) {
console.log("%s odbc.js:querySync() params = ", getElapsedTime(), params);
}
}
if(typeof query === 'object')
{
query.params = params;
result = self.conn.querySync(query);
}
else
{
result = self.conn.querySync(sql, params);
}
}
else
{
result = self.conn.querySync(query);
}
} catch (e)
{
self.checkConnectionError(e);
return e;
}
if(Array.isArray(result))
{
if(result[1]) {
outparams = result[1]; // INOUT and OUT param values for SP.
}
result = result[0];
}
if(!result) return outparams; // For noResults.
// Processing for resultset.
var data, resultset = [], moreResults = true, moreResultsError = null;
var nullresult = true;
if(outparams) {
resultset = outparams;
nullresult = false;
}
if (self.fetchMode)
{
result.fetchMode = self.fetchMode;
}
while(moreResults)
{
data = result.fetchAllSync();
if(!data.length) {
moreResults = false;
break;
}
try
{
moreResults = result.moreResultsSync();
}
catch (e)
{
moreResultsError = e;
moreResults = false;
break;
}
if(data.length) {
if(nullresult && !moreResults) resultset = data;
else resultset.push(data);
nullresult = false;
}
}
result.closeSync();
if(moreResultsError) {
self.checkConnectionError(moreResultsError);
return moreResultsError;
}
if(nullresult) return [];
return resultset;
}; // Database.querySync
Database.prototype.executeFile = function (inputfile, delimiter, outputfile, cb) {
var fs = require('fs-extra');
var self = this, deferred, sql;
if (typeof (delimiter) === 'function') {
cb = delimiter;
delimiter = ';'
}
if (typeof (outputfile) === 'function') {
cb = outputfile;
outputfile = undefined;
}
//support for promises
if (!cb) {
deferred = Q.defer();
}
if (typeof inputfile !== "string") {
sql = inputfile.sql;
if (inputfile.delimiter) delimiter = inputfile.delimiter;
if (inputfile.outputfile) outputfile = inputfile.outputfile;
}
else {
sql = inputfile;
}
if (!self.connected) {
deferred ? deferred.reject(connError) : cb(connError);
}
fs.readFile(sql, function (err, sql) {
if (err) {
deferred ? deferred.reject(err) : cb(err);
}
else {
var query = sql.toString();
var myarray = query.split(delimiter);
var res = "";
if (outputfile !== undefined) {
if (fs.existsSync(outputfile)) {
fs.unlinkSync(outputfile)
}
else {
fs.ensureFileSync(outputfile)
}
}
for (var i = 0; i < myarray.length; i++) {
query = (myarray[i]).trim();
var result = [];
if(query) {
result = self.querySync(query);
if (!Array.isArray(result)) {
if (outputfile === undefined) {
deferred ? deferred.reject(result) : cb(result);
}
else {
fs.appendFileSync(outputfile, result);
}
}
if (result.length > 0) {
result = JSON.stringify(result);
if (outputfile === undefined) {
res += result + delimiter;
}
else {
fs.appendFileSync(outputfile, result + delimiter);
}
}
}
}
deferred ? deferred.resolve(res) : cb(err, res);
}
});
};
Database.prototype.executeFileSync = function (inputfile, delimiter, outputfile)
{
var fs = require('fs-extra');
var self = this, sql = null;
if (!self.connected)
{
throw ({ message: "Connection not open." });
}
if (typeof inputfile !== "string")
{
sql = inputfile.sql;
if (inputfile.delimiter) delimiter = inputfile.delimiter;
if (inputfile.outputfile) outputfile = inputfile.outputfile;
}
else
{
sql = inputfile;
}
if (!fs.existsSync(sql))
{
return Error("[node-ibm_db] Input file " + sql + " does not exists");
}
if (outputfile !== undefined)
{
if (fs.existsSync(outputfile))
{
fs.unlinkSync(outputfile)
}
else
{
fs.ensureFileSync(outputfile)
}
}
if (delimiter === undefined)
{
delimiter = ';'
}
var query = fs.readFileSync(sql, 'utf8')
var myarray = query.split(delimiter);
var res = "";
for (var i = 0; i < myarray.length; i++)
{
var result = self.querySync(myarray[i]);
if (!Array.isArray(result))
{
return result;
}
if (result.length > 0)
{
result = JSON.stringify(result)
if (outputfile === undefined)
{
res += result + delimiter
}
else
{
fs.appendFileSync(outputfile, result + delimiter)
}
}
}
return res;
};
Database.prototype.queryStream = function queryStream(sql, params)
{
var self = this;
var stream = new Readable({ objectMode: true });
var results;
stream._read = function()
{
// after the first internal call to _read, the 'results' should be set
// and the stream can continue fetching the results
if (results) return self.fetchStreamingResults(results, stream);
// in the first call to _read the stream starts to emit data once we've
// queried for results
return self.queryResult(sql, params, function (err, result)
{
if (err)
{
return process.nextTick(function () { stream.emit('error', err); });
}
results = result;
return self.fetchStreamingResults(results, stream);
});
};
return stream;
};
Database.prototype.fetchStreamingResults = function(results, stream)
{
var self = this;
return results.fetch(function (err, data)
{
if (err)
{
return process.nextTick(function () { stream.emit('error', err); });
}
// when no more data returns, return push null to indicate the end of stream
if (!data)
{
results.closeSync(); // Close stmt handle.
stream.push(null);
}
else {
stream.push(data);
}
});
};
Database.prototype.beginTransaction = function (cb)
{
var self = this
, deferred = null
, onBeginTransaction;
if(!cb)
{
deferred = Q.defer();
}
onBeginTransaction = function(err)
{
if(err)
{
self.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err);
}
else
{
deferred ? deferred.resolve(true) : cb(null);
}
};
self.conn.beginTransaction(onBeginTransaction);
self.conn.inTransaction = true;
return deferred ? deferred.promise : self;
};
Database.prototype.endTransaction = function (rollback, cb)
{
var self = this, deferred = null;
if(!cb)
{
deferred = Q.defer();
}
var onEndTransaction = function(err)
{
if(err)
{
self.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err);
}
else
{
deferred ? deferred.resolve(true) : cb(null);
}
};
self.conn.endTransaction(rollback, onEndTransaction);
self.conn.inTransaction = false;
return deferred ? deferred.promise : self;
};
Database.prototype.commitTransaction = function (cb)
{
var self = this;
//don't rollback
return self.endTransaction(false, cb);
};
Database.prototype.rollbackTransaction = function (cb)
{
var self = this;
return self.endTransaction(true, cb); //rollback
};
Database.prototype.beginTransactionSync = function ()
{
var self = this;
try {
self.conn.beginTransactionSync();
} catch (e) {
self.checkConnectionError(e);
throw new Error(e);
}
self.conn.inTransaction = true;
return self;
};
Database.prototype.endTransactionSync = function (rollback)
{
var self = this;
self.conn.inTransaction = false;
try {
self.conn.endTransactionSync(rollback);
} catch (e) {
self.checkConnectionError(e);
throw new Error(e);
}
return self;
};
Database.prototype.commitTransactionSync = function ()
{
var self = this;
return self.endTransactionSync(false); //don't rollback
};
Database.prototype.rollbackTransactionSync = function ()
{
var self = this;
return self.endTransactionSync(true); //rollback
};
Database.prototype.columns = async function(catalog, schema, table, column, callback)
{
var self = this, deferred;
var error = {
error : "[node-ibm_db] Missing Arguments",
message : "The object you passed must contain four arguments: " +
"catalog, schema, table and column. " +
"This is required for the columns method to work."
};
if (!self.queue) self.queue = [];
callback = callback || arguments[arguments.length - 1];
if(typeof(callback) !== 'function') {
callback = null;
deferred = Q.defer();
if( arguments.length != 4 ) {
deferred.reject(error);
throw new Error(error.message);
}
}
else if( arguments.length != 5 ) {
callback(error, [], false);
}
self.queue.push(function (next)
{
self.conn.columns(catalog, schema, table, column, function (err, result)
{
if (err) {
self.checkConnectionError(err);
return deferred ? deferred.reject(err) : callback(err, [], false);
}
result._fetchAll(function (err, data)
{
result.closeSync();
self.checkConnectionError(err);
deferred ? deferred.resolve(data) : callback(err, data);
return next();
});
});
});
if(deferred) return deferred.promise;
};
Database.prototype.tables = function(catalog, schema, table, type, callback)
{
var self = this, deferred;
var error = {
error : "[node-ibm_db] Missing Arguments",
message : "The object you passed must contain four arguments: " +
"catalog, schema, table and type. " +
"This is required for the tables method to work."
};
if (!self.queue) self.queue = [];
callback = callback || arguments[arguments.length - 1];
if(typeof(callback) !== 'function') {
callback = null;
deferred = Q.defer();
if( arguments.length != 4 ) {
deferred.reject(error);
throw new Error(error.message);
}
}
else if( arguments.length != 5 ) {
callback(error, [], false);
}
self.queue.push(function (next)
{
self.conn.tables(catalog, schema, table, type, function (err, result)
{
if (err) {
self.checkConnectionError(err);
return deferred ? deferred.reject(err) : callback(err, [], false);
}
result._fetchAll(function (err, data)
{
result.closeSync();
self.checkConnectionError(err);
deferred ? deferred.resolve(data) : callback(err, data);
return next();
});
});
});
if(deferred) return deferred.promise;
};
Database.prototype.describe = async function(obj, callback)
{
var self = this, deferred;
var error = {
error : "[node-ibm_db] Missing Arguments",
message : "You must pass an object as argument 0 if you want " +
"anything productive to happen in the describe method."
};
if (typeof(callback) !== "function") {
callback = null;
}
if(!callback) {
deferred = Q.defer();
}
if (typeof(obj) !== "object") {
deferred ? deferred.reject(error) : callback && callback(error, []);
}
if (!obj.database) {
error = {
error : "[node-ibmdb] Missing Arguments",
message : "The object you passed did not contain a database " +
"property. This is required for the describe method to work."
};
deferred ? deferred.reject(error) : callback && callback(error, []);
}
//set some defaults if they weren't passed
obj.schema = obj.schema || "%";
obj.type = obj.type || "TABLE";
if (obj.table && obj.column)
{
//get the column details
if(callback)
self.columns(obj.database, obj.schema, obj.table, obj.column, callback);
else
return await self.columns(obj.database, obj.schema, obj.table, obj.column);
}
else if (obj.table)
{
//get the columns in the table
if(callback)
self.columns(obj.database, obj.schema, obj.table, "%", callback);
else
return await self.columns(obj.database, obj.schema, obj.table, "%")
.catch((e)=> { deferred.reject(e); });
}
else
{
//get the tables in the database
if(callback)
self.tables(obj.database, obj.schema, null, obj.type, callback);
else
return await self.tables(obj.database, obj.schema, null, obj.type);
}
}; //Database.describe
Database.prototype.prepare = function (sql, cb)
{
var self = this, deferred;
if(!cb)
{
deferred = Q.defer();
}
self.conn.createStatement(function (err, stmt)
{
if(err)
{
self.checkConnectionError(err);
if(cb)
{
return cb(err);
} else
{
deferred.reject(err);
}
}
stmt.queue = new SimpleQueue();
stmt.db = self; // We need a reference to db to check for connection errors
stmt.prepare(sql, function (err)
{
if (err)
{
self.checkConnectionError(err);
if(cb)
{
return cb(err);
} else
{
deferred.reject(err)
}
}
deferred ? deferred.resolve(stmt) : cb(null, stmt);
});
});
return deferred ? deferred.promise : null;
};
Database.prototype.prepareSync = function (sql)
{
var self = this;
var stmt = {};
try {
stmt = self.conn.createStatementSync();
} catch (e) {
self.checkConnectionError(e);
throw new Error(e);
}
stmt.queue = new SimpleQueue();
stmt.db = self; // We need a reference to db to check for connection errors
try {
stmt.prepareSync(sql);
} catch (e) {
self.checkConnectionError(e);
throw new Error(e);
}
return stmt;
};
Database.prototype.setIsolationLevel = function(isolationLevel)
{
var self = this;
try {
self.conn.setIsolationLevel(isolationLevel);
} catch (e) {
self.checkConnectionError(e);
throw new Error(e);
}
return true;
};
const MAXINFO_LEN = 256; // odbc.C CLI_MAXINFO_LEN
Database.prototype.getInfoSync = function(infoType, infoLen)
{
// infoType - mandatory
// infoLen - optional
var self = this;
if (!self.connected)
{
throw ({ message : "Connection not open."});
}
if (Number.isInteger(infoType) === false) {
return { message : "Invalid infoType requested."};
}
if (Number.isInteger(infoLen) === false) {
infoLen = MAXINFO_LEN;
}
try {
return self.conn.getInfoSync(infoType, infoLen);
} catch (e) {
self.checkConnectionError(e);
throw new Error(e);
}
} //getInfoSync
Database.prototype.getInfo = function (infoType, infoLen, cb)
{
// infoType - mandatory
// infoLen - optional
// cb - optional. If not cb, then return promise
var self = this, deferred;
//support for promises
if (!cb && typeof infoLen !== 'function')
{
deferred = Q.defer();
}
// For (infoType, cb)
if (typeof(infoLen) === 'function')
{
cb = infoLen;
infoLen = MAXINFO_LEN;
}
if (Number.isInteger(infoLen) === false) {
infoLen = MAXINFO_LEN;
}
self.queue.push(function (next)
{
exports.debug && console.log(getElapsedTime(), "odbc.js: infoType => ", infoType);
if (!self.connected) {
deferred ? deferred.reject(connError) : cb(connError);
return next();
}
if (Number.isInteger(infoType) === false) {
var err = { message : "Invalid infoType used, " + infoType};
deferred ? deferred.reject(err) : cb(err);
return next();
}
self.conn.getInfo(infoType, infoLen, function (err, value)
{
if (err)
{
self.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err);
return next();
}
deferred ? deferred.resolve(value) : cb(null, value);
return next();
});
});
if(deferred) return deferred.promise;
} //getInfo
Database.prototype.getTypeInfoSync = function(dataType)
{
// infoType - mandatory
var self = this;
var stmt = 0;
var data = [];
if (!self.connected)
{
throw ({ message : "Connection not open."});
}
if (Number.isInteger(dataType) === false) {
throw { message : "Invalid dataType used, " + dataType};
}
try {
stmt = self.conn.getTypeInfoSync(dataType);
data = stmt.fetchAllSync();
stmt.closeSync();
return data;
} catch (e) {
self.checkConnectionError(e);
throw new Error(e);
}
} //getTypeInfoSync
Database.prototype.getTypeInfo = function (dataType, cb)
{
// dataType - mandatory
// cb - optional. If not cb, then return promise
var self = this, deferred;
if (typeof dataType === 'function')
{
cb = dataType;
}
//support for promises
if (!cb && typeof dataType !== 'function')
{
deferred = Q.defer();
}
self.queue.push(function (next)
{
exports.debug && console.log(getElapsedTime(), "odbc.js: dataType => ", dataType);
if (!self.connected) {
deferred ? deferred.reject(connError) : cb(connError);
return next();
}
if (Number.isInteger(dataType) === false) {
var err = { message : "Invalid dataType." };
if (typeof dataType === 'function') {
err = { message : "Missing dataType." };
}
deferred ? deferred.reject(err) : cb(err);
return next();
}
self.conn.getTypeInfo(dataType, function (err, result)
{
if (err) {
self.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err, false);
return next();
}
result.fetchAll(function (err, data)
{
result.closeSync();
if(err) {
self.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err, data);
} else {
deferred ? deferred.resolve(data) : cb(err, data);
}
return next();
});
});
});
if(deferred) return deferred.promise;
} //getTypeInfo
Database.prototype.getFunctionsSync = function(functionId)
{
// functionId - mandatory
var self = this;
var fExists = 0;
if (!self.connected)
{
throw ({ message : "Connection not open."});
}
if (Number.isInteger(functionId) === false) {
return { message : "Invalid functionId."};
}
try {
fExists = self.conn.getFunctionsSync(functionId);
} catch (e) {
self.checkConnectionError(e);
throw new Error(e);
}
if(fExists)
{
if(functionId) return true;
else
{
var functionMap = {};
var i = 0;
for ( i = 1; i < 100; i++ )
{
if(fExists[i])
{
functionMap[macro.functionids[i]] = true;
}
else
{
functionMap[i] = false;
}
}
return functionMap;
}
}
else return false;
} //getFunctionsSync
Database.prototype.getFunctions = function (functionId, cb)
{
// functionId - mandatory
// cb - optional. If not cb, then return promise
var self = this, deferred;
if (typeof(functionId) === 'function')
{
cb = functionId;
functionId = null;
}
//support for promises
if (!cb)
{
deferred = Q.defer();
}
self.queue.push(function (next)
{
exports.debug && console.log(getElapsedTime(), "odbc.js: functionId => ", functionId);
if (!self.connected) {
deferred ? deferred.reject(connError) : cb(connError);
return next();
}
if (Number.isInteger(functionId) === false) {
var err = { message : "Invalid functionId = " + functionId};
deferred ? deferred.reject(err) : cb(err);
return next();
}
self.conn.getFunctions(functionId, function (err, value)
{
if (err)
{
self.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err);
return next();
}
var result = false;
if(value)
{
if(functionId) result = true;
else
{
var functionMap = {};
var i = 0;
for ( i = 1; i < 100; i++ )
{
if(value[i])
{
functionMap[macro.functionids[i]] = true;
}
else
{
functionMap[i] = false;
}
}
result = functionMap;
}
}
deferred ? deferred.resolve(result) : cb(null, result);
return next();
});
});
if(deferred) return deferred.promise;
} //getFunctions
// Function to set connection level attributes
Database.prototype.setAttr = function (attr, value, cb)
{
// attr, value - mandatory
// cb - optional. If not cb, then return promise
var self = this, deferred;
//support for promises
if (!cb)
{
deferred = Q.defer();
}
self.queue.push(function (next)
{
exports.debug && console.log(getElapsedTime(),
"odbc.js:setAttr() Attribute = ", attr, ", Value = ", value);
if (!self.connected) {
deferred ? deferred.reject(connError) : cb(connError);
return next();
}
if (Number.isInteger(attr) === false) {
var connattr = macro.connAttributes[attr];
if (Number.isInteger(connattr) === false) {
var err = { message : "Invalid attr = " + attr};
deferred ? deferred.reject(err) : cb(err);
return next();
} else {
attr = connattr;
}
}
self.conn.setAttr(attr, value, function (err)
{
if (err)
{
self.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err);
return next();
}
deferred ? deferred.resolve(true) : cb(null, true);
return next();
});
});
if(deferred) return deferred.promise;
} //setAttr
// Async Function to set connection level attributes
Database.prototype.setAttrSync = function (attr, value)
{
// attr, value - mandatory
var self = this;
exports.debug && console.log(getElapsedTime(),
"odbc.js:setAttrSync() Attribute = ", attr, ", Value = ", value);
if (!self.connected) {
return (connError);
}
if (Number.isInteger(attr) === false) {
return { message : "Invalid Attribute."};
}
try {
return self.conn.setAttrSync(attr, value);
} catch (e) {
self.checkConnectionError(e);
return new Error(e);
}
}; // setAttrSync
//Proxy all of the ODBCStatement functions so that they are queued
if( !odbc.ODBCStatement.prototype._execute ) { //issue #514
odbc.ODBCStatement.prototype._execute = odbc.ODBCStatement.prototype.execute;
odbc.ODBCStatement.prototype._executeSync = odbc.ODBCStatement.prototype.executeSync;
odbc.ODBCStatement.prototype._executeDirect = odbc.ODBCStatement.prototype.executeDirect;
odbc.ODBCStatement.prototype._executeDirectSync = odbc.ODBCStatement.prototype.executeDirectSync;
odbc.ODBCStatement.prototype._executeNonQuery = odbc.ODBCStatement.prototype.executeNonQuery;
odbc.ODBCStatement.prototype._executeNonQuerySync = odbc.ODBCStatement.prototype.executeNonQuerySync;
odbc.ODBCStatement.prototype._prepare = odbc.ODBCStatement.prototype.prepare;
odbc.ODBCStatement.prototype._bind = odbc.ODBCStatement.prototype.bind;
odbc.ODBCStatement.prototype._bindSync = odbc.ODBCStatement.prototype.bindSync;
odbc.ODBCStatement.prototype._setAttr = odbc.ODBCStatement.prototype.setAttr;
odbc.ODBCStatement.prototype._setAttrSync = odbc.ODBCStatement.prototype.setAttrSync;
odbc.ODBCStatement.prototype._close = odbc.ODBCStatement.prototype.close;
//Proxy all of the ODBCResult functions so that they are queued
odbc.ODBCResult.prototype._fetch = odbc.ODBCResult.prototype.fetch;
odbc.ODBCResult.prototype._fetchAll = odbc.ODBCResult.prototype.fetchAll;
odbc.ODBCResult.prototype._getData = odbc.ODBCResult.prototype.getData;
odbc.ODBCResult.prototype._close = odbc.ODBCResult.prototype.close;
}
odbc.ODBCStatement.prototype.execute = function (params, cb)
{
var self = this, deferred;
self.queue = self.queue || new SimpleQueue();
if (!cb) {
if (typeof params === 'function') {
cb = params;
params = null;
}
else {
deferred = Q.defer();
}
}
self.queue.push(function (next) {
//If params were passed to this function, then bind them and
//then execute.
if (params)
{
if(Array.isArray(params))
{
var err = parseParams(params);
if(err)
{
if(deferred)
{
deferred.reject(err);
} else
{
cb(err);
}
}
}
if(exports.debug) {
if(logParams) {
console.log("%s odbc.js:execute() bind params = ", getElapsedTime(), params);
} else {
console.log("%s odbc.js:execute()", getElapsedTime());
}
}
self._bind(params, function (err) {
if (err) {
self.db.checkConnectionError(err);
deferred ? deferred.reject(err) : cb(err);
return next();
}
self._execute(function (err, result, outparams) {
if (err) {
self.db.checkConnectionError(err);
}
if(deferred) {
if(err) {
deferred.reject(err);
} else {
if(outparams) {
deferred.resolve([result, outparams]);
} else {
deferred.resolve(result);
}
}
} else {
cb(err, result, outparams);
}
return next();
});
});
}
//Otherwise execute and pop the next bind call
else
{
self._execute(function (err, result, outparams) {
if (err) {
self.db.checkConnectionError(err);
}
if(deferred) {
if(err) {
deferred.reject(err);
} else {
if(outparams) {
deferred.resolve([result, outparams]);
} else {
deferred.resolve(result);
}
}
} else {
cb(err, result, outparams);
}
//NOTE: We only execute the next queued bind call after
// we have called execute() or executeNonQuery(). This ensures
// that we don't call a bind() a bunch of times without ever
// actually executing that bind. Not
self.bindQueue && self.bindQueue.next();
return next();
});
}
});
return deferred ? deferred.promise : null;
};
odbc.ODBC