promise.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. 'use strict';
  2. const core = require('./index.js');
  3. const EventEmitter = require('events').EventEmitter;
  4. function makeDoneCb(resolve, reject, localErr) {
  5. return function (err, rows, fields) {
  6. if (err) {
  7. localErr.message = err.message;
  8. localErr.code = err.code;
  9. localErr.errno = err.errno;
  10. localErr.sql = err.sql;
  11. localErr.sqlState = err.sqlState;
  12. localErr.sqlMessage = err.sqlMessage;
  13. reject(localErr);
  14. } else {
  15. resolve([rows, fields]);
  16. }
  17. };
  18. }
  19. function inheritEvents(source, target, events) {
  20. const listeners = {};
  21. target
  22. .on('newListener', eventName => {
  23. if (events.indexOf(eventName) >= 0 && !target.listenerCount(eventName)) {
  24. source.on(
  25. eventName,
  26. (listeners[eventName] = function () {
  27. const args = [].slice.call(arguments);
  28. args.unshift(eventName);
  29. target.emit.apply(target, args);
  30. })
  31. );
  32. }
  33. })
  34. .on('removeListener', eventName => {
  35. if (events.indexOf(eventName) >= 0 && !target.listenerCount(eventName)) {
  36. source.removeListener(eventName, listeners[eventName]);
  37. delete listeners[eventName];
  38. }
  39. });
  40. }
  41. class PromisePreparedStatementInfo {
  42. constructor(statement, promiseImpl) {
  43. this.statement = statement;
  44. this.Promise = promiseImpl;
  45. }
  46. execute(parameters) {
  47. const s = this.statement;
  48. const localErr = new Error();
  49. return new this.Promise((resolve, reject) => {
  50. const done = makeDoneCb(resolve, reject, localErr);
  51. if (parameters) {
  52. s.execute(parameters, done);
  53. } else {
  54. s.execute(done);
  55. }
  56. });
  57. }
  58. close() {
  59. return new this.Promise(resolve => {
  60. this.statement.close();
  61. resolve();
  62. });
  63. }
  64. }
  65. class PromiseConnection extends EventEmitter {
  66. constructor(connection, promiseImpl) {
  67. super();
  68. this.connection = connection;
  69. this.Promise = promiseImpl || Promise;
  70. inheritEvents(connection, this, [
  71. 'error',
  72. 'drain',
  73. 'connect',
  74. 'end',
  75. 'enqueue'
  76. ]);
  77. }
  78. release() {
  79. this.connection.release();
  80. }
  81. query(query, params) {
  82. const c = this.connection;
  83. const localErr = new Error();
  84. if (typeof params === 'function') {
  85. throw new Error(
  86. 'Callback function is not available with promise clients.'
  87. );
  88. }
  89. return new this.Promise((resolve, reject) => {
  90. const done = makeDoneCb(resolve, reject, localErr);
  91. if (params !== undefined) {
  92. c.query(query, params, done);
  93. } else {
  94. c.query(query, done);
  95. }
  96. });
  97. }
  98. execute(query, params) {
  99. const c = this.connection;
  100. const localErr = new Error();
  101. if (typeof params === 'function') {
  102. throw new Error(
  103. 'Callback function is not available with promise clients.'
  104. );
  105. }
  106. return new this.Promise((resolve, reject) => {
  107. const done = makeDoneCb(resolve, reject, localErr);
  108. if (params !== undefined) {
  109. c.execute(query, params, done);
  110. } else {
  111. c.execute(query, done);
  112. }
  113. });
  114. }
  115. end() {
  116. return new this.Promise(resolve => {
  117. this.connection.end(resolve);
  118. });
  119. }
  120. beginTransaction() {
  121. const c = this.connection;
  122. const localErr = new Error();
  123. return new this.Promise((resolve, reject) => {
  124. const done = makeDoneCb(resolve, reject, localErr);
  125. c.beginTransaction(done);
  126. });
  127. }
  128. commit() {
  129. const c = this.connection;
  130. const localErr = new Error();
  131. return new this.Promise((resolve, reject) => {
  132. const done = makeDoneCb(resolve, reject, localErr);
  133. c.commit(done);
  134. });
  135. }
  136. rollback() {
  137. const c = this.connection;
  138. const localErr = new Error();
  139. return new this.Promise((resolve, reject) => {
  140. const done = makeDoneCb(resolve, reject, localErr);
  141. c.rollback(done);
  142. });
  143. }
  144. ping() {
  145. const c = this.connection;
  146. const localErr = new Error();
  147. return new this.Promise((resolve, reject) => {
  148. c.ping(err => {
  149. if (err) {
  150. localErr.message = err.message;
  151. localErr.code = err.code;
  152. localErr.errno = err.errno;
  153. localErr.sqlState = err.sqlState;
  154. localErr.sqlMessage = err.sqlMessage;
  155. reject(localErr);
  156. } else {
  157. resolve(true);
  158. }
  159. });
  160. });
  161. }
  162. connect() {
  163. const c = this.connection;
  164. const localErr = new Error();
  165. return new this.Promise((resolve, reject) => {
  166. c.connect((err, param) => {
  167. if (err) {
  168. localErr.message = err.message;
  169. localErr.code = err.code;
  170. localErr.errno = err.errno;
  171. localErr.sqlState = err.sqlState;
  172. localErr.sqlMessage = err.sqlMessage;
  173. reject(localErr);
  174. } else {
  175. resolve(param);
  176. }
  177. });
  178. });
  179. }
  180. prepare(options) {
  181. const c = this.connection;
  182. const promiseImpl = this.Promise;
  183. const localErr = new Error();
  184. return new this.Promise((resolve, reject) => {
  185. c.prepare(options, (err, statement) => {
  186. if (err) {
  187. localErr.message = err.message;
  188. localErr.code = err.code;
  189. localErr.errno = err.errno;
  190. localErr.sqlState = err.sqlState;
  191. localErr.sqlMessage = err.sqlMessage;
  192. reject(localErr);
  193. } else {
  194. const wrappedStatement = new PromisePreparedStatementInfo(
  195. statement,
  196. promiseImpl
  197. );
  198. resolve(wrappedStatement);
  199. }
  200. });
  201. });
  202. }
  203. changeUser(options) {
  204. const c = this.connection;
  205. const localErr = new Error();
  206. return new this.Promise((resolve, reject) => {
  207. c.changeUser(options, err => {
  208. if (err) {
  209. localErr.message = err.message;
  210. localErr.code = err.code;
  211. localErr.errno = err.errno;
  212. localErr.sqlState = err.sqlState;
  213. localErr.sqlMessage = err.sqlMessage;
  214. reject(localErr);
  215. } else {
  216. resolve();
  217. }
  218. });
  219. });
  220. }
  221. get config() {
  222. return this.connection.config;
  223. }
  224. get threadId() {
  225. return this.connection.threadId;
  226. }
  227. }
  228. function createConnection(opts) {
  229. const coreConnection = core.createConnection(opts);
  230. const createConnectionErr = new Error();
  231. const thePromise = opts.Promise || Promise;
  232. if (!thePromise) {
  233. throw new Error(
  234. 'no Promise implementation available.' +
  235. 'Use promise-enabled node version or pass userland Promise' +
  236. " implementation as parameter, for example: { Promise: require('bluebird') }"
  237. );
  238. }
  239. return new thePromise((resolve, reject) => {
  240. coreConnection.once('connect', () => {
  241. resolve(new PromiseConnection(coreConnection, thePromise));
  242. });
  243. coreConnection.once('error', err => {
  244. createConnectionErr.message = err.message;
  245. createConnectionErr.code = err.code;
  246. createConnectionErr.errno = err.errno;
  247. createConnectionErr.sqlState = err.sqlState;
  248. reject(createConnectionErr);
  249. });
  250. });
  251. }
  252. // note: the callback of "changeUser" is not called on success
  253. // hence there is no possibility to call "resolve"
  254. // patching PromiseConnection
  255. // create facade functions for prototype functions on "Connection" that are not yet
  256. // implemented with PromiseConnection
  257. // proxy synchronous functions only
  258. (function (functionsToWrap) {
  259. for (let i = 0; functionsToWrap && i < functionsToWrap.length; i++) {
  260. const func = functionsToWrap[i];
  261. if (
  262. typeof core.Connection.prototype[func] === 'function' &&
  263. PromiseConnection.prototype[func] === undefined
  264. ) {
  265. PromiseConnection.prototype[func] = (function factory(funcName) {
  266. return function () {
  267. return core.Connection.prototype[funcName].apply(
  268. this.connection,
  269. arguments
  270. );
  271. };
  272. })(func);
  273. }
  274. }
  275. })([
  276. // synchronous functions
  277. 'close',
  278. 'createBinlogStream',
  279. 'destroy',
  280. 'escape',
  281. 'escapeId',
  282. 'format',
  283. 'pause',
  284. 'pipe',
  285. 'resume',
  286. 'unprepare'
  287. ]);
  288. class PromisePoolConnection extends PromiseConnection {
  289. constructor(connection, promiseImpl) {
  290. super(connection, promiseImpl);
  291. }
  292. destroy() {
  293. return core.PoolConnection.prototype.destroy.apply(
  294. this.connection,
  295. arguments
  296. );
  297. }
  298. }
  299. class PromisePool extends EventEmitter {
  300. constructor(pool, thePromise) {
  301. super();
  302. this.pool = pool;
  303. this.Promise = thePromise || Promise;
  304. inheritEvents(pool, this, ['acquire', 'connection', 'enqueue', 'release']);
  305. }
  306. getConnection() {
  307. const corePool = this.pool;
  308. return new this.Promise((resolve, reject) => {
  309. corePool.getConnection((err, coreConnection) => {
  310. if (err) {
  311. reject(err);
  312. } else {
  313. resolve(new PromisePoolConnection(coreConnection, this.Promise));
  314. }
  315. });
  316. });
  317. }
  318. query(sql, args) {
  319. const corePool = this.pool;
  320. const localErr = new Error();
  321. if (typeof args === 'function') {
  322. throw new Error(
  323. 'Callback function is not available with promise clients.'
  324. );
  325. }
  326. return new this.Promise((resolve, reject) => {
  327. const done = makeDoneCb(resolve, reject, localErr);
  328. if (args !== undefined) {
  329. corePool.query(sql, args, done);
  330. } else {
  331. corePool.query(sql, done);
  332. }
  333. });
  334. }
  335. execute(sql, args) {
  336. const corePool = this.pool;
  337. const localErr = new Error();
  338. if (typeof args === 'function') {
  339. throw new Error(
  340. 'Callback function is not available with promise clients.'
  341. );
  342. }
  343. return new this.Promise((resolve, reject) => {
  344. const done = makeDoneCb(resolve, reject, localErr);
  345. if (args) {
  346. corePool.execute(sql, args, done);
  347. } else {
  348. corePool.execute(sql, done);
  349. }
  350. });
  351. }
  352. end() {
  353. const corePool = this.pool;
  354. const localErr = new Error();
  355. return new this.Promise((resolve, reject) => {
  356. corePool.end(err => {
  357. if (err) {
  358. localErr.message = err.message;
  359. localErr.code = err.code;
  360. localErr.errno = err.errno;
  361. localErr.sqlState = err.sqlState;
  362. localErr.sqlMessage = err.sqlMessage;
  363. reject(localErr);
  364. } else {
  365. resolve();
  366. }
  367. });
  368. });
  369. }
  370. }
  371. function createPool(opts) {
  372. const corePool = core.createPool(opts);
  373. const thePromise = opts.Promise || Promise;
  374. if (!thePromise) {
  375. throw new Error(
  376. 'no Promise implementation available.' +
  377. 'Use promise-enabled node version or pass userland Promise' +
  378. " implementation as parameter, for example: { Promise: require('bluebird') }"
  379. );
  380. }
  381. return new PromisePool(corePool, thePromise);
  382. }
  383. (function (functionsToWrap) {
  384. for (let i = 0; functionsToWrap && i < functionsToWrap.length; i++) {
  385. const func = functionsToWrap[i];
  386. if (
  387. typeof core.Pool.prototype[func] === 'function' &&
  388. PromisePool.prototype[func] === undefined
  389. ) {
  390. PromisePool.prototype[func] = (function factory(funcName) {
  391. return function () {
  392. return core.Pool.prototype[funcName].apply(this.pool, arguments);
  393. };
  394. })(func);
  395. }
  396. }
  397. })([
  398. // synchronous functions
  399. 'escape',
  400. 'escapeId',
  401. 'format'
  402. ]);
  403. class PromisePoolCluster extends EventEmitter {
  404. constructor(poolCluster, thePromise) {
  405. super();
  406. this.poolCluster = poolCluster;
  407. this.Promise = thePromise || Promise;
  408. inheritEvents(poolCluster, this, ['acquire', 'connection', 'enqueue', 'release']);
  409. }
  410. getConnection() {
  411. const corePoolCluster = this.poolCluster;
  412. return new this.Promise((resolve, reject) => {
  413. corePoolCluster.getConnection((err, coreConnection) => {
  414. if (err) {
  415. reject(err);
  416. } else {
  417. resolve(new PromisePoolConnection(coreConnection, this.Promise));
  418. }
  419. });
  420. });
  421. }
  422. query(sql, args) {
  423. const corePoolCluster = this.poolCluster;
  424. const localErr = new Error();
  425. if (typeof args === 'function') {
  426. throw new Error(
  427. 'Callback function is not available with promise clients.'
  428. );
  429. }
  430. return new this.Promise((resolve, reject) => {
  431. const done = makeDoneCb(resolve, reject, localErr);
  432. corePoolCluster.query(sql, args, done);
  433. });
  434. }
  435. execute(sql, args) {
  436. const corePoolCluster = this.poolCluster;
  437. const localErr = new Error();
  438. if (typeof args === 'function') {
  439. throw new Error(
  440. 'Callback function is not available with promise clients.'
  441. );
  442. }
  443. return new this.Promise((resolve, reject) => {
  444. const done = makeDoneCb(resolve, reject, localErr);
  445. corePoolCluster.execute(sql, args, done);
  446. });
  447. }
  448. of(pattern, selector) {
  449. return new PromisePoolCluster(
  450. this.poolCluster.of(pattern, selector),
  451. this.Promise
  452. );
  453. }
  454. end() {
  455. const corePoolCluster = this.poolCluster;
  456. const localErr = new Error();
  457. return new this.Promise((resolve, reject) => {
  458. corePoolCluster.end(err => {
  459. if (err) {
  460. localErr.message = err.message;
  461. localErr.code = err.code;
  462. localErr.errno = err.errno;
  463. localErr.sqlState = err.sqlState;
  464. localErr.sqlMessage = err.sqlMessage;
  465. reject(localErr);
  466. } else {
  467. resolve();
  468. }
  469. });
  470. });
  471. }
  472. }
  473. /**
  474. * proxy poolCluster synchronous functions
  475. */
  476. (function (functionsToWrap) {
  477. for (let i = 0; functionsToWrap && i < functionsToWrap.length; i++) {
  478. const func = functionsToWrap[i];
  479. if (
  480. typeof core.PoolCluster.prototype[func] === 'function' &&
  481. PromisePoolCluster.prototype[func] === undefined
  482. ) {
  483. PromisePoolCluster.prototype[func] = (function factory(funcName) {
  484. return function () {
  485. return core.PoolCluster.prototype[funcName].apply(this.poolCluster, arguments);
  486. };
  487. })(func);
  488. }
  489. }
  490. })([
  491. 'add'
  492. ]);
  493. function createPoolCluster(opts) {
  494. const corePoolCluster = core.createPoolCluster(opts);
  495. const thePromise = (opts && opts.Promise) || Promise;
  496. if (!thePromise) {
  497. throw new Error(
  498. 'no Promise implementation available.' +
  499. 'Use promise-enabled node version or pass userland Promise' +
  500. " implementation as parameter, for example: { Promise: require('bluebird') }"
  501. );
  502. }
  503. return new PromisePoolCluster(corePoolCluster, thePromise);
  504. }
  505. exports.createConnection = createConnection;
  506. exports.createPool = createPool;
  507. exports.createPoolCluster = createPoolCluster;
  508. exports.escape = core.escape;
  509. exports.escapeId = core.escapeId;
  510. exports.format = core.format;
  511. exports.raw = core.raw;
  512. exports.PromisePool = PromisePool;
  513. exports.PromiseConnection = PromiseConnection;
  514. exports.PromisePoolConnection = PromisePoolConnection;