index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. 'use strict';
  2. var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
  3. var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
  4. var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
  5. var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
  6. var _inherits2 = require('babel-runtime/helpers/inherits');
  7. var _inherits3 = _interopRequireDefault(_inherits2);
  8. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  9. // Oracledb Client
  10. // -------
  11. var _ = require('lodash');
  12. var inherits = require('inherits');
  13. var QueryCompiler = require('./query/compiler');
  14. var ColumnCompiler = require('./schema/columncompiler');
  15. var BlobHelper = require('./utils').BlobHelper;
  16. var ReturningHelper = require('./utils').ReturningHelper;
  17. var Promise = require('bluebird');
  18. var stream = require('stream');
  19. var helpers = require('../../helpers');
  20. var Transaction = require('./transaction');
  21. var Client_Oracle = require('../oracle');
  22. var Oracle_Formatter = require('../oracle/formatter');
  23. var Buffer = require('safe-buffer').Buffer;
  24. function Client_Oracledb() {
  25. Client_Oracle.apply(this, arguments);
  26. // Node.js only have 4 background threads by default, oracledb needs one by connection
  27. if (this.driver) {
  28. process.env.UV_THREADPOOL_SIZE = process.env.UV_THREADPOOL_SIZE || 1;
  29. process.env.UV_THREADPOOL_SIZE += this.driver.poolMax;
  30. }
  31. }
  32. inherits(Client_Oracledb, Client_Oracle);
  33. Client_Oracledb.prototype.driverName = 'oracledb';
  34. Client_Oracledb.prototype._driver = function () {
  35. var oracledb = require('oracledb');
  36. return oracledb;
  37. };
  38. Client_Oracledb.prototype.queryCompiler = function () {
  39. return new (Function.prototype.bind.apply(QueryCompiler, [null].concat([this], Array.prototype.slice.call(arguments))))();
  40. };
  41. Client_Oracledb.prototype.columnCompiler = function () {
  42. return new (Function.prototype.bind.apply(ColumnCompiler, [null].concat([this], Array.prototype.slice.call(arguments))))();
  43. };
  44. Client_Oracledb.prototype.formatter = function () {
  45. return new Oracledb_Formatter(this);
  46. };
  47. Client_Oracledb.prototype.transaction = function () {
  48. return new (Function.prototype.bind.apply(Transaction, [null].concat([this], Array.prototype.slice.call(arguments))))();
  49. };
  50. Client_Oracledb.prototype.prepBindings = function (bindings) {
  51. var _this = this;
  52. return _.map(bindings, function (value) {
  53. if (value instanceof BlobHelper && _this.driver) {
  54. return { type: _this.driver.BLOB, dir: _this.driver.BIND_OUT };
  55. // Returning helper always use ROWID as string
  56. } else if (value instanceof ReturningHelper && _this.driver) {
  57. return { type: _this.driver.STRING, dir: _this.driver.BIND_OUT };
  58. } else if (typeof value === 'boolean') {
  59. return value ? 1 : 0;
  60. }
  61. return value;
  62. });
  63. };
  64. // Get a raw connection, called by the `pool` whenever a new
  65. // connection needs to be added to the pool.
  66. Client_Oracledb.prototype.acquireRawConnection = function () {
  67. var client = this;
  68. var asyncConnection = new Promise(function (resolver, rejecter) {
  69. // If external authentication dont have to worry about username/password and
  70. // if not need to set the username and password
  71. var oracleDbConfig = client.connectionSettings.externalAuth ? { externalAuth: client.connectionSettings.externalAuth } : {
  72. user: client.connectionSettings.user,
  73. password: client.connectionSettings.password
  74. };
  75. // In the case of external authentication connection string will be given
  76. oracleDbConfig.connectString = client.connectionSettings.connectString || client.connectionSettings.host + '/' + client.connectionSettings.database;
  77. if (client.connectionSettings.prefetchRowCount) {
  78. oracleDbConfig.prefetchRows = client.connectionSettings.prefetchRowCount;
  79. }
  80. if (!_.isUndefined(client.connectionSettings.stmtCacheSize)) {
  81. oracleDbConfig.stmtCacheSize = client.connectionSettings.stmtCacheSize;
  82. }
  83. client.driver.getConnection(oracleDbConfig, function (err, connection) {
  84. if (err) {
  85. return rejecter(err);
  86. }
  87. connection.commitAsync = function () {
  88. var _this2 = this;
  89. return new Promise(function (commitResolve, commitReject) {
  90. if (connection.isTransaction) {
  91. return commitResolve();
  92. }
  93. _this2.commit(function (err) {
  94. if (err) {
  95. return commitReject(err);
  96. }
  97. commitResolve();
  98. });
  99. });
  100. };
  101. connection.rollbackAsync = function () {
  102. var _this3 = this;
  103. return new Promise(function (rollbackResolve, rollbackReject) {
  104. _this3.rollback(function (err) {
  105. if (err) {
  106. return rollbackReject(err);
  107. }
  108. rollbackResolve();
  109. });
  110. });
  111. };
  112. var fetchAsync = function fetchAsync(sql, bindParams, options, cb) {
  113. options = options || {};
  114. options.outFormat = client.driver.OBJECT;
  115. if (options.resultSet) {
  116. connection.execute(sql, bindParams || [], options, function (err, result) {
  117. if (err) {
  118. return cb(err);
  119. }
  120. var fetchResult = { rows: [], resultSet: result.resultSet };
  121. var numRows = 100;
  122. var fetchRowsFromRS = function fetchRowsFromRS(connection, resultSet, numRows) {
  123. resultSet.getRows(numRows, function (err, rows) {
  124. if (err) {
  125. resultSet.close(function () {
  126. return cb(err);
  127. });
  128. } else if (rows.length === 0) {
  129. return cb(null, fetchResult);
  130. } else if (rows.length > 0) {
  131. if (rows.length === numRows) {
  132. fetchResult.rows = fetchResult.rows.concat(rows);
  133. fetchRowsFromRS(connection, resultSet, numRows);
  134. } else {
  135. fetchResult.rows = fetchResult.rows.concat(rows);
  136. return cb(null, fetchResult);
  137. }
  138. }
  139. });
  140. };
  141. fetchRowsFromRS(connection, result.resultSet, numRows);
  142. });
  143. } else {
  144. connection.execute(sql, bindParams || [], options, cb);
  145. }
  146. };
  147. connection.executeAsync = function (sql, bindParams, options) {
  148. // Read all lob
  149. return new Promise(function (resultResolve, resultReject) {
  150. fetchAsync(sql, bindParams, options, function (err, results) {
  151. if (err) {
  152. return resultReject(err);
  153. }
  154. // Collect LOBs to read
  155. var lobs = [];
  156. if (results.rows) {
  157. if (Array.isArray(results.rows)) {
  158. for (var i = 0; i < results.rows.length; i++) {
  159. // Iterate through the rows
  160. var row = results.rows[i];
  161. for (var column in row) {
  162. if (row[column] instanceof stream.Readable) {
  163. lobs.push({ index: i, key: column, stream: row[column] });
  164. }
  165. }
  166. }
  167. }
  168. }
  169. Promise.each(lobs, function (lob) {
  170. return new Promise(function (lobResolve, lobReject) {
  171. readStream(lob.stream, function (err, d) {
  172. if (err) {
  173. if (results.resultSet) {
  174. results.resultSet.close(function () {
  175. return lobReject(err);
  176. });
  177. }
  178. return lobReject(err);
  179. }
  180. results.rows[lob.index][lob.key] = d;
  181. lobResolve();
  182. });
  183. });
  184. }).then(function () {
  185. if (results.resultSet) {
  186. results.resultSet.close(function (err) {
  187. if (err) {
  188. return resultReject(err);
  189. }
  190. return resultResolve(results);
  191. });
  192. }
  193. resultResolve(results);
  194. }, function (err) {
  195. resultReject(err);
  196. });
  197. });
  198. });
  199. };
  200. resolver(connection);
  201. });
  202. });
  203. return asyncConnection;
  204. };
  205. // Used to explicitly close a connection, called internally by the pool
  206. // when a connection times out or the pool is shutdown.
  207. Client_Oracledb.prototype.destroyRawConnection = function (connection) {
  208. connection.release();
  209. };
  210. // Runs the query on the specified connection, providing the bindings
  211. // and any other necessary prep work.
  212. Client_Oracledb.prototype._query = function (connection, obj) {
  213. // Convert ? params into positional bindings (:1)
  214. obj.sql = this.positionBindings(obj.sql);
  215. obj.bindings = this.prepBindings(obj.bindings) || [];
  216. return new Promise(function (resolver, rejecter) {
  217. if (!obj.sql) {
  218. return rejecter(new Error('The query is empty'));
  219. }
  220. var options = { autoCommit: false };
  221. if (obj.method === 'select') {
  222. options.resultSet = true;
  223. }
  224. connection.executeAsync(obj.sql, obj.bindings, options).then(function (response) {
  225. // Flatten outBinds
  226. var outBinds = _.flatten(response.outBinds);
  227. obj.response = response.rows || [];
  228. obj.rowsAffected = response.rows ? response.rows.rowsAffected : response.rowsAffected;
  229. if (obj.method === 'update') {
  230. (function () {
  231. var modifiedRowsCount = obj.rowsAffected.length || obj.rowsAffected;
  232. var updatedObjOutBinding = [];
  233. var updatedOutBinds = [];
  234. var updateOutBinds = function updateOutBinds(i) {
  235. return function (value, index) {
  236. var OutBindsOffset = index * modifiedRowsCount;
  237. updatedOutBinds.push(outBinds[i + OutBindsOffset]);
  238. };
  239. };
  240. for (var i = 0; i < modifiedRowsCount; i++) {
  241. updatedObjOutBinding.push(obj.outBinding[0]);
  242. _.each(obj.outBinding[0], updateOutBinds(i));
  243. }
  244. outBinds = updatedOutBinds;
  245. obj.outBinding = updatedObjOutBinding;
  246. })();
  247. }
  248. if (!obj.returning && outBinds.length === 0) {
  249. return connection.commitAsync().then(function () {
  250. resolver(obj);
  251. });
  252. }
  253. var rowIds = [];
  254. var offset = 0;
  255. Promise.each(obj.outBinding, function (ret, line) {
  256. offset = offset + (obj.outBinding[line - 1] ? obj.outBinding[line - 1].length : 0);
  257. return Promise.each(ret, function (out, index) {
  258. return new Promise(function (bindResolver, bindRejecter) {
  259. if (out instanceof BlobHelper) {
  260. var blob = outBinds[index + offset];
  261. if (out.returning) {
  262. obj.response[line] = obj.response[line] || {};
  263. obj.response[line][out.columnName] = out.value;
  264. }
  265. blob.on('error', function (err) {
  266. bindRejecter(err);
  267. });
  268. blob.on('finish', function () {
  269. bindResolver();
  270. });
  271. blob.write(out.value);
  272. blob.end();
  273. } else if (obj.outBinding[line][index] === 'ROWID') {
  274. rowIds.push(outBinds[index + offset]);
  275. bindResolver();
  276. } else {
  277. obj.response[line] = obj.response[line] || {};
  278. obj.response[line][out] = outBinds[index + offset];
  279. bindResolver();
  280. }
  281. });
  282. });
  283. }).then(function () {
  284. return connection.commitAsync();
  285. }).then(function () {
  286. if (obj.returningSql) {
  287. return connection.executeAsync(obj.returningSql(), rowIds, { resultSet: true }).then(function (response) {
  288. obj.response = response.rows;
  289. return obj;
  290. }, rejecter);
  291. }
  292. return obj;
  293. }, rejecter).then(function (obj) {
  294. resolver(obj);
  295. });
  296. }, rejecter);
  297. });
  298. };
  299. // Handle clob
  300. function readStream(stream, cb) {
  301. var oracledb = require('oracledb');
  302. var data = '';
  303. if (stream.iLob.type === oracledb.CLOB) {
  304. stream.setEncoding('utf-8');
  305. } else {
  306. data = Buffer.alloc(0);
  307. }
  308. stream.on('error', function (err) {
  309. cb(err);
  310. });
  311. stream.on('data', function (chunk) {
  312. if (stream.iLob.type === oracledb.CLOB) {
  313. data += chunk;
  314. } else {
  315. data = Buffer.concat([data, chunk]);
  316. }
  317. });
  318. stream.on('end', function () {
  319. cb(null, data);
  320. });
  321. }
  322. // Process the response as returned from the query.
  323. Client_Oracledb.prototype.processResponse = function (obj, runner) {
  324. var response = obj.response;
  325. var method = obj.method;
  326. if (obj.output) {
  327. return obj.output.call(runner, response);
  328. }
  329. switch (method) {
  330. case 'select':
  331. case 'pluck':
  332. case 'first':
  333. response = helpers.skim(response);
  334. if (obj.method === 'pluck') {
  335. response = _.map(response, obj.pluck);
  336. }
  337. return obj.method === 'first' ? response[0] : response;
  338. case 'insert':
  339. case 'del':
  340. case 'update':
  341. case 'counter':
  342. if (obj.returning && !_.isEmpty(obj.returning)) {
  343. if (obj.returning.length === 1 && obj.returning[0] !== '*') {
  344. return _.flatten(_.map(response, _.values));
  345. }
  346. return response;
  347. } else if (!_.isUndefined(obj.rowsAffected)) {
  348. return obj.rowsAffected;
  349. } else {
  350. return 1;
  351. }
  352. default:
  353. return response;
  354. }
  355. };
  356. var Oracledb_Formatter = function (_Oracle_Formatter) {
  357. (0, _inherits3.default)(Oracledb_Formatter, _Oracle_Formatter);
  358. function Oracledb_Formatter() {
  359. (0, _classCallCheck3.default)(this, Oracledb_Formatter);
  360. return (0, _possibleConstructorReturn3.default)(this, _Oracle_Formatter.apply(this, arguments));
  361. }
  362. // Checks whether a value is a function... if it is, we compile it
  363. // otherwise we check whether it's a raw
  364. Oracledb_Formatter.prototype.parameter = function parameter(value) {
  365. if (typeof value === 'function') {
  366. return this.outputQuery(this.compileCallback(value), true);
  367. } else if (value instanceof BlobHelper) {
  368. return 'EMPTY_BLOB()';
  369. }
  370. return this.unwrapRaw(value, true) || '?';
  371. };
  372. return Oracledb_Formatter;
  373. }(Oracle_Formatter);
  374. module.exports = Client_Oracledb;