sworm
Version:
a lightweight write-only ORM for MSSQL, MySQL, PostgreSQL, Oracle, Sqlite 3
256 lines (207 loc) • 6.83 kB
JavaScript
var optionalRequire = require('./optionalRequire');
var promisify = require('./promisify');
var debug = require('debug')('sworm:oracle');
var swormDebug = require('debug')('sworm');
var _ = require('underscore');
var urlUtils = require('url');
var redactConfig = require('./redactConfig');
var outstandingQueries = require('./outstandingQueries');
var randomstring = require('randomstring');
var cooperative = require('cooperative')
module.exports = function () {
var oracledb = optionalRequire('oracledb');
return {
outstandingQueries: outstandingQueries(),
query: function (query, params, options) {
var resultsPromise = this.execute(replaceParameters(query), params, _.extend({outFormat: oracledb.ARRAY}, options))
if (options.statement || options.insert) {
return resultsPromise.then(function (results) {
var r = {}
if (options.statement) {
r.changes = results.rowsAffected
}
if (options.insert) {
r.id = results.outBinds.returning_into_id[0]
}
return r
})
} else {
return resultsPromise;
}
},
execute: function (query, params, options) {
var self = this;
debug(query, params);
var promise = options.statement || options.insert
? promisify(function (cb) {
self.connection.execute(query, params || {}, options, cb)
})
: this.queryResultSet(query, params || {}, options)
return this.outstandingQueries.execute(promise);
},
queryResultSet: function(query, params, options) {
return this.connection.execute(query, params, _.extend(options, {resultSet: true})).then(function (results) {
var resultSet = results.resultSet
var numberOfRows = oracledb.maxRows || 100
function fetchRows(allRows) {
return resultSet.getRows(numberOfRows).then(function (rows) {
allRows.push(rows)
if (rows.length < numberOfRows) {
return resultSet.close().then(function () {
return allRows
})
} else {
return fetchRows(allRows)
}
})
}
return fetchRows([]).then(function (_rows) {
var rows = _.flatten(_rows, true)
if (options.formatRows === false) {
return rows
} else {
return formatRows(results.metaData, rows)
}
}, function (error) {
return resultSet.close().then(function () {
throw error
})
})
}, function (error) {
if (/NJS-019/.test(error.message)) {
throw new Error('oracle: you cannot pass an SQL statement to db.query(), please use db.statement()')
} else {
throw error
}
})
},
insert: function(query, params, options) {
var id = options.id;
return this.query(query + ' returning ' + id + ' into :returning_into_id', params, options)
},
generateTransactionName: function() {
return 't' + randomstring.generate();
},
begin: function(options) {
var transactionName = options && options.name || this.generateTransactionName();
return this.query("set transaction name '" + transactionName + "'", undefined, {statement: true});
},
connect: function (swormConfig) {
var self = this;
var config = swormConfig.url? parseUrl(swormConfig.url): swormConfig.config;
oracledb.autoCommit = true;
if (config.options) {
Object.keys(config.options).forEach(function (key) {
oracledb[key] = config.options[key];
});
}
function makeConnection() {
if (config.pool === true) {
return connectionPool(oracledb, config, swormConfig).then(function (pool) {
return promisify(function (cb) { pool.getConnection(cb); });
});
} else if (config.pool) {
return promisify(function (cb) { config.pool.getConnection(cb); });
} else {
return promisify(function (cb) { oracledb.getConnection(config, cb); });
}
}
return makeConnection().then(function (connection) {
self.connection = connection;
});
},
close: function () {
var self = this;
if (self.connection) {
return this.outstandingQueries.whenNotExecuting(function () {
return promisify(function (cb) {
self.connection.release(cb);
});
});
} else {
return Promise.resolve();
}
},
insertEmpty: function(meta) {
return 'insert into ' + meta.table + ' (' + meta.id + ') values (default)';
},
outputIdKeys: function (idType) {
return {
returning_into_id: { type: idType || oracledb.NUMBER, dir: oracledb.BIND_OUT }
};
}
};
};
function formatRows(metadata, rows) {
var fields = metadata.map(function (field) {
if (/[a-z]/.test(field.name)) {
return field.name;
} else {
return field.name.toLowerCase();
}
});
if (fields.length > 0) {
var length = rows.length;
var results = new Array(length);
return cooperative.forEach(rows, function(row, index) {
var formattedRow = {};
results[index] = formattedRow;
for (var f = 0; f < fields.length; f++) {
formattedRow[fields[f]] = row[f];
}
}).then(function () {
return results
})
} else {
return Promise.resolve(rows);
}
}
function replaceParameters(query) {
return query.replace(/@([a-z_0-9]+)\b/gi, function (_, paramName) {
return ':' + paramName;
});
}
function parseValue(value) {
var number = Number(value);
if (!isNaN(number)) {
return number;
}
if (value == 'true' || value == 'false') {
return value == 'true';
}
return value;
}
function parseOptions(options) {
var result = {};
Object.keys(options).forEach(function (key) {
result[key] = parseValue(options[key]);
});
return result;
}
function parseUrl(url) {
var u = urlUtils.parse(url, true);
var auth = u.auth? u.auth.split(':'): [];
var options = parseOptions(u.query);
var pool = options.pool;
delete options.pool;
return {
user: auth[0],
password: auth[1],
connectString: u.host + u.pathname,
pool: pool,
options: options
};
}
var connectionPoolCache = {};
module.exports.connectionPoolCache = connectionPoolCache;
function connectionPool(oracledb, config, swormConfig) {
var key = JSON.stringify(config);
var value = connectionPoolCache[key];
if (!value) {
value = connectionPoolCache[key] = promisify(function (cb) {
swormDebug('creating connection pool', redactConfig(swormConfig));
oracledb.createPool(config, cb);
});
}
return value;
}