@softrams/nodejs-oracle-connector
Version:
Database connector wrapper to work with Oracle database from nodejs applications
307 lines (277 loc) • 8.98 kB
JavaScript
/* istanbul ignore file */
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-else-return */
const oracledb = require("oracledb");
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
const POOL_MAX_DEFAULT = 4;
const POOL_MIN_DEFAULT = 0;
const POOL_INCREMENT_DEFAULT = 1;
let promises = {};
let pools = {};
let config = {};
/**
* OracleDB Parameter types
*/
exports.OracleDBTypes = {
STRING: oracledb.STRING,
NUMBER: oracledb.NUMBER,
DATE: oracledb.DATE
};
exports.createPool = async (poolName) => {
try {
const srcCfg = config.DATASOURCES[poolName];
if (srcCfg) {
let poolMax = POOL_MAX_DEFAULT;
let poolMin = POOL_MIN_DEFAULT;
let poolIncrement = POOL_INCREMENT_DEFAULT;
if (process.env.UV_THREADPOOL_SIZE && srcCfg.MAX_POOL && !isNaN(srcCfg.MAX_POOL)) {
// if a max pool is provided, make sure to set poolMin equal to poolMax, as well as
// the poolIncrement to zero, based on oracledb documentation to prevent any
// kind of connection storms.
// For more info, checkout https://oracle.github.io/node-oracledb/doc/api.html#-1531-connection-pool-sizing
poolMax = srcCfg.MAX_POOL;
poolMin = srcCfg.MAX_POOL;
poolIncrement = 0;
}
if (!promises[poolName]) {
promises[poolName] = oracledb.createPool({
poolAlias: poolName,
user: srcCfg.DB_USER,
password: srcCfg.DB_PASSWORD,
connectString: `${srcCfg.DB_HOST}:${srcCfg.DB_PORT}/${srcCfg.DB_DATABASE}`,
poolMax,
poolMin,
poolIncrement
});
}
pools[poolName] = await promises[poolName];
console.debug(`Oracle Adapter: Pool ${poolName} created`);
return true;
} else {
console.error(`Oracle Adapter: Missing configuration for ${poolName}`);
return false;
}
} catch (err) {
console.error("Oracle Adapter: Error while closing connection", err);
return false;
}
};
exports.connect = async (poolName) => {
try {
if (!pools[poolName]) {
await this.createPool(poolName);
}
const connection = await oracledb.getConnection(poolName);
console.debug(`Oracle Adapter: Successfully retrieved a connection from ${poolName} pool`);
return connection;
} catch (err) {
console.error("Oracle Adapter: Error while retrieving a connection", err);
throw new Error(err.message);
}
};
exports.close = async (conn) => {
if (conn) {
try {
await conn.close();
console.debug("Oracle Adapter: DB connection returned to pool");
return true;
} catch (err) {
console.error("Oracle Adapter: Error while closing connection", err);
return false;
}
} else {
return true;
}
};
exports.closePool = async (poolAlias) => {
try {
await oracledb.getPool(poolAlias).close(10);
console.debug(`Oracle Adapter: Pool ${poolAlias} closed`);
return true;
} catch (err) {
console.error("Oracle Adapter: Error while closing connection", err);
return false;
}
};
exports.init = async (cfg) => {
config = cfg;
};
exports.execute = async (srcName, query, params = {}, options = {}) => {
let result;
let conn;
try {
console.debug(query);
if (params) {
console.debug(JSON.stringify(params));
}
const start = process.hrtime();
conn = await this.connect(srcName);
console.debug(
`Oracle Adapter: Connection secured: ${process.hrtime(start)[0]}s ${
process.hrtime(start)[1] / 1000000
}ms`
);
// to get the text from columns of type CLOB
oracledb.fetchAsString = [ oracledb.CLOB ];
result = await conn.execute(query, params, options);
console.debug(
`Oracle Adapter: Query executed: ${process.hrtime(start)[0]}s ${
process.hrtime(start)[1] / 1000000
}ms`
);
return result;
} catch (err) {
console.error("Oracle Adapter: Error while executing query", err);
throw new Error(err.message);
} finally {
if (conn) await conn.close();
}
};
/**
*
* @param {*} srcName - Connection name to connect to DB
* @param {*} query - SQL Query to execute. Example:
* INSERT INTO TABLE1 (ID, NAME) VALUES (:id, :name)
* @param {*} binds - Array of objects whose keys match the bind variable names in the SQL statement. For Example:
* [
* {id:1,name: name1},
* {id:2,name: name2}
* ]
* @param {*} options - It is an optional parameter contains following properties:
* 1. autoCommit
* 2. batchErrors - call will stop when first error occurs
* 3. bindDefs - object defines the bind variable types, sizes and directions. Example:
* bindDefs: {
* id: { type: dataSource.OracleDBTypes.NUMBER, maxSize: 5 },
* name: { type: dataSource.OracleDBTypes.STRING, maxSize: 10 }
* }
*/
exports.executeMany = async (srcName, query, binds = [], options = {}) => {
let result;
let conn;
try {
console.debug(query);
if (binds) {
console.debug(JSON.stringify(binds));
}
const start = process.hrtime();
conn = await this.connect(srcName);
console.debug(
`Oracle Adapter: Connection secured: ${process.hrtime(start)[0]}s ${
process.hrtime(start)[1] / 1000000
}ms`
);
result = await conn.executeMany(query, binds, options);
console.debug(
`Oracle Adapter: Query executed: ${process.hrtime(start)[0]}s ${
process.hrtime(start)[1] / 1000000
}ms`
);
return result;
} catch (err) {
console.error("Oracle Adapter: Error while executing query", err);
throw new Error(err.message);
} finally {
if (conn) await conn.close();
}
};
exports.executeStoredProc = async (srcName, storeproc, params = {}, options = {}) => {
let result;
let conn;
let rows = [];
try {
console.debug(storeproc);
if (params) {
console.debug(JSON.stringify(params));
}
const start = process.hrtime();
conn = await this.connect(srcName);
console.debug(
`Oracle Adapter: Connection secured: ${process.hrtime(start)[0]}s ${
process.hrtime(start)[1] / 1000000
}ms`
);
result = await conn.execute(
storeproc, params
);
console.debug(
`Oracle Adapter: Query executed: ${process.hrtime(start)[0]}s ${
process.hrtime(start)[1] / 1000000
}ms`
);
const resultSet = result.outBinds.response;
let row;
while ((row = await resultSet.getRow())) {
rows.push(row);
}
} catch (err) {
console.error("Oracle Adapter: Error while executing query", err);
throw new Error(err.message);
} finally {
if (conn) await conn.close();
return rows;
}
};
exports.closeAllPools = async () => {
try {
const tempPools = pools;
promises = {};
pools = {};
for (const poolAlias of Object.keys(tempPools)) {
await oracledb.getPool(poolAlias).close(10);
console.debug(`Oracle Adapter: Pool ${poolAlias} closed`);
}
return true;
} catch (err) {
console.error("Oracle Adapter: Error while closing connection", err);
return false;
}
};
/**
* create a new standalone, non-pooled query and executes query using
* the datasource based on the source name provided
*
* @param {string} sourceName - provide the data source name to be used. It should be
* one match one of the datasources set in the express.config file
* @param {string} query - provide the query to be executed
* @param {object} params - provide the bindings used in the query
* @param {object} options - provide oracleDB.ExecuteOptions
*/
exports.executeStandaloneConnection = async (
sourceName,
query,
params = {},
options = {}
) => {
const srcCfg = config.DATASOURCES[sourceName];
if (!srcCfg) {
console.error(`Oracle Adapter: Missing configuration for ${sourceName}`);
return false;
}
const connection = await oracledb.getConnection({
user: srcCfg.DB_USER,
password: srcCfg.DB_PASSWORD,
connectString: `${srcCfg.DB_HOST}:${srcCfg.DB_PORT}/${srcCfg.DB_DATABASE}`
});
const start = process.hrtime();
let result;
console.debug(
`Oracle Adapter: Connection secured: ${process.hrtime(start)[0]}s ${
process.hrtime(start)[1] / 1000000
}ms`
);
try {
result = await connection.execute(query, params, options);
console.debug(
`Oracle Adapter: Query executed: ${process.hrtime(start)[0]}s ${
process.hrtime(start)[1] / 1000000
}ms`
);
} catch (ex) {
console.error(`Oracle Adapter: There was an error at the moment of executing oracle query using a standalone connection: ${JSON.stringify(ex)}`);
} finally {
if (connection) await connection.close();
return result;
}
};