UNPKG

ibm_db

Version:

IBM DB2 and IBM Informix bindings for node

1,956 lines (1,751 loc) 86.8 kB
/* 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