Source: query.js

  1. /*global process*/
  2. /**
  3. * @file Manages query for records in Salesforce
  4. * @author Shinichi Tomita <shinichi.tomita@gmail.com>
  5. */
  6. 'use strict';
  7. var inherits = require('inherits'),
  8. events = require('events'),
  9. stream = require('readable-stream'),
  10. _ = require('lodash/core'),
  11. Promise = require('./promise'),
  12. SfDate = require("./date"),
  13. SOQLBuilder = require("./soql-builder"),
  14. RecordStream = require("./record-stream");
  15. /**
  16. * Query
  17. *
  18. * @protected
  19. * @class
  20. * @extends {stream.Readable}
  21. * @implements Promise.<T>
  22. * @template T
  23. * @param {Connection} conn - Connection object
  24. * @param {Object|String} config - Query config object or SOQL string
  25. * @param {String} [locator] - Locator string to fetch next record set
  26. */
  27. var Query = module.exports = function(conn, config, locator) {
  28. Query.super_.call(this, { objectMode: true });
  29. this._conn = conn;
  30. if (config) {
  31. if (_.isString(config)) { // if query config is string, it is given in SOQL.
  32. this._soql = config;
  33. } else {
  34. this._config = config;
  35. this.select(config.fields);
  36. if (config.includes) {
  37. this.include(config.includes);
  38. }
  39. }
  40. }
  41. if (locator && locator.indexOf("/") >= 0) { // if locator given in url for next records
  42. locator = locator.split("/").pop();
  43. }
  44. this._locator = locator;
  45. this._executed = false;
  46. this._finished = false;
  47. this._chaining = false;
  48. this._deferred = Promise.defer();
  49. var self = this;
  50. };
  51. inherits(Query, stream.Readable);
  52. /**
  53. * Select fields to include in the returning result
  54. *
  55. * @param {Object|Array.<String>|String} fields - Fields to fetch. Format can be in JSON object (MongoDB-like), array of field names, or comma-separated field names.
  56. * @returns {Query.<T>}
  57. */
  58. Query.prototype.select = function(fields) {
  59. if (this._soql) {
  60. throw Error("Cannot set select fields for the query which has already built SOQL.");
  61. }
  62. fields = fields || '*';
  63. if (_.isString(fields)) {
  64. fields = fields.split(/\s*,\s*/);
  65. } else if (_.isObject(fields) && !_.isArray(fields)) {
  66. var _fields = [];
  67. for (var k in fields) {
  68. if (fields[k]) { _fields.push(k); }
  69. }
  70. fields = _fields;
  71. }
  72. this._config.fields = fields;
  73. return this;
  74. };
  75. /**
  76. * Set query conditions to filter the result records
  77. *
  78. * @param {Object|String} conditions - Conditions in JSON object (MongoDB-like), or raw SOQL WHERE clause string.
  79. * @returns {Query.<T>}
  80. */
  81. Query.prototype.where = function(conditions) {
  82. if (this._soql) {
  83. throw Error("Cannot set where conditions for the query which has already built SOQL.");
  84. }
  85. this._config.conditions = conditions;
  86. return this;
  87. };
  88. /**
  89. * Limit the returning result
  90. *
  91. * @param {Number} limit - Maximum number of records the query will return.
  92. * @returns {Query.<T>}
  93. */
  94. Query.prototype.limit = function(limit) {
  95. if (this._soql) {
  96. throw Error("Cannot set limit for the query which has already built SOQL.");
  97. }
  98. this._config.limit = limit;
  99. return this;
  100. };
  101. /**
  102. * Synonym of Query#skip()
  103. *
  104. * @method Query#offset
  105. * @param {Number} offset - Offset number where begins returning results.
  106. * @returns {Query.<T>}
  107. */
  108. /**
  109. * Skip records
  110. *
  111. * @method Query#offset
  112. * @param {Number} offset - Offset number where begins returning results.
  113. * @returns {Query.<T>}
  114. */
  115. Query.prototype.skip =
  116. Query.prototype.offset = function(offset) {
  117. if (this._soql) {
  118. throw Error("Cannot set skip/offset for the query which has already built SOQL.");
  119. }
  120. this._config.offset = offset;
  121. return this;
  122. };
  123. /**
  124. * Synonym of Query#sort()
  125. *
  126. * @memthod Query#orderby
  127. * @param {String|Object} sort - Sorting field or hash object with field name and sord direction
  128. * @param {String|Number} [dir] - Sorting direction (ASC|DESC|1|-1)
  129. * @returns {Query.<T>}
  130. */
  131. /**
  132. * Set query sort with direction
  133. *
  134. * @method Query#sort
  135. * @param {String|Object} sort - Sorting field or hash object with field name and sord direction
  136. * @param {String|Number} [dir] - Sorting direction (ASC|DESC|1|-1)
  137. * @returns {Query.<T>}
  138. */
  139. Query.prototype.sort =
  140. Query.prototype.orderby = function(sort, dir) {
  141. if (this._soql) {
  142. throw Error("Cannot set sort for the query which has already built SOQL.");
  143. }
  144. if (_.isString(sort) && _.isString(dir)) {
  145. sort = [ [ sort, dir ] ];
  146. }
  147. this._config.sort = sort;
  148. return this;
  149. };
  150. /**
  151. * Include child relationship query
  152. *
  153. * @param {String} childRelName - Child relationship name to include in query result
  154. * @param {Object|String} [conditions] - Conditions in JSON object (MongoDB-like), or raw SOQL WHERE clause string.
  155. * @param {Object|Array.<String>|String} [fields] - Fields to fetch. Format can be in JSON object (MongoDB-like), array of field names, or comma-separated field names.
  156. * @param {Object} [options] - Query options.
  157. * @param {Number} [options.limit] - Maximum number of records the query will return.
  158. * @param {Number} [options.offset] - Offset number where begins returning results.
  159. * @param {Number} [options.skip] - Synonym of options.offset.
  160. * @returns {Query~SubQuery}
  161. */
  162. Query.prototype.include = function(childRelName, conditions, fields, options) {
  163. if (this._soql) {
  164. throw Error("Cannot include child relationship into the query which has already built SOQL.");
  165. }
  166. if (_.isObject(childRelName)) {
  167. var includes = childRelName;
  168. for (var crname in includes) {
  169. var config = includes[crname];
  170. this.include(crname, config.conditions, config.fields, config);
  171. }
  172. return;
  173. }
  174. var childConfig = {
  175. table: childRelName,
  176. conditions: conditions,
  177. fields: fields,
  178. limit: options && options.limit,
  179. offset: options && (options.offset || options.skip)
  180. };
  181. this._config.includes = this._config.includes || [];
  182. this._config.includes.push(childConfig);
  183. var childQuery = new SubQuery(this._conn, this, childConfig);
  184. this._children = this._children || [];
  185. this._children.push(childQuery);
  186. return childQuery;
  187. };
  188. /** @private **/
  189. Query.prototype._maxFetch = 10000;
  190. /**
  191. * Setting maxFetch query option
  192. *
  193. * @param {Number} maxFetch - Max fetching records in auto fetch mode
  194. * @returns {Query.<T>}
  195. */
  196. Query.prototype.maxFetch = function(maxFetch) {
  197. this._maxFetch = maxFetch;
  198. return this;
  199. };
  200. /** @private **/
  201. Query.prototype._autoFetch = false;
  202. /**
  203. * Switching auto fetch mode
  204. *
  205. * @param {Boolean} autoFetch - Using auto fetch mode or not
  206. * @returns {Query.<T>}
  207. */
  208. Query.prototype.autoFetch = function(autoFetch) {
  209. this._autoFetch = autoFetch;
  210. return this;
  211. };
  212. /** @private **/
  213. Query.prototype._scanAll = false;
  214. /**
  215. * Set flag to scan all records including deleted and archived.
  216. *
  217. * @param {Boolean} scanAll - Flag whether include deleted/archived record or not. Default is false.
  218. * @returns {Query.<T>}
  219. */
  220. Query.prototype.scanAll = function(scanAll) {
  221. this._scanAll = scanAll;
  222. return this;
  223. };
  224. /**
  225. * @private
  226. */
  227. var ResponseTargets = Query.ResponseTargets = {};
  228. [ "QueryResult", "Records", "SingleRecord", "Count" ].forEach(function(f) {
  229. ResponseTargets[f] = f;
  230. });
  231. /** @private **/
  232. Query.prototype._responseTarget = ResponseTargets.QueryResult;
  233. /**
  234. * @protected
  235. * @param {String} responseTarget - Query response target
  236. * @returns {Query.<S>}
  237. */
  238. Query.prototype.setResponseTarget = function(responseTarget) {
  239. if (responseTarget in ResponseTargets) {
  240. this._responseTarget = responseTarget;
  241. }
  242. return this;
  243. };
  244. /**
  245. * Synonym of Query#execute()
  246. *
  247. * @method Query#run
  248. * @param {Object} [options] - Query options
  249. * @param {Boolean} [options.autoFetch] - Using auto fetch mode or not
  250. * @param {Number} [options.maxFetch] - Max fetching records in auto fetch mode
  251. * @param {Boolean} [options.scanAll] - Including deleted records for query target or not
  252. * @param {Callback.<T>} [callback] - Callback function
  253. * @returns {Query.<T>}
  254. */
  255. Query.prototype.run =
  256. /**
  257. * Synonym of Query#execute()
  258. *
  259. * @method Query#exec
  260. * @param {Object} [options] - Query options
  261. * @param {Boolean} [options.autoFetch] - Using auto fetch mode or not
  262. * @param {Number} [options.maxFetch] - Max fetching records in auto fetch mode
  263. * @param {Boolean} [options.scanAll] - Including deleted records for query target or not
  264. * @param {Callback.<T>} [callback] - Callback function
  265. * @returns {Query.<T>}
  266. */
  267. Query.prototype.exec =
  268. /**
  269. * Execute query and fetch records from server.
  270. *
  271. * @method Query#execute
  272. * @param {Object} [options] - Query options
  273. * @param {Boolean} [options.autoFetch] - Using auto fetch mode or not
  274. * @param {Number} [options.maxFetch] - Max fetching records in auto fetch mode
  275. * @param {Boolean} [options.scanAll] - Including deleted records for query target or not
  276. * @param {Callback.<T>} [callback] - Callback function
  277. * @returns {Query.<T>}
  278. */
  279. Query.prototype.execute = function(options, callback) {
  280. var self = this;
  281. var logger = this._conn._logger;
  282. var deferred = this._deferred;
  283. if (this._executed) {
  284. deferred.reject(new Error("re-executing already executed query"));
  285. return this;
  286. }
  287. if (this._finished) {
  288. deferred.reject(new Error("executing already closed query"));
  289. return this;
  290. }
  291. if (typeof options === "function") {
  292. callback = options;
  293. options = {};
  294. }
  295. options = options || {};
  296. options = {
  297. responseTarget: options.responseTarget || self._responseTarget,
  298. autoFetch: options.autoFetch || self._autoFetch,
  299. maxFetch: options.maxFetch || self._maxFetch,
  300. scanAll: options.scanAll || self._scanAll
  301. };
  302. // callback and promise resolution;
  303. var promiseCallback = function(err, res) {
  304. if (_.isFunction(callback)) {
  305. try {
  306. res = callback(err, res);
  307. err = null;
  308. } catch(e) {
  309. err = e;
  310. }
  311. }
  312. if (err) {
  313. deferred.reject(err);
  314. } else {
  315. deferred.resolve(res);
  316. }
  317. };
  318. this.once('response', function(res) {
  319. promiseCallback(null, res);
  320. });
  321. this.once('error', function(err) {
  322. promiseCallback(err);
  323. });
  324. // collect fetched records in array
  325. // only when response target is Records and
  326. // either callback or chaining promises are available to this query.
  327. this.once('fetch', function() {
  328. if (options.responseTarget === ResponseTargets.Records && (self._chaining || callback)) {
  329. logger.debug('--- collecting all fetched records ---');
  330. var records = [];
  331. var onRecord = function(record) {
  332. records.push(record);
  333. };
  334. self.on('record', onRecord);
  335. self.once('end', function() {
  336. self.removeListener('record', onRecord);
  337. self.emit('response', records, self);
  338. });
  339. }
  340. });
  341. // flag to prevent re-execution
  342. this._executed = true;
  343. // start actual query
  344. logger.debug('>>> Query start >>>');
  345. this._execute(options).then(function() {
  346. logger.debug('*** Query finished ***');
  347. }).fail(function(err) {
  348. logger.debug('--- Query error ---');
  349. self.emit('error', err);
  350. });
  351. // return Query instance for chaining
  352. return this;
  353. };
  354. /**
  355. * @private
  356. */
  357. Query.prototype._execute = function(options) {
  358. var self = this;
  359. var logger = this._conn._logger;
  360. var responseTarget = options.responseTarget;
  361. var autoFetch = options.autoFetch;
  362. var maxFetch = options.maxFetch;
  363. var scanAll = options.scanAll;
  364. return Promise.resolve(
  365. self._locator ?
  366. self._conn._baseUrl() + "/query/" + self._locator :
  367. self.toSOQL().then(function(soql) {
  368. self.totalFetched = 0;
  369. logger.debug("SOQL = " + soql);
  370. return self._conn._baseUrl() + "/" + (scanAll ? "queryAll" : "query") + "?q=" + encodeURIComponent(soql);
  371. })
  372. ).then(function(url) {
  373. return self._conn.request(url);
  374. }).then(function(data) {
  375. self.emit("fetch");
  376. self.totalSize = data.totalSize;
  377. var res;
  378. switch(responseTarget) {
  379. case ResponseTargets.SingleRecord:
  380. res = data.records && data.records.length > 0 ? data.records[0] : null;
  381. break;
  382. case ResponseTargets.Records:
  383. res = data.records;
  384. break;
  385. case ResponseTargets.Count:
  386. res = data.totalSize;
  387. break;
  388. default:
  389. res = data;
  390. }
  391. // only fire response event when it should be notified per fetch
  392. if (responseTarget !== ResponseTargets.Records) {
  393. self.emit("response", res, self);
  394. }
  395. // streaming record instances
  396. for (var i=0, l=data.records.length; i<l; i++) {
  397. if (self.totalFetched >= maxFetch) {
  398. self._finished = true;
  399. break;
  400. }
  401. var record = data.records[i];
  402. self.push(record);
  403. self.emit('record', record, self.totalFetched++, self);
  404. }
  405. if (data.nextRecordsUrl) {
  406. self._locator = data.nextRecordsUrl.split('/').pop();
  407. }
  408. self._finished = self._finished || data.done || !autoFetch;
  409. if (self._finished) {
  410. self.push(null);
  411. } else {
  412. self._execute(options);
  413. }
  414. return res;
  415. });
  416. };
  417. /**
  418. * Readable stream implementation
  419. *
  420. * @override
  421. * @private
  422. */
  423. Query.prototype._read = function(size) {
  424. if (!this._finished && !this._executed) {
  425. this.execute({ autoFetch: true });
  426. }
  427. };
  428. /** @override **/
  429. Query.prototype.on = function(e, fn) {
  430. if (e === 'record') {
  431. var self = this;
  432. this.on('readable', function() {
  433. while(self.read() !== null) {} // discard buffered records
  434. });
  435. }
  436. return Query.super_.prototype.on.call(this, e, fn);
  437. };
  438. /** @override **/
  439. Query.prototype.addListener = Query.prototype.on;
  440. /**
  441. * @private
  442. */
  443. Query.prototype._expandFields = function() {
  444. if (this._soql) {
  445. return Promise.reject(new Error("Cannot expand fields for the query which has already built SOQL."));
  446. }
  447. var self = this;
  448. var logger = self._conn._logger;
  449. var conn = this._conn;
  450. var table = this._config.table;
  451. var fields = this._config.fields || [];
  452. logger.debug('_expandFields: table = ' + table + ', fields = ' + fields.join(', '));
  453. return Promise.all([
  454. Promise.resolve(self._parent ? findRelationTable(table) : table)
  455. .then(function(table) {
  456. return Promise.all(
  457. _.map(fields, function(field) { return expandAsteriskField(table, field); })
  458. ).then(function(expandedFields) {
  459. self._config.fields = _.flatten(expandedFields);
  460. });
  461. }),
  462. Promise.all(
  463. _.map(self._children || [], function(childQuery) {
  464. return childQuery._expandFields();
  465. })
  466. )
  467. ]);
  468. function findRelationTable(rname) {
  469. var ptable = self._parent._config.table;
  470. logger.debug('finding table for relation "' + rname + '" in "' + ptable + '"...');
  471. return describeCache(ptable).then(function(sobject) {
  472. var upperRname = rname.toUpperCase();
  473. var childRelation = _.find(sobject.childRelationships, function(cr) {
  474. return (cr.relationshipName || '').toUpperCase() === upperRname;
  475. });
  476. return childRelation ? childRelation.childSObject :
  477. Promise.reject(new Error("No child relationship found: " + rname ));
  478. });
  479. }
  480. function describeCache(table) {
  481. logger.debug('describe cache: '+table);
  482. var deferred = Promise.defer();
  483. conn.describe$(table, function(err, sobject) {
  484. logger.debug('... done.');
  485. if (err) { deferred.reject(err); }
  486. else { deferred.resolve(sobject); }
  487. });
  488. return deferred.promise;
  489. }
  490. function expandAsteriskField(table, field) {
  491. logger.debug('expanding field "'+ field + '" in "' + table + '"...');
  492. var fpath = field.split('.');
  493. return fpath[fpath.length - 1] === '*' ?
  494. describeCache(table).then(function(sobject) {
  495. logger.debug('table '+table+'has been described');
  496. if (fpath.length > 1) {
  497. var rname = fpath.shift();
  498. var rfield = _.find(sobject.fields, function(f) {
  499. return f.relationshipName &&
  500. f.relationshipName.toUpperCase() === rname.toUpperCase();
  501. });
  502. if (rfield) {
  503. var rtable = rfield.referenceTo.length === 1 ? rfield.referenceTo[0] : 'Name';
  504. return expandAsteriskField(rtable, fpath.join('.')).then(function(fpaths) {
  505. return _.map(fpaths, function(fpath) { return rname + '.' + fpath; });
  506. });
  507. } else {
  508. return [];
  509. }
  510. } else {
  511. return _.map(sobject.fields, function(f) { return f.name; });
  512. }
  513. }) :
  514. Promise.resolve([ field ]);
  515. }
  516. };
  517. /**
  518. * Explain plan for executing query
  519. *
  520. * @param {Callback.<ExplainInfo>} [callback] - Callback function
  521. * @returns {Promise.<ExplainInfo>}
  522. */
  523. Query.prototype.explain = function(callback) {
  524. var self = this;
  525. var logger = this._conn._logger;
  526. return self.toSOQL().then(function(soql) {
  527. logger.debug("SOQL = " + soql);
  528. var url = "/query/?explain=" + encodeURIComponent(soql);
  529. return self._conn.request(url);
  530. }).thenCall(callback);
  531. };
  532. /**
  533. * Return SOQL expression for the query
  534. *
  535. * @param {Callback.<String>} [callback] - Callback function
  536. * @returns {Promise.<String>}
  537. */
  538. Query.prototype.toSOQL = function(callback) {
  539. var self = this;
  540. return Promise.resolve(self._soql ||
  541. self._expandFields().then(function() { return SOQLBuilder.createSOQL(self._config); })
  542. ).thenCall(callback);
  543. };
  544. /**
  545. * Create data stream of queried records.
  546. * Automatically resume query if paused.
  547. *
  548. * @param {String} [type] - Type of outgoing data format. Currently 'csv' is default value and the only supported.
  549. * @param {Object} [options] - Options passed to converter
  550. * @returns {stream.Readable}
  551. */
  552. Query.prototype.stream = RecordStream.Serializable.prototype.stream;
  553. /**
  554. * Get record stream of queried records applying the given mapping function
  555. *
  556. * @param {RecordMapFunction} fn - Record mapping function
  557. * @returns {RecordStream.Serializable}
  558. */
  559. Query.prototype.map = RecordStream.prototype.map;
  560. /**
  561. * Get record stream of queried records, applying the given filter function
  562. *
  563. * @param {RecordFilterFunction} fn - Record filtering function
  564. * @returns {RecordStream.Serializable}
  565. */
  566. Query.prototype.filter = RecordStream.prototype.map;
  567. /**
  568. * Synonym of Query#destroy()
  569. *
  570. * @method Query#delete
  571. * @param {String} [type] - SObject type. Required for SOQL based query object.
  572. * @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
  573. * @returns {Bulk~Batch}
  574. */
  575. /**
  576. * Synonym of Query#destroy()
  577. *
  578. * @method Query#del
  579. * @param {String} [type] - SObject type. Required for SOQL based query object.
  580. * @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
  581. * @returns {Bulk~Batch}
  582. */
  583. /**
  584. * Bulk delete queried records
  585. *
  586. * @method Query#destroy
  587. * @param {String} [type] - SObject type. Required for SOQL based query object.
  588. * @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
  589. * @returns {Promise.<Array.<RecordResult>>}
  590. */
  591. Query.prototype["delete"] =
  592. Query.prototype.del =
  593. Query.prototype.destroy = function(type, callback) {
  594. if (typeof type === 'function') {
  595. callback = type;
  596. type = null;
  597. }
  598. type = type || (this._config && this._config.table);
  599. if (!type) {
  600. throw new Error("SOQL based query needs SObject type information to bulk delete.");
  601. }
  602. var batch = this._conn.sobject(type).deleteBulk();
  603. var deferred = Promise.defer();
  604. var handleError = function(err) {
  605. if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records
  606. else { deferred.reject(err); }
  607. };
  608. this.on('error', handleError)
  609. .pipe(batch)
  610. .on('response', function(res) { deferred.resolve(res); })
  611. .on('error', handleError);
  612. return deferred.promise.thenCall(callback);
  613. };
  614. /**
  615. * Bulk update queried records, using given mapping function/object
  616. *
  617. * @param {Record|RecordMapFunction} mapping - Mapping record or record mapping function
  618. * @param {String} [type] - SObject type. Required for SOQL based query object.
  619. * @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
  620. * @returns {Promise.<Array.<RecordResult>>}
  621. */
  622. Query.prototype.update = function(mapping, type, callback) {
  623. if (typeof type === 'function') {
  624. callback = type;
  625. type = null;
  626. }
  627. type = type || (this._config && this._config.table);
  628. if (!type) {
  629. throw new Error("SOQL based query needs SObject type information to bulk update.");
  630. }
  631. var updateStream = _.isFunction(mapping) ? RecordStream.map(mapping) : RecordStream.recordMapStream(mapping);
  632. var batch = this._conn.sobject(type).updateBulk();
  633. var deferred = Promise.defer();
  634. var handleError = function(err) {
  635. if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records
  636. else { deferred.reject(err); }
  637. };
  638. this.on('error', handleError)
  639. .pipe(updateStream)
  640. .on('error', handleError)
  641. .pipe(batch)
  642. .on('response', function(res) { deferred.resolve(res); })
  643. .on('error', handleError);
  644. return deferred.promise.thenCall(callback);
  645. };
  646. /**
  647. * Promise/A+ interface
  648. * http://promises-aplus.github.io/promises-spec/
  649. *
  650. * Delegate to deferred promise, return promise instance for query result
  651. *
  652. * @param {FulfilledCallback.<T, S1>} [onFulfilled]
  653. * @param {RejectedCallback.<S2>} [onRejected]
  654. * @returns {Promise.<S1|S2>}
  655. */
  656. Query.prototype.then = function(onResolved, onReject) {
  657. this._chaining = true;
  658. if (!this._finished && !this._executed) { this.execute(); }
  659. return this._deferred.promise.then.apply(this._deferred.promise, arguments);
  660. };
  661. /**
  662. * Promise/A+ extension
  663. * Call "then" using given node-style callback function
  664. *
  665. * @param {Callback.<T>} [callback] - Callback function
  666. * @returns {Query}
  667. */
  668. Query.prototype.thenCall = function(callback) {
  669. if (_.isFunction(callback)) {
  670. this.then(function(res) {
  671. process.nextTick(function() {
  672. callback(null, res);
  673. });
  674. }, function(err) {
  675. process.nextTick(function() {
  676. callback(err);
  677. });
  678. });
  679. }
  680. return this;
  681. };
  682. /*--------------------------------------------*/
  683. /**
  684. * SubQuery object for representing child relationship query
  685. *
  686. * @protected
  687. * @class Query~SubQuery
  688. * @extends Query
  689. * @param {Connection} conn - Connection object
  690. * @param {Query} parent - Parent query object
  691. * @param {Object} config - Sub query configuration
  692. */
  693. var SubQuery = function(conn, parent, config) {
  694. SubQuery.super_.call(this, conn, config);
  695. this._parent = parent;
  696. };
  697. inherits(SubQuery, Query);
  698. /**
  699. * @method Query~SubQuery#include
  700. * @override
  701. */
  702. SubQuery.prototype.include = function() {
  703. throw new Error("Not allowed to include another subquery in subquery.");
  704. };
  705. /**
  706. * Back the context to parent query object
  707. *
  708. * @method Query~SubQuery#end
  709. * @returns {Query}
  710. */
  711. SubQuery.prototype.end = function() {
  712. return this._parent;
  713. };
  714. /**
  715. * If execute is called in subquery context, delegate it to parent query object
  716. *
  717. * @method Query~SubQuery#execute
  718. * @override
  719. */
  720. SubQuery.prototype.run =
  721. SubQuery.prototype.exec =
  722. SubQuery.prototype.execute = function() {
  723. return this._parent.execute.apply(this._parent, arguments);
  724. };