diff --git a/History.md b/History.md index ba6c1571..65b9bcec 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,11 @@ +#0.1.1 / 2012-08-29 +* new patio.Dataset features + * #sourceList - get all sources as identifiers + * #joinSourceList - get all join sources + * #hasSelectSource - returns true if there are not any select sources (i.e select *) + * #seleIfNoSource - add the selects if there is not currently a select +* Fixed issue with patio.Model#_setFromDb where values not in the models table columns would be in accesible, (i.e a join with a model would not show the join columns) +* updated docs #0.1.0 / 2012-08-25 * Added custom getters (mbenedettini) * Added Validator plugin for models diff --git a/docs-md/coverage.html b/docs-md/coverage.html index c6344a66..21fb1ef8 100644 --- a/docs-md/coverage.html +++ b/docs-md/coverage.html @@ -256,10 +256,10 @@
- Coverage89.22 - SLOC21912 - LOC5351 - Missed577 + Coverage89.04 + SLOC21962 + LOC5365 + Missed588
@@ -943,10 +943,10 @@
- Coverage72.53 - SLOC1076 - LOC91 - Missed25 + Coverage71.74 + SLOC1078 + LOC92 + Missed26
@@ -1682,8 +1682,10 @@
  • * @param opts
  • */
  • configureLogging:function (opts) {
  • -
  • 0 comb.logger.configure();
  • -
  • 0 LOGGER.level = "info";
  • +
  • 0 comb.logger.configure(opts);
  • +
  • 0 if (!opts) {
  • +
  • 0 LOGGER.level = "info";
  • +
  • }
  • },
  • /**
  • @@ -3743,13 +3745,13 @@
  • * @return the typecasted value.
  • * */
  • typecastValue:function (columnType, value) {
  • -
  • 32725 if (isNull(value) || isUndefined(value)) {
  • +
  • 32726 if (isNull(value) || isUndefined(value)) {
  • 5793 return null;
  • }
  • -
  • 26932 var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
  • -
  • 26932 try {
  • -
  • 26932 if (isFunction(this[meth])) {
  • -
  • 26932 return this[meth](value);
  • +
  • 26933 var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
  • +
  • 26933 try {
  • +
  • 26933 if (isFunction(this[meth])) {
  • +
  • 26933 return this[meth](value);
  • } else {
  • 0 return value;
  • }
  • @@ -3915,7 +3917,7 @@
  • // Typecast the value to a String
  • __typecastValueString:function (value) {
  • -
  • 19607 return "" + value;
  • +
  • 19608 return "" + value;
  • }
  • },
  • @@ -5733,253 +5735,728 @@
  • -
    +
    -
    associations/_Association.js
    +
    dataset/index.js
    - Coverage87.18 - SLOC515 - LOC156 - Missed20 + Coverage86.11 + SLOC457 + LOC72 + Missed10
    -
    1. 1var comb = require("comb-proxy"),
    2. -
    3. define = comb.define,
    4. +
      1. 1var comb = require("comb"),
      2. +
      3. hitch = comb.hitch,
      4. +
      5. logging = comb.logging,
      6. +
      7. Logger = logging.Logger,
      8. +
      9. errors = require("../errors"),
      10. +
      11. QueryError = errors.QueryError,
      12. +
      13. DatasetError = errors.DatasetError,
      14. +
      15. Promise = comb.Promise,
      16. +
      17. PromiseList = comb.PromiseList,
      18. isUndefined = comb.isUndefined,
      19. isUndefinedOrNull = comb.isUndefinedOrNull,
      20. -
      21. isBoolean = comb.isBoolean,
      22. isString = comb.isString,
      23. -
      24. isHash = comb.isHash,
      25. -
      26. isFunction = comb.isFunction,
      27. isInstanceOf = comb.isInstanceOf,
      28. -
      29. Promise = comb.Promise,
      30. -
      31. PromiseList = comb.PromiseList,
      32. -
      33. hitch = comb.hitch,
      34. -
      35. array = comb.array,
      36. -
      37. isArray = comb.isArray,
      38. -
      39. Middleware = comb.plugins.Middleware,
      40. -
      41. PatioError = require("../errors").PatioError;
      42. -
      43. -
      44. 1var fetch = {
      45. -
      46. LAZY:"lazy",
      47. -
      48. EAGER:"eager"
      49. -
      50. };
      51. +
      52. isString = comb.isString,
      53. +
      54. isFunction = comb.isFunction,
      55. +
      56. isNull = comb.isNull,
      57. +
      58. merge = comb.merge,
      59. +
      60. define = comb.define,
      61. +
      62. graph = require("./graph"),
      63. +
      64. actions = require("./actions"),
      65. +
      66. features = require("./features"),
      67. +
      68. query = require("./query"),
      69. +
      70. sql = require("./sql"),
      71. +
      72. SQL = require("../sql").sql,
      73. +
      74. AliasedExpression = SQL.AliasedExpression,
      75. +
      76. Identifier = SQL.Identifier,
      77. +
      78. QualifiedIdentifier = SQL.QualifiedIdentifier;
      79. -
      80. /**
      81. -
      82. * @class
      83. -
      84. * Base class for all associations.
      85. -
      86. *
      87. -
      88. * </br>
      89. -
      90. * <b>NOT to be instantiated directly</b>
      91. -
      92. * Its just documented for reference.
      93. -
      94. *
      95. -
      96. * @constructs
      97. -
      98. * @param {Object} options
      99. -
      100. * @param {String} options.model a string to look up the model that we are associated with
      101. -
      102. * @param {Function} options.filter a callback to find association if a filter is defined then
      103. -
      104. * the association is read only
      105. -
      106. * @param {Object} options.key object with left key and right key
      107. -
      108. * @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
      109. -
      110. * @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
      111. -
      112. * the associations are automatically filled, if fetch.Lazy is supplied
      113. -
      114. * then a promise is returned and is called back with the loaded models
      115. -
      116. * @property {Model} model the model associatied with this association.
      117. -
      118. * @name Association
      119. -
      120. * @memberOf patio.associations
      121. -
      122. * */
      123. -
      124. 1define(Middleware, {
      125. +
      126. 1var LOGGER = comb.logger("patio.Dataset");
      127. +
      128. 1define([actions, graph, features, query, sql], {
      129. instance:{
      130. -
      131. /**@lends patio.associations.Association.prototype*/
      132. -
      133. -
      134. type:"",
      135. -
      136. -
      137. //Our associated model
      138. -
      139. _model:null,
      140. -
      141. -
      142. /**
      143. -
      144. * Fetch type
      145. -
      146. */
      147. -
      148. fetchType:fetch.LAZY,
      149. -
      150. -
      151. /**how to order our association*/
      152. -
      153. orderBy:null,
      154. -
      155. -
      156. /**Our filter method*/
      157. -
      158. filter:null,
      159. -
      160. -
      161. __hooks:null,
      162. -
      163. -
      164. isOwner:true,
      165. -
      166. -
      167. createSetter:true,
      168. -
      169. -
      170. isCascading:false,
      171. -
      172. -
      173. supportsStringKey:true,
      174. -
      175. -
      176. supportsHashKey:true,
      177. -
      178. -
      179. supportsCompositeKey:true,
      180. -
      181. supportsLeftAndRightKey:true,
      182. +
      183. /**@lends patio.Dataset.prototype*/
      184. /**
      185. +
      186. * Class that is used for querying/retirving datasets from a database.
      187. *
      188. -
      189. *Method to call to look up association,
      190. -
      191. *called after the model has been filtered
      192. -
      193. **/
      194. -
      195. _fetchMethod:"all",
      196. -
      197. -
      198. -
      199. constructor:function (options, patio, filter) {
      200. -
      201. 55 options = options || {};
      202. -
      203. 55 if (!options.model) {
      204. -
      205. 0 throw new Error("Model is required for " + this.type + " association");
      206. -
      207. }
      208. -
      209. 55 this._model = options.model;
      210. -
      211. 55 this.patio = patio;
      212. -
      213. 55 this.__opts = options;
      214. -
      215. 55 !isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
      216. -
      217. 55 this.filter = filter;
      218. -
      219. 55 this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
      220. -
      221. 55 this.__hooks =
      222. -
      223. {before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
      224. -
      225. 55 var hooks = ["Add", "Remove", "Set", "Load"];
      226. -
      227. 55 ["before", "after"].forEach(function (h) {
      228. -
      229. 110 hooks.forEach(function (a) {
      230. -
      231. 440 var hookName = h + a, hook;
      232. -
      233. 440 if (isFunction((hook = options[hookName]))) {
      234. -
      235. 0 this.__hooks[h][a.toLowerCase()] = hook;
      236. -
      237. }
      238. -
      239. }, this);
      240. -
      241. }, this);
      242. -
      243. 55 this.fetchType = options.fetchType || fetch.LAZY;
      244. -
      245. },
      246. -
      247. -
      248. _callHook:function (hook, action, args) {
      249. -
      250. 0 var func = this.__hooks[hook][action], ret;
      251. -
      252. 0 if (isFunction(func)) {
      253. -
      254. 0 ret = func.apply(this, args);
      255. -
      256. }
      257. -
      258. 0 return ret;
      259. -
      260. },
      261. -
      262. -
      263. _clearAssociations:function (model) {
      264. -
      265. 125 if (!this.readOnly) {
      266. -
      267. 125 delete model.__associations[this.name];
      268. -
      269. }
      270. -
      271. },
      272. -
      273. -
      274. _forceReloadAssociations:function (model) {
      275. -
      276. 243 if (!this.readOnly) {
      277. -
      278. 243 delete model.__associations[this.name];
      279. -
      280. 243 return model[this.name];
      281. -
      282. }
      283. -
      284. },
      285. -
      286. -
      287. /**
      288. -
      289. * @return {Boolean} true if the association is eager.
      290. -
      291. */
      292. -
      293. isEager:function () {
      294. -
      295. 2114 return this.fetchType == fetch.EAGER;
      296. -
      297. },
      298. -
      299. -
      300. _checkAssociationKey:function (parent) {
      301. -
      302. 785 var q = {};
      303. -
      304. 785 this._setAssociationKeys(parent, q);
      305. -
      306. 785 return Object.keys(q).every(function (k) {
      307. -
      308. 785 return q[k] != null
      309. -
      310. });
      311. -
      312. },
      313. -
      314. -
      315. _getAssociationKey:function () {
      316. -
      317. 5205 var options = this.__opts, key, ret = [], lk, rk;
      318. -
      319. 5205 if (!isUndefinedOrNull((key = options.key))) {
      320. -
      321. 722 if (this.supportsStringKey && isString(key)) {
      322. -
      323. //normalize the key first!
      324. -
      325. 356 ret = [
      326. -
      327. [this.isOwner ? this.defaultLeftKey : key],
      328. -
      329. [this.isOwner ? key : this.defaultRightKey]
      330. -
      331. ];
      332. -
      333. 366 } else if (this.supportsHashKey && isHash(key)) {
      334. -
      335. 366 var leftKey = Object.keys(key)[0];
      336. -
      337. 366 var rightKey = key[leftKey];
      338. -
      339. 366 ret = [
      340. -
      341. [leftKey],
      342. -
      343. [rightKey]
      344. -
      345. ];
      346. -
      347. 0 } else if (this.supportsCompositeKey && isArray(key)) {
      348. -
      349. 0 ret = [
      350. -
      351. [key],
      352. -
      353. null
      354. -
      355. ];
      356. -
      357. }
      358. -
      359. 4483 } else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
      360. -
      361. && !isUndefinedOrNull((rk = options.rightKey)))) {
      362. -
      363. 140 ret = [
      364. -
      365. array.toArray(lk), array.toArray(rk)
      366. -
      367. ];
      368. -
      369. } else {
      370. -
      371. //todo handle composite primary keys
      372. -
      373. 4343 ret = [
      374. -
      375. [this.defaultLeftKey],
      376. -
      377. [this.defaultRightKey]
      378. -
      379. ];
      380. -
      381. }
      382. -
      383. 5205 return ret;
      384. -
      385. },
      386. -
      387. -
      388. -
      389. _setAssociationKeys:function (parent, model, val) {
      390. -
      391. 1573 var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
      392. -
      393. 1573 if (leftKey && rightKey) {
      394. -
      395. 1573 for (var i = 0; i < leftKey.length; i++) {
      396. -
      397. 1573 model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
      398. -
      399. }
      400. -
      401. } else {
      402. -
      403. 0 leftKey.forEach(function (k) {
      404. -
      405. 0 model[k] = !isUndefined(val) ? val : parent[k];
      406. -
      407. });
      408. -
      409. }
      410. -
      411. },
      412. -
      413. -
      414. _setDatasetOptions:function (ds) {
      415. -
      416. 974 var options = this.__opts || {};
      417. -
      418. 974 var order, limit, distinct, select, query;
      419. -
      420. //allow for multi key ordering
      421. -
      422. 974 if (!isUndefined((select = this.select))) {
      423. -
      424. 0 ds = ds.select.apply(ds, array.toArray(select));
      425. -
      426. }
      427. -
      428. 974 if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
      429. -
      430. 0 ds = ds.filter(query);
      431. -
      432. }
      433. -
      434. 974 if (isFunction(this.filter)) {
      435. -
      436. 334 var ret = this.filter.apply(this, [ds]);
      437. -
      438. 334 if (isInstanceOf(ret, ds._static)) {
      439. -
      440. 334 ds = ret;
      441. -
      442. }
      443. -
      444. }
      445. -
      446. 974 if (!isUndefined((distinct = options.distinct))) {
      447. -
      448. 0 ds = ds.limit.apply(ds, array.toArray(distinct));
      449. -
      450. }
      451. -
      452. 974 if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
      453. -
      454. 0 ds = ds.order.apply(ds, array.toArray(order));
      455. -
      456. }
      457. -
      458. 974 if (!isUndefined((limit = options.limit))) {
      459. -
      460. 0 ds = ds.limit.apply(ds, array.toArray(limit));
      461. -
      462. }
      463. -
      464. 974 return ds;
      465. -
      466. -
      467. },
      468. -
      469. -
      470. /**
      471. +
      472. * <p> Dynamically genertated methods include
      473. +
      474. * <ul>
      475. +
      476. * <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
      477. +
      478. * {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
      479. +
      480. * to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
      481. +
      482. * {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
      483. +
      484. * <ul>
      485. +
      486. * <li>Conditioned join types that accept conditions.
      487. +
      488. * <ul>
      489. +
      490. * <li>inner - INNER JOIN</li>
      491. +
      492. * <li>fullOuter - FULL OUTER</li>
      493. +
      494. * <li>rightOuter - RIGHT OUTER JOIN</li>
      495. +
      496. * <li>leftOuter - LEFT OUTER JOIN</li>
      497. +
      498. * <li>full - FULL JOIN</li>
      499. +
      500. * <li>right - RIGHT JOIN</li>
      501. +
      502. * <li>left - LEFT JOIN</li>
      503. +
      504. * </ul>
      505. +
      506. * </li>
      507. +
      508. * <li>Unconditioned join types that do not accept join conditions
      509. +
      510. * <ul>
      511. +
      512. * <li>natural - NATURAL JOIN</li>
      513. +
      514. * <li>naturalLeft - NATURAL LEFT JOIN</li>
      515. +
      516. * <li>naturalRight - NATURAL RIGHT JOIN</li>
      517. +
      518. * <li>naturalFull - NATURA FULLL JOIN</li>
      519. +
      520. * <li>cross - CROSS JOIN</li>
      521. +
      522. * </ul>
      523. +
      524. * </li>
      525. +
      526. * </ul>
      527. +
      528. * </li>
      529. +
      530. * </li>
      531. +
      532. * </ul>
      533. +
      534. *
      535. +
      536. * <p>
      537. +
      538. * <h4>Features:</h4>
      539. +
      540. * <p>
      541. +
      542. * Features that a particular {@link patio.Dataset} supports are shown in the example below.
      543. +
      544. * If you wish to implement an adapter please override these values depending on the database that
      545. +
      546. * you are developing the adapter for.
      547. +
      548. * </p>
      549. +
      550. * <pre class="code">
      551. +
      552. * var ds = DB.from("test");
      553. +
      554. *
      555. +
      556. * //The default values returned
      557. +
      558. *
      559. +
      560. * //Whether this dataset quotes identifiers.
      561. +
      562. * //Whether this dataset quotes identifiers.
      563. +
      564. * ds.quoteIdentifiers //=>true
      565. +
      566. *
      567. +
      568. * //Whether this dataset will provide accurate number of rows matched for
      569. +
      570. * //delete and update statements. Accurate in this case is the number of
      571. +
      572. * //rows matched by the dataset's filter.
      573. +
      574. * ds.providesAccurateRowsMatched; //=>true
      575. +
      576. *
      577. +
      578. * //Times Whether the dataset requires SQL standard datetimes (false by default,
      579. +
      580. * // as most allow strings with ISO 8601 format).
      581. +
      582. * ds.requiresSqlStandardDate; //=>false
      583. +
      584. *
      585. +
      586. * //Whether the dataset supports common table expressions (the WITH clause).
      587. +
      588. * ds.supportsCte; //=>true
      589. +
      590. *
      591. +
      592. * //Whether the dataset supports the DISTINCT ON clause, false by default.
      593. +
      594. * ds.supportsDistinctOn; //=>false
      595. +
      596. *
      597. +
      598. * //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      599. +
      600. * ds.supportsIntersectExcept; //=>true
      601. +
      602. *
      603. +
      604. * //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
      605. +
      606. * ds.supportsIntersectExceptAll; //=>true
      607. +
      608. *
      609. +
      610. * //Whether the dataset supports the IS TRUE syntax.
      611. +
      612. * ds.supportsIsTrue; //=>true
      613. +
      614. *
      615. +
      616. * //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      617. +
      618. * ds.supportsJoinUsing; //=>true
      619. +
      620. *
      621. +
      622. * //Whether modifying joined datasets is supported.
      623. +
      624. * ds.supportsModifyingJoin; //=>false
      625. +
      626. *
      627. +
      628. * //Whether the IN/NOT IN operators support multiple columns when an
      629. +
      630. * ds.supportsMultipleColumnIn; //=>true
      631. +
      632. *
      633. +
      634. * //Whether the dataset supports timezones in literal timestamps
      635. +
      636. * ds.supportsTimestampTimezone; //=>false
      637. +
      638. *
      639. +
      640. * //Whether the dataset supports fractional seconds in literal timestamps
      641. +
      642. * ds.supportsTimestampUsecs; //=>true
      643. +
      644. *
      645. +
      646. * //Whether the dataset supports window functions.
      647. +
      648. * ds.supportsWindowFunctions; //=>false
      649. +
      650. * </pre>
      651. +
      652. * <p>
      653. +
      654. * <p>
      655. +
      656. * <h4>Actions</h4>
      657. +
      658. * <p>
      659. +
      660. * Each dataset does not actually send any query to the database until an action method has
      661. +
      662. * been called upon it(with the exception of {@link patio.Dataset#graph} because columns
      663. +
      664. * from the other table might need retrived in order to set up the graph). Each action
      665. +
      666. * returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
      667. +
      668. * that you account for errors otherwise it can be difficult to track down issues.
      669. +
      670. * The list of action methods is:
      671. +
      672. * <ul>
      673. +
      674. * <li>{@link patio.Dataset#all}</li>
      675. +
      676. * <li>{@link patio.Dataset#one}</li>
      677. +
      678. * <li>{@link patio.Dataset#avg}</li>
      679. +
      680. * <li>{@link patio.Dataset#count}</li>
      681. +
      682. * <li>{@link patio.Dataset#columns}</li>
      683. +
      684. * <li>{@link patio.Dataset#remove}</li>
      685. +
      686. * <li>{@link patio.Dataset#forEach}</li>
      687. +
      688. * <li>{@link patio.Dataset#empty}</li>
      689. +
      690. * <li>{@link patio.Dataset#first}</li>
      691. +
      692. * <li>{@link patio.Dataset#get}</li>
      693. +
      694. * <li>{@link patio.Dataset#import}</li>
      695. +
      696. * <li>{@link patio.Dataset#insert}</li>
      697. +
      698. * <li>{@link patio.Dataset#save}</li>
      699. +
      700. * <li>{@link patio.Dataset#insertMultiple}</li>
      701. +
      702. * <li>{@link patio.Dataset#saveMultiple}</li>
      703. +
      704. * <li>{@link patio.Dataset#interval}</li>
      705. +
      706. * <li>{@link patio.Dataset#last}</li>
      707. +
      708. * <li>{@link patio.Dataset#map}</li>
      709. +
      710. * <li>{@link patio.Dataset#max}</li>
      711. +
      712. * <li>{@link patio.Dataset#min}</li>
      713. +
      714. * <li>{@link patio.Dataset#multiInsert}</li>
      715. +
      716. * <li>{@link patio.Dataset#range}</li>
      717. +
      718. * <li>{@link patio.Dataset#selectHash}</li>
      719. +
      720. * <li>{@link patio.Dataset#selectMap}</li>
      721. +
      722. * <li>{@link patio.Dataset#selectOrderMap}</li>
      723. +
      724. * <li>{@link patio.Dataset#set}</li>
      725. +
      726. * <li>{@link patio.Dataset#singleRecord}</li>
      727. +
      728. * <li>{@link patio.Dataset#singleValue}</li>
      729. +
      730. * <li>{@link patio.Dataset#sum}</li>
      731. +
      732. * <li>{@link patio.Dataset#toCsv}</li>
      733. +
      734. * <li>{@link patio.Dataset#toHash}</li>
      735. +
      736. * <li>{@link patio.Dataset#truncate}</li>
      737. +
      738. * <li>{@link patio.Dataset#update}</li>
      739. +
      740. * </ul>
      741. +
      742. *
      743. +
      744. * </p>
      745. +
      746. * </p>
      747. +
      748. *
      749. +
      750. * @constructs
      751. +
      752. *
      753. +
      754. *
      755. +
      756. * @param {patio.Database} db the database this dataset should use when querying for data.
      757. +
      758. * @param {Object} opts options to set on this dataset instance
      759. +
      760. *
      761. +
      762. * @property {Function} rowCb callback to be invoked for each row returned from the database.
      763. +
      764. * the return value will be used as the result of query. The rowCb can also return a promise,
      765. +
      766. * The resolved value of the promise will be used as result.
      767. +
      768. *
      769. +
      770. * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
      771. +
      772. * This value will be defaulted to whatever the identifierInputMethod
      773. +
      774. * is on the database used in initialization.
      775. +
      776. *
      777. +
      778. * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
      779. +
      780. * This value will be defaulted to whatever the identifierOutputMethod
      781. +
      782. * is on the database used in initialization.
      783. +
      784. * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
      785. +
      786. * throws a {patio.DatasetError} tf the dataset doesn't have a table.
      787. +
      788. * <pre class="code">
      789. +
      790. * DB.from("table").firstSourceAlias;
      791. +
      792. * //=> "table"
      793. +
      794. *
      795. +
      796. * DB.from("table___t").firstSourceAlias;
      797. +
      798. * //=> "t"
      799. +
      800. * </pre>
      801. +
      802. *
      803. +
      804. * @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
      805. +
      806. * have a table, raises a {@link patio.erros.DatasetError}.
      807. +
      808. *<pre class="code">
      809. +
      810. *
      811. +
      812. * DB.from("table").firstSourceTable;
      813. +
      814. * //=> "table"
      815. +
      816. *
      817. +
      818. * DB.from("table___t").firstSourceTable;
      819. +
      820. * //=> "t"
      821. +
      822. * </pre>
      823. +
      824. * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
      825. +
      826. * <pre class="code">
      827. +
      828. * DB.from("items").isSimpleSelectAll; //=> true
      829. +
      830. * DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
      831. +
      832. * </pre>
      833. +
      834. * @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
      835. +
      836. * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
      837. +
      838. * delete and update statements. Accurate in this case is the number of
      839. +
      840. * rows matched by the dataset's filter.
      841. +
      842. * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
      843. +
      844. * as most allow strings with ISO 8601 format).
      845. +
      846. * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
      847. +
      848. * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
      849. +
      850. * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      851. +
      852. * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
      853. +
      854. * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
      855. +
      856. * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      857. +
      858. * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
      859. +
      860. * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
      861. +
      862. * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
      863. +
      864. * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
      865. +
      866. * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
      867. +
      868. * @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset.
      869. +
      870. * @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources
      871. +
      872. * @property {Boolean} hasSelectSource true if this dataset already has a select sources.
      873. +
      874. */
      875. +
      876. constructor:function (db, opts) {
      877. +
      878. 29216 this._super(arguments);
      879. +
      880. 29216 this.db = db;
      881. +
      882. 29216 this.__opts = {};
      883. +
      884. 29216 this.__rowCb = null;
      885. +
      886. 29216 if (db) {
      887. +
      888. 15039 this.__quoteIdentifiers = db.quoteIdentifiers;
      889. +
      890. 15039 this.__identifierInputMethod = db.identifierInputMethod;
      891. +
      892. 15039 this.__identifierOutputMethod = db.identifierOutputMethod;
      893. +
      894. }
      895. +
      896. },
      897. +
      898. +
      899. +
      900. /**
      901. +
      902. * Returns a new clone of the dataset with with the given options merged into the current datasets options.
      903. +
      904. * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
      905. +
      906. * columns are deleted. This method should generally not be called
      907. +
      908. * directly by user code.
      909. +
      910. *
      911. +
      912. * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
      913. +
      914. * @return [patio.Dataset] a cloned dataset with the merged options
      915. +
      916. **/
      917. +
      918. mergeOptions:function (opts) {
      919. +
      920. 14948 opts = isUndefined(opts) ? {} : opts;
      921. +
      922. 14948 var ds = new this._static(this.db, {});
      923. +
      924. 14948 ds.rowCb = this.rowCb;
      925. +
      926. 14948 this._static.FEATURES.forEach(function (f) {
      927. +
      928. 209272 ds[f] = this[f];
      929. +
      930. }, this);
      931. +
      932. 14948 ds.__opts = merge({}, this.__opts, opts);
      933. +
      934. 14948 ds.identifierInputMethod = this.identifierInputMethod;
      935. +
      936. 14948 ds.identifierOutputMethod = this.identifierOutputMethod;
      937. +
      938. 14948 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
      939. +
      940. 14948 if (Object.keys(opts).some(function (o) {
      941. +
      942. 13536 return columnChangeOpts.indexOf(o) != -1;
      943. +
      944. })) {
      945. +
      946. 2456 ds.__opts.columns = null;
      947. +
      948. }
      949. +
      950. 14948 return ds;
      951. +
      952. },
      953. +
      954. +
      955. +
      956. /**
      957. +
      958. * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      959. +
      960. * or {@link patio.sql.AliasedExpression}, depending on the format:
      961. +
      962. *
      963. +
      964. * <ul>
      965. +
      966. * <li>For columns : table__column___alias.</li>
      967. +
      968. * <li>For tables : schema__table___alias.</li>
      969. +
      970. * </ul>
      971. +
      972. * each portion of the identifier is optional. See example below
      973. +
      974. *
      975. +
      976. * @example
      977. +
      978. *
      979. +
      980. * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
      981. +
      982. * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
      983. +
      984. * ds.stringToIdentifier("table__column___alias");
      985. +
      986. * //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
      987. +
      988. *
      989. +
      990. * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      991. +
      992. * or {@link patio.sql.AliasedExpression}.
      993. +
      994. *
      995. +
      996. * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
      997. +
      998. */
      999. +
      1000. stringToIdentifier:function (name) {
      1001. +
      1002. 15093 if (isString(name)) {
      1003. +
      1004. 10138 var parts = this._splitString(name);
      1005. +
      1006. 10138 var schema = parts[0], table = parts[1], alias = parts[2];
      1007. +
      1008. 10138 return (schema && table && alias
      1009. +
      1010. ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
      1011. +
      1012. : (schema && table
      1013. +
      1014. ? new QualifiedIdentifier(schema, table)
      1015. +
      1016. : (table && alias
      1017. +
      1018. ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
      1019. +
      1020. } else {
      1021. +
      1022. 4955 return name;
      1023. +
      1024. }
      1025. +
      1026. },
      1027. +
      1028. +
      1029. /**
      1030. +
      1031. * Can either be a string or null.
      1032. +
      1033. *
      1034. +
      1035. *
      1036. +
      1037. * @example
      1038. +
      1039. * //columns
      1040. +
      1041. * table__column___alias //=> table.column as alias
      1042. +
      1043. * table__column //=> table.column
      1044. +
      1045. * //tables
      1046. +
      1047. * schema__table___alias //=> schema.table as alias
      1048. +
      1049. * schema__table //=> schema.table
      1050. +
      1051. *
      1052. +
      1053. * //name and alias
      1054. +
      1055. * columnOrTable___alias //=> columnOrTable as alias
      1056. +
      1057. *
      1058. +
      1059. *
      1060. +
      1061. *
      1062. +
      1063. * @return {String[]} an array with the elements being:
      1064. +
      1065. * <ul>
      1066. +
      1067. * <li>For columns :[table, column, alias].</li>
      1068. +
      1069. * <li>For tables : [schema, table, alias].</li>
      1070. +
      1071. * </ul>
      1072. +
      1073. */
      1074. +
      1075. _splitString:function (s) {
      1076. +
      1077. 19662 var ret, m;
      1078. +
      1079. 19662 if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
      1080. +
      1081. 189 ret = m.slice(1);
      1082. +
      1083. }
      1084. +
      1085. 19473 else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
      1086. +
      1087. 28 ret = [null, m[1], m[2]];
      1088. +
      1089. }
      1090. +
      1091. 19445 else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
      1092. +
      1093. 2079 ret = [m[1], m[2], null];
      1094. +
      1095. }
      1096. +
      1097. else {
      1098. +
      1099. 17366 ret = [null, s, null];
      1100. +
      1101. }
      1102. +
      1103. 19662 return ret;
      1104. +
      1105. },
      1106. +
      1107. +
      1108. /**
      1109. +
      1110. * @ignore
      1111. +
      1112. **/
      1113. +
      1114. getters:{
      1115. +
      1116. +
      1117. rowCb:function () {
      1118. +
      1119. 23052 return this.__rowCb;
      1120. +
      1121. },
      1122. +
      1123. +
      1124. identifierInputMethod:function () {
      1125. +
      1126. 14948 return this.__identifierInputMethod;
      1127. +
      1128. },
      1129. +
      1130. +
      1131. identifierOutputMethod:function () {
      1132. +
      1133. 14948 return this.__identifierOutputMethod;
      1134. +
      1135. },
      1136. +
      1137. +
      1138. firstSourceAlias:function () {
      1139. +
      1140. 579 var source = this.__opts.from;
      1141. +
      1142. 579 if (isUndefinedOrNull(source) || !source.length) {
      1143. +
      1144. 2 throw new DatasetError("No source specified for the query");
      1145. +
      1146. }
      1147. +
      1148. 577 source = source[0];
      1149. +
      1150. 577 if (isInstanceOf(source, AliasedExpression)) {
      1151. +
      1152. 20 return source.alias;
      1153. +
      1154. 557 } else if (isString(source)) {
      1155. +
      1156. 0 var parts = this._splitString(source);
      1157. +
      1158. 0 var alias = parts[2];
      1159. +
      1160. 0 return alias ? alias : source;
      1161. +
      1162. } else {
      1163. +
      1164. 557 return source;
      1165. +
      1166. }
      1167. +
      1168. },
      1169. +
      1170. +
      1171. firstSourceTable:function () {
      1172. +
      1173. 15 var source = this.__opts.from;
      1174. +
      1175. 15 if (isUndefinedOrNull(source) || !source.length) {
      1176. +
      1177. 1 throw new QueryError("No source specified for the query");
      1178. +
      1179. }
      1180. +
      1181. 14 var source = source[0];
      1182. +
      1183. 14 if (isInstanceOf(source, AliasedExpression)) {
      1184. +
      1185. 3 return source.expression;
      1186. +
      1187. 11 } else if (isString(source)) {
      1188. +
      1189. 0 var parts = this._splitString(source);
      1190. +
      1191. 0 return source;
      1192. +
      1193. } else {
      1194. +
      1195. 11 return source;
      1196. +
      1197. }
      1198. +
      1199. },
      1200. +
      1201. +
      1202. sourceList:function () {
      1203. +
      1204. 0 return (this.__opts.from || []).map(this.stringToIdentifier, this);
      1205. +
      1206. },
      1207. +
      1208. +
      1209. joinSourceList:function () {
      1210. +
      1211. 0 return (this.__opts.join || []).map(function (join) {
      1212. +
      1213. 0 return this.stringToIdentifier(join.tableAlias || join.table);
      1214. +
      1215. }, this);
      1216. +
      1217. },
      1218. +
      1219. +
      1220. hasSelectSource:function () {
      1221. +
      1222. 0 var select = this.__opts.select;
      1223. +
      1224. 0 return !(isUndefinedOrNull(select) || select.length === 0);
      1225. +
      1226. }
      1227. +
      1228. },
      1229. +
      1230. +
      1231. /**
      1232. +
      1233. * @ignore
      1234. +
      1235. **/
      1236. +
      1237. setters:{
      1238. +
      1239. /**@lends patio.Dataset.prototype*/
      1240. +
      1241. +
      1242. identifierInputMethod:function (meth) {
      1243. +
      1244. 15038 this.__identifierInputMethod = meth;
      1245. +
      1246. },
      1247. +
      1248. +
      1249. identifierOutputMethod:function (meth) {
      1250. +
      1251. 15038 this.__identifierOutputMethod = meth;
      1252. +
      1253. },
      1254. +
      1255. +
      1256. rowCb:function (cb) {
      1257. +
      1258. 18564 if (isFunction(cb) || isNull(cb)) {
      1259. +
      1260. 18559 this.__rowCb = cb;
      1261. +
      1262. } else {
      1263. +
      1264. 5 throw new DatasetError("rowCb mus be a function");
      1265. +
      1266. }
      1267. +
      1268. }
      1269. +
      1270. }
      1271. +
      1272. },
      1273. +
      1274. +
      1275. static:{
      1276. +
      1277. COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
      1278. +
      1279. COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
      1280. +
      1281. COLUMN_REF_RE3:/^(\w+)__(\w+)$/
      1282. +
      1283. }
      1284. +
      1285. }).as(module);
      1286. +
      1287. +
      +
    + +
    + + + + + +
    +
    associations/_Association.js
    +
    +
    + Coverage87.18 + SLOC515 + LOC156 + Missed20 +
    +
    +
    1. 1var comb = require("comb-proxy"),
    2. +
    3. define = comb.define,
    4. +
    5. isUndefined = comb.isUndefined,
    6. +
    7. isUndefinedOrNull = comb.isUndefinedOrNull,
    8. +
    9. isBoolean = comb.isBoolean,
    10. +
    11. isString = comb.isString,
    12. +
    13. isHash = comb.isHash,
    14. +
    15. isFunction = comb.isFunction,
    16. +
    17. isInstanceOf = comb.isInstanceOf,
    18. +
    19. Promise = comb.Promise,
    20. +
    21. PromiseList = comb.PromiseList,
    22. +
    23. hitch = comb.hitch,
    24. +
    25. array = comb.array,
    26. +
    27. isArray = comb.isArray,
    28. +
    29. Middleware = comb.plugins.Middleware,
    30. +
    31. PatioError = require("../errors").PatioError;
    32. +
    33. +
    34. 1var fetch = {
    35. +
    36. LAZY:"lazy",
    37. +
    38. EAGER:"eager"
    39. +
    40. };
    41. +
    42. +
    43. +
    44. /**
    45. +
    46. * @class
    47. +
    48. * Base class for all associations.
    49. +
    50. *
    51. +
    52. * </br>
    53. +
    54. * <b>NOT to be instantiated directly</b>
    55. +
    56. * Its just documented for reference.
    57. +
    58. *
    59. +
    60. * @constructs
    61. +
    62. * @param {Object} options
    63. +
    64. * @param {String} options.model a string to look up the model that we are associated with
    65. +
    66. * @param {Function} options.filter a callback to find association if a filter is defined then
    67. +
    68. * the association is read only
    69. +
    70. * @param {Object} options.key object with left key and right key
    71. +
    72. * @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
    73. +
    74. * @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
    75. +
    76. * the associations are automatically filled, if fetch.Lazy is supplied
    77. +
    78. * then a promise is returned and is called back with the loaded models
    79. +
    80. * @property {Model} model the model associatied with this association.
    81. +
    82. * @name Association
    83. +
    84. * @memberOf patio.associations
    85. +
    86. * */
    87. +
    88. 1define(Middleware, {
    89. +
    90. instance:{
    91. +
    92. /**@lends patio.associations.Association.prototype*/
    93. +
    94. +
    95. type:"",
    96. +
    97. +
    98. //Our associated model
    99. +
    100. _model:null,
    101. +
    102. +
    103. /**
    104. +
    105. * Fetch type
    106. +
    107. */
    108. +
    109. fetchType:fetch.LAZY,
    110. +
    111. +
    112. /**how to order our association*/
    113. +
    114. orderBy:null,
    115. +
    116. +
    117. /**Our filter method*/
    118. +
    119. filter:null,
    120. +
    121. +
    122. __hooks:null,
    123. +
    124. +
    125. isOwner:true,
    126. +
    127. +
    128. createSetter:true,
    129. +
    130. +
    131. isCascading:false,
    132. +
    133. +
    134. supportsStringKey:true,
    135. +
    136. +
    137. supportsHashKey:true,
    138. +
    139. +
    140. supportsCompositeKey:true,
    141. +
    142. +
    143. supportsLeftAndRightKey:true,
    144. +
    145. +
    146. /**
    147. +
    148. *
    149. +
    150. *Method to call to look up association,
    151. +
    152. *called after the model has been filtered
    153. +
    154. **/
    155. +
    156. _fetchMethod:"all",
    157. +
    158. +
    159. +
    160. constructor:function (options, patio, filter) {
    161. +
    162. 55 options = options || {};
    163. +
    164. 55 if (!options.model) {
    165. +
    166. 0 throw new Error("Model is required for " + this.type + " association");
    167. +
    168. }
    169. +
    170. 55 this._model = options.model;
    171. +
    172. 55 this.patio = patio;
    173. +
    174. 55 this.__opts = options;
    175. +
    176. 55 !isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
    177. +
    178. 55 this.filter = filter;
    179. +
    180. 55 this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
    181. +
    182. 55 this.__hooks =
    183. +
    184. {before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
    185. +
    186. 55 var hooks = ["Add", "Remove", "Set", "Load"];
    187. +
    188. 55 ["before", "after"].forEach(function (h) {
    189. +
    190. 110 hooks.forEach(function (a) {
    191. +
    192. 440 var hookName = h + a, hook;
    193. +
    194. 440 if (isFunction((hook = options[hookName]))) {
    195. +
    196. 0 this.__hooks[h][a.toLowerCase()] = hook;
    197. +
    198. }
    199. +
    200. }, this);
    201. +
    202. }, this);
    203. +
    204. 55 this.fetchType = options.fetchType || fetch.LAZY;
    205. +
    206. },
    207. +
    208. +
    209. _callHook:function (hook, action, args) {
    210. +
    211. 0 var func = this.__hooks[hook][action], ret;
    212. +
    213. 0 if (isFunction(func)) {
    214. +
    215. 0 ret = func.apply(this, args);
    216. +
    217. }
    218. +
    219. 0 return ret;
    220. +
    221. },
    222. +
    223. +
    224. _clearAssociations:function (model) {
    225. +
    226. 125 if (!this.readOnly) {
    227. +
    228. 125 delete model.__associations[this.name];
    229. +
    230. }
    231. +
    232. },
    233. +
    234. +
    235. _forceReloadAssociations:function (model) {
    236. +
    237. 243 if (!this.readOnly) {
    238. +
    239. 243 delete model.__associations[this.name];
    240. +
    241. 243 return model[this.name];
    242. +
    243. }
    244. +
    245. },
    246. +
    247. +
    248. /**
    249. +
    250. * @return {Boolean} true if the association is eager.
    251. +
    252. */
    253. +
    254. isEager:function () {
    255. +
    256. 2114 return this.fetchType == fetch.EAGER;
    257. +
    258. },
    259. +
    260. +
    261. _checkAssociationKey:function (parent) {
    262. +
    263. 785 var q = {};
    264. +
    265. 785 this._setAssociationKeys(parent, q);
    266. +
    267. 785 return Object.keys(q).every(function (k) {
    268. +
    269. 785 return q[k] != null
    270. +
    271. });
    272. +
    273. },
    274. +
    275. +
    276. _getAssociationKey:function () {
    277. +
    278. 5205 var options = this.__opts, key, ret = [], lk, rk;
    279. +
    280. 5205 if (!isUndefinedOrNull((key = options.key))) {
    281. +
    282. 722 if (this.supportsStringKey && isString(key)) {
    283. +
    284. //normalize the key first!
    285. +
    286. 356 ret = [
    287. +
    288. [this.isOwner ? this.defaultLeftKey : key],
    289. +
    290. [this.isOwner ? key : this.defaultRightKey]
    291. +
    292. ];
    293. +
    294. 366 } else if (this.supportsHashKey && isHash(key)) {
    295. +
    296. 366 var leftKey = Object.keys(key)[0];
    297. +
    298. 366 var rightKey = key[leftKey];
    299. +
    300. 366 ret = [
    301. +
    302. [leftKey],
    303. +
    304. [rightKey]
    305. +
    306. ];
    307. +
    308. 0 } else if (this.supportsCompositeKey && isArray(key)) {
    309. +
    310. 0 ret = [
    311. +
    312. [key],
    313. +
    314. null
    315. +
    316. ];
    317. +
    318. }
    319. +
    320. 4483 } else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
    321. +
    322. && !isUndefinedOrNull((rk = options.rightKey)))) {
    323. +
    324. 140 ret = [
    325. +
    326. array.toArray(lk), array.toArray(rk)
    327. +
    328. ];
    329. +
    330. } else {
    331. +
    332. //todo handle composite primary keys
    333. +
    334. 4343 ret = [
    335. +
    336. [this.defaultLeftKey],
    337. +
    338. [this.defaultRightKey]
    339. +
    340. ];
    341. +
    342. }
    343. +
    344. 5205 return ret;
    345. +
    346. },
    347. +
    348. +
    349. +
    350. _setAssociationKeys:function (parent, model, val) {
    351. +
    352. 1573 var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
    353. +
    354. 1573 if (leftKey && rightKey) {
    355. +
    356. 1573 for (var i = 0; i < leftKey.length; i++) {
    357. +
    358. 1573 model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
    359. +
    360. }
    361. +
    362. } else {
    363. +
    364. 0 leftKey.forEach(function (k) {
    365. +
    366. 0 model[k] = !isUndefined(val) ? val : parent[k];
    367. +
    368. });
    369. +
    370. }
    371. +
    372. },
    373. +
    374. +
    375. _setDatasetOptions:function (ds) {
    376. +
    377. 974 var options = this.__opts || {};
    378. +
    379. 974 var order, limit, distinct, select, query;
    380. +
    381. //allow for multi key ordering
    382. +
    383. 974 if (!isUndefined((select = this.select))) {
    384. +
    385. 0 ds = ds.select.apply(ds, array.toArray(select));
    386. +
    387. }
    388. +
    389. 974 if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
    390. +
    391. 0 ds = ds.filter(query);
    392. +
    393. }
    394. +
    395. 974 if (isFunction(this.filter)) {
    396. +
    397. 334 var ret = this.filter.apply(this, [ds]);
    398. +
    399. 334 if (isInstanceOf(ret, ds._static)) {
    400. +
    401. 334 ds = ret;
    402. +
    403. }
    404. +
    405. }
    406. +
    407. 974 if (!isUndefined((distinct = options.distinct))) {
    408. +
    409. 0 ds = ds.limit.apply(ds, array.toArray(distinct));
    410. +
    411. }
    412. +
    413. 974 if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
    414. +
    415. 0 ds = ds.order.apply(ds, array.toArray(order));
    416. +
    417. }
    418. +
    419. 974 if (!isUndefined((limit = options.limit))) {
    420. +
    421. 0 ds = ds.limit.apply(ds, array.toArray(limit));
    422. +
    423. }
    424. +
    425. 974 return ds;
    426. +
    427. +
    428. },
    429. +
    430. +
    431. /**
    432. *Filters our associated dataset to load our association.
    433. *
    434. *@return {Dataset} the dataset with all filters applied.
    435. @@ -6274,9 +6751,9 @@
      - Coverage88.36 - SLOC1061 - LOC318 + Coverage88.47 + SLOC1066 + LOC321 Missed37
      @@ -6474,24 +6951,24 @@
    436. * @borrows patio.Dataset#leftJoin as leftJoin
    437. * */
    438. constructor:function (options, fromDb) {
    439. -
    440. 3218 if (this.synced) {
    441. -
    442. 3218 this.__emitter = new EventEmitter();
    443. -
    444. 3218 this._super(arguments);
    445. -
    446. 3218 this.patio = patio || require("./index");
    447. -
    448. 3218 fromDb = isBoolean(fromDb) ? fromDb : false;
    449. -
    450. 3218 this.__changed = {};
    451. -
    452. 3218 this.__values = {};
    453. -
    454. 3218 if (fromDb) {
    455. -
    456. 1753 this._hook("pre", "load");
    457. -
    458. 1753 this.__isNew = false;
    459. -
    460. 1753 this.__setFromDb(options, true);
    461. -
    462. 1753 if (this._static.emitOnLoad) {
    463. -
    464. 1753 this.emit("load", this);
    465. -
    466. 1753 this._static.emit("load", this);
    467. +
    468. 3107 if (this.synced) {
    469. +
    470. 3107 this.__emitter = new EventEmitter();
    471. +
    472. 3107 this._super(arguments);
    473. +
    474. 3107 this.patio = patio || require("./index");
    475. +
    476. 3107 fromDb = isBoolean(fromDb) ? fromDb : false;
    477. +
    478. 3107 this.__changed = {};
    479. +
    480. 3107 this.__values = {};
    481. +
    482. 3107 if (fromDb) {
    483. +
    484. 1754 this._hook("pre", "load");
    485. +
    486. 1754 this.__isNew = false;
    487. +
    488. 1754 this.__setFromDb(options, true);
    489. +
    490. 1754 if (this._static.emitOnLoad) {
    491. +
    492. 1754 this.emit("load", this);
    493. +
    494. 1754 this._static.emit("load", this);
    495. }
    496. } else {
    497. -
    498. 1465 this.__isNew = true;
    499. -
    500. 1465 this.__set(options);
    501. +
    502. 1353 this.__isNew = true;
    503. +
    504. 1353 this.__set(options);
    505. }
    506. } else {
    507. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
    508. @@ -6499,26 +6976,31 @@
    509. },
    510. __set:function (values, ignore) {
    511. -
    512. 1587 values = values || {};
    513. -
    514. 1587 this.__ignore = ignore === true;
    515. -
    516. 1587 Object.keys(values).forEach(function (attribute) {
    517. +
    518. 1475 values = values || {};
    519. +
    520. 1475 this.__ignore = ignore === true;
    521. +
    522. 1475 Object.keys(values).forEach(function (attribute) {
    523. 6632 var value = values[attribute];
    524. //check if the column is a constrained value and is allowed to be set
    525. 6632 !ignore && this._checkIfColumnIsConstrained(attribute);
    526. 6632 this[attribute] = value;
    527. }, this);
    528. -
    529. 1587 this.__ignore = false;
    530. +
    531. 1475 this.__ignore = false;
    532. },
    533. __setFromDb:function (values, ignore) {
    534. -
    535. 3056 values = values || {};
    536. -
    537. 3056 this.__ignore = ignore === true;
    538. -
    539. 3056 Object.keys(values).forEach(function (column) {
    540. -
    541. 25828 var value = values[column];
    542. +
    543. 3057 values = values || {};
    544. +
    545. 3057 this.__ignore = ignore === true;
    546. +
    547. 3057 var schema = this.schema;
    548. +
    549. 3057 Object.keys(values).forEach(function (column) {
    550. +
    551. 25830 var value = values[column];
    552. // Typecast value retrieved from db
    553. -
    554. 25828 this.__values[column] = this._typeCastValue(column, value, ignore);
    555. +
    556. 25830 if (schema.hasOwnProperty(column)) {
    557. +
    558. 25149 this.__values[column] = this._typeCastValue(column, value, ignore);
    559. +
    560. } else {
    561. +
    562. 681 this[column] = value;
    563. +
    564. }
    565. }, this);
    566. -
    567. 3056 this.__ignore = false;
    568. +
    569. 3057 this.__ignore = false;
    570. },
    571. @@ -6576,11 +7058,11 @@
    572. },
    573. _getColumnValue:function (name) {
    574. -
    575. 5426 var val = this.__values[name];
    576. -
    577. 5426 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
    578. -
    579. 5426 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
    580. +
    581. 5427 var val = this.__values[name];
    582. +
    583. 5427 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
    584. +
    585. 5427 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
    586. -
    587. 5426 return columnValue;
    588. +
    589. 5427 return columnValue;
    590. },
    591. _setColumnValue:function (name, val) {
    592. @@ -6600,25 +7082,25 @@
    593. //typecast_value method, so database adapters can override/augment the handling
    594. //for database specific column types.
    595. _typeCastValue:function (column, value, fromDatabase) {
    596. -
    597. 33384 var colSchema, clazz = this._static;
    598. -
    599. 33384 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
    600. -
    601. 32704 var type = colSchema.type;
    602. -
    603. 32704 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
    604. +
    605. 32705 var colSchema, clazz = this._static;
    606. +
    607. 32705 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
    608. +
    609. 32705 var type = colSchema.type;
    610. +
    611. 32705 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
    612. 3 value = null;
    613. }
    614. -
    615. 32704 var raiseOnError = clazz.raiseOnTypecastError;
    616. -
    617. 32704 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
    618. +
    619. 32705 var raiseOnError = clazz.raiseOnTypecastError;
    620. +
    621. 32705 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
    622. 0 throw new ModelError("null is not allowed for the " + column + " column.");
    623. }
    624. -
    625. 32704 try {
    626. -
    627. 32704 value = clazz.db.typecastValue(type, value);
    628. +
    629. 32705 try {
    630. +
    631. 32705 value = clazz.db.typecastValue(type, value);
    632. } catch (e) {
    633. 0 if (raiseOnError === true) {
    634. 0 throw e;
    635. }
    636. }
    637. }
    638. -
    639. 33384 return value;
    640. +
    641. 32705 return value;
    642. },
    643. /**
    644. @@ -6690,8 +7172,8 @@
    645. 0 return emitter.listeners.apply(emitter, arguments);
    646. },
    647. emit:function () {
    648. -
    649. 11119 var emitter = this.__emitter;
    650. -
    651. 11119 return emitter.emit.apply(emitter, arguments);
    652. +
    653. 11120 var emitter = this.__emitter;
    654. +
    655. 11120 return emitter.emit.apply(emitter, arguments);
    656. },
    657. @@ -6732,7 +7214,7 @@
    658. },
    659. schema:function () {
    660. -
    661. 73400 return this._static.schema;
    662. +
    663. 75099 return this._static.schema;
    664. },
    665. columns:function () {
    666. @@ -6740,7 +7222,7 @@
    667. },
    668. synced:function () {
    669. -
    670. 12891 return this._static.synced;
    671. +
    672. 12780 return this._static.synced;
    673. }
    674. }
    675. @@ -7049,7 +7531,7 @@
    676. _defineColumnGetter:function (name) {
    677. 779 this.prototype.__defineGetter__(name, function () {
    678. -
    679. 5426 return this._getColumnValue(name);
    680. +
    681. 5427 return this._getColumnValue(name);
    682. });
    683. },
    684. @@ -7105,14 +7587,14 @@
    685. * @type patio.Database
    686. */
    687. db:function () {
    688. -
    689. 46373 var db = this.__db;
    690. -
    691. 46373 if (!db) {
    692. +
    693. 46374 var db = this.__db;
    694. +
    695. 46374 if (!db) {
    696. 86 db = this.__db = patio.defaultDatabase;
    697. }
    698. -
    699. 46373 if (!db) {
    700. +
    701. 46374 if (!db) {
    702. 0 throw new ModelError("patio has not been connected to a database");
    703. }
    704. -
    705. 46373 return db;
    706. +
    707. 46374 return db;
    708. },
    709. /**
    710. @@ -7159,8 +7641,8 @@
    711. * @type Object
    712. */
    713. schema:function () {
    714. -
    715. 73404 if (this.synced) {
    716. -
    717. 73404 return this.__schema;
    718. +
    719. 75103 if (this.synced) {
    720. +
    721. 75103 return this.__schema;
    722. } else {
    723. 0 throw new ModelError("Model has not been synced yet");
    724. }
    725. @@ -9060,21 +9542,21 @@
    726. 1var virtualRow = function (name) {
    727. -
    728. 1069 var WILDCARD = new LiteralString('*');
    729. -
    730. 1069 var QUESTION_MARK = new LiteralString('?');
    731. -
    732. 1069 var COMMA_SEPARATOR = new LiteralString(', ');
    733. -
    734. 1069 var DOUBLE_UNDERSCORE = '__';
    735. -
    736. -
    737. 1069 var parts = name.split(DOUBLE_UNDERSCORE);
    738. -
    739. 1069 var table = parts[0], column = parts[1];
    740. -
    741. 1069 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
    742. -
    743. 1069 var prox = methodMissing(ident, function (m) {
    744. +
    745. 875 var WILDCARD = new LiteralString('*');
    746. +
    747. 875 var QUESTION_MARK = new LiteralString('?');
    748. +
    749. 875 var COMMA_SEPARATOR = new LiteralString(', ');
    750. +
    751. 875 var DOUBLE_UNDERSCORE = '__';
    752. +
    753. +
    754. 875 var parts = name.split(DOUBLE_UNDERSCORE);
    755. +
    756. 875 var table = parts[0], column = parts[1];
    757. +
    758. 875 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
    759. +
    760. 875 var prox = methodMissing(ident, function (m) {
    761. 4 return function () {
    762. 3 var args = argsToArray(arguments);
    763. 3 return SQLFunction.fromArgs([m, name].concat(args));
    764. }
    765. }, column ? QualifiedIdentifier : Identifier);
    766. -
    767. 1069 var ret = createFunctionWrapper(prox, function (m) {
    768. +
    769. 875 var ret = createFunctionWrapper(prox, function (m) {
    770. 548 var args = argsToArray(arguments);
    771. 548 if (args.length) {
    772. 542 return SQLFunction.fromArgs([name].concat(args));
    773. @@ -9084,8 +9566,8 @@
    774. }, function () {
    775. 0 return SQLFunction.fromArgs(arguments);
    776. });
    777. -
    778. 1069 ret.__proto__ = ident;
    779. -
    780. 1069 return ret;
    781. +
    782. 875 ret.__proto__ = ident;
    783. +
    784. 875 return ret;
    785. };
    786. 1var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
    787. @@ -9472,3395 +9954,2938 @@
    788. 442 return args.length > 1 ? PlaceHolderLiteralString.fromArgs(args) : new LiteralString(s);
    789. },
    790. -
    791. /**
    792. -
    793. * Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.
    794. -
    795. *
    796. -
    797. * @example
    798. -
    799. *
    800. -
    801. * sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)
    802. -
    803. *
    804. -
    805. */
    806. -
    807. "case":function (hash, /*args**/opts) {
    808. -
    809. 2 var args = argsToArray(arguments, 1);
    810. -
    811. 2 return CaseExpression.fromArgs([hashToArray(hash)].concat(args));
    812. -
    813. },
    814. -
    815. -
    816. /**
    817. -
    818. * Creates a {@link patio.sql.StringExpression}
    819. -
    820. *
    821. -
    822. * Return a {@link patio.sql.StringExpression} representing an SQL string made up of the
    823. -
    824. * concatenation of this array's elements. If an joiner is passed
    825. -
    826. * it is used in between each element of the array in the SQL
    827. -
    828. * concatenation.
    829. -
    830. *
    831. -
    832. * @example
    833. -
    834. * patio.sql.sqlStringJoin(["a"]); //=> a
    835. -
    836. * //you can use sql.* as a shortcut to get an identifier
    837. -
    838. * patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b
    839. -
    840. * patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'
    841. -
    842. * patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b
    843. -
    844. */
    845. -
    846. sqlStringJoin:function (arr, joiner) {
    847. -
    848. 6 joiner = joiner || null;
    849. -
    850. 6 var args;
    851. -
    852. 6 arr = arr.map(function (a) {
    853. -
    854. 12 return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)
    855. -
    856. });
    857. -
    858. 6 if (joiner) {
    859. -
    860. 4 var newJoiner = [];
    861. -
    862. 4 for (var i = 0; i < arr.length; i++) {
    863. -
    864. 9 newJoiner.push(joiner);
    865. -
    866. }
    867. -
    868. 4 args = array.flatten(array.zip(arr, newJoiner));
    869. -
    870. 4 args.pop();
    871. -
    872. } else {
    873. -
    874. 2 args = arr;
    875. -
    876. }
    877. -
    878. 6 args = args.map(function (a) {
    879. -
    880. 17 return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;
    881. -
    882. });
    883. -
    884. 6 return StringExpression.fromArgs(["||"].concat(args));
    885. -
    886. },
    887. -
    888. -
    889. Year:Year,
    890. -
    891. TimeStamp:TimeStamp,
    892. -
    893. Time:Time,
    894. -
    895. DateTime:DateTime,
    896. -
    897. Float:Float,
    898. -
    899. Decimal:Decimal
    900. -
    901. -
    902. };
    903. -
    904. -
    905. 1sql.__defineGetter__("patio", function () {
    906. -
    907. 0 !patio && (patio = require("./index"));
    908. -
    909. 0 return patio;
    910. -
    911. });
    912. -
    913. -
    914. 1exports.sql = methodMissing(sql, function (name) {
    915. -
    916. 1069 return virtualRow(name);
    917. -
    918. });
    919. -
    920. -
    921. 1var OPERTATOR_INVERSIONS = {
    922. -
    923. AND:"OR",
    924. -
    925. OR:"AND",
    926. -
    927. GT:"lte",
    928. -
    929. GTE:"lt",
    930. -
    931. LT:"gte",
    932. -
    933. LTE:"gt",
    934. -
    935. EQ:"neq",
    936. -
    937. NEQ:"eq",
    938. -
    939. LIKE:'NOT LIKE',
    940. -
    941. "NOT LIKE":"LIKE",
    942. -
    943. '!~*':'~*',
    944. -
    945. '~*':'!~*',
    946. -
    947. "~":'!~',
    948. -
    949. "IN":'NOTIN',
    950. -
    951. "NOTIN":"IN",
    952. -
    953. "IS":'IS NOT',
    954. -
    955. "ISNOT":"IS",
    956. -
    957. NOT:"NOOP",
    958. -
    959. NOOP:"NOT",
    960. -
    961. ILIKE:'NOT ILIKE',
    962. -
    963. NOTILIKE:"ILIKE"
    964. -
    965. };
    966. -
    967. -
    968. // Standard mathematical operators used in +NumericMethods+
    969. -
    970. 1var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};
    971. -
    972. -
    973. // Bitwise mathematical operators used in +NumericMethods+
    974. -
    975. 1var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};
    976. -
    977. -
    978. -
    979. 1var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};
    980. -
    981. -
    982. //Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+
    983. -
    984. 1var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};
    985. -
    986. -
    987. //Operators that use IN/NOT IN for inclusion/exclusion
    988. -
    989. 1var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};
    990. -
    991. -
    992. //Operators that use IS, used for special casing to override literal true/false values
    993. -
    994. 1var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};
    995. -
    996. -
    997. //Operator symbols that take exactly two arguments
    998. -
    999. 1var TWO_ARITY_OPERATORS = merge({
    1000. -
    1001. EQ:'=',
    1002. -
    1003. NEQ:'!=', LIKE:"LIKE",
    1004. -
    1005. "NOT LIKE":'NOT LIKE',
    1006. -
    1007. ILIKE:"ILIKE",
    1008. -
    1009. "NOT ILIKE":'NOT ILIKE',
    1010. -
    1011. "~":"~",
    1012. -
    1013. '!~':"!~",
    1014. -
    1015. '~*':"~*",
    1016. -
    1017. '!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);
    1018. -
    1019. -
    1020. //Operator symbols that take one or more arguments
    1021. -
    1022. 1var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);
    1023. -
    1024. -
    1025. //Operator symbols that take only a single argument
    1026. -
    1027. 1var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};
    1028. -
    1029. -
    1030. /**
    1031. -
    1032. * @class Mixin to provide alias methods to an expression.
    1033. -
    1034. *
    1035. -
    1036. * @name AliasMethods
    1037. -
    1038. * @memberOf patio.sql
    1039. -
    1040. */
    1041. -
    1042. 1var AliasMethods = define(null, {
    1043. -
    1044. instance:{
    1045. -
    1046. /**@lends patio.sql.AliasMethods.prototype*/
    1047. -
    1048. -
    1049. /**
    1050. -
    1051. * Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression
    1052. -
    1053. * to the given alias.
    1054. -
    1055. *
    1056. -
    1057. * @example
    1058. -
    1059. *
    1060. -
    1061. * sql.identifier("column").as("alias");
    1062. -
    1063. * //=> "column" AS "alias"
    1064. -
    1065. *
    1066. -
    1067. * @param {String} alias the alias to assign to the expression.
    1068. -
    1069. *
    1070. -
    1071. * @return {patio.sql.AliasedExpression} the aliased expression.
    1072. -
    1073. */
    1074. -
    1075. as:function (alias) {
    1076. -
    1077. 552 return new AliasedExpression(this, alias);
    1078. -
    1079. }
    1080. -
    1081. -
    1082. }
    1083. -
    1084. }).as(sql, "AliasMethods");
    1085. -
    1086. -
    1087. 1var bitWiseMethod = function (op) {
    1088. -
    1089. 5 return function (expression) {
    1090. -
    1091. 0 if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {
    1092. -
    1093. 0 throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
    1094. -
    1095. }
    1096. -
    1097. else {
    1098. -
    1099. 0 return new BooleanExpression(op, this, expression);
    1100. -
    1101. }
    1102. -
    1103. }
    1104. -
    1105. };
    1106. -
    1107. -
    1108. /**
    1109. -
    1110. * @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These
    1111. -
    1112. * methods are only on {@link patio.sql.NumericExpression}
    1113. -
    1114. *
    1115. -
    1116. * @example
    1117. -
    1118. * sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
    1119. -
    1120. * sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
    1121. -
    1122. * sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
    1123. -
    1124. * sql.a.sqlNumber.leftShift("b") // "a" << "b"
    1125. -
    1126. * sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
    1127. -
    1128. *
    1129. -
    1130. * @name BitWiseMethods
    1131. -
    1132. * @memberOf patio.sql
    1133. -
    1134. */
    1135. -
    1136. 1var BitWiseMethods = define(null, {
    1137. -
    1138. instance:{
    1139. -
    1140. /**@lends patio.sql.BitWiseMethods.prototype*/
    1141. -
    1142. -
    1143. /**
    1144. -
    1145. * Bitwise and
    1146. -
    1147. *
    1148. -
    1149. * @example
    1150. -
    1151. * sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
    1152. -
    1153. */
    1154. -
    1155. bitWiseAnd:bitWiseMethod("bitWiseAnd"),
    1156. -
    1157. -
    1158. /**
    1159. -
    1160. * Bitwise or
    1161. -
    1162. *
    1163. -
    1164. * @example
    1165. -
    1166. * sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
    1167. -
    1168. */
    1169. -
    1170. bitWiseOr:bitWiseMethod("bitWiseOr"),
    1171. -
    1172. -
    1173. /**
    1174. -
    1175. * Exclusive Or
    1176. -
    1177. *
    1178. -
    1179. * @example
    1180. -
    1181. *
    1182. -
    1183. * sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
    1184. -
    1185. */
    1186. -
    1187. exclusiveOr:bitWiseMethod("exclusiveOr"),
    1188. -
    1189. -
    1190. /**
    1191. -
    1192. * Bitwise shift left
    1193. -
    1194. *
    1195. -
    1196. * @example
    1197. -
    1198. *
    1199. -
    1200. * sql.a.sqlNumber.leftShift("b") // "a" << "b"
    1201. -
    1202. */
    1203. -
    1204. leftShift:bitWiseMethod("leftShift"),
    1205. -
    1206. -
    1207. /**
    1208. -
    1209. * Bitwise shift right
    1210. -
    1211. *
    1212. -
    1213. * @example
    1214. -
    1215. *
    1216. -
    1217. * sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
    1218. -
    1219. */
    1220. -
    1221. rightShift:bitWiseMethod("rightShift")
    1222. -
    1223. }
    1224. -
    1225. }).as(sql, "BitWiseMethods");
    1226. +
    1227. /**
    1228. +
    1229. * Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.
    1230. +
    1231. *
    1232. +
    1233. * @example
    1234. +
    1235. *
    1236. +
    1237. * sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)
    1238. +
    1239. *
    1240. +
    1241. */
    1242. +
    1243. "case":function (hash, /*args**/opts) {
    1244. +
    1245. 2 var args = argsToArray(arguments, 1);
    1246. +
    1247. 2 return CaseExpression.fromArgs([hashToArray(hash)].concat(args));
    1248. +
    1249. },
    1250. -
    1251. 1var booleanMethod = function (op) {
    1252. -
    1253. 2 return function (expression) {
    1254. -
    1255. 7 if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
    1256. -
    1257. 0 throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");
    1258. -
    1259. }
    1260. -
    1261. else {
    1262. -
    1263. 7 return new BooleanExpression(op, this, expression);
    1264. +
    1265. /**
    1266. +
    1267. * Creates a {@link patio.sql.StringExpression}
    1268. +
    1269. *
    1270. +
    1271. * Return a {@link patio.sql.StringExpression} representing an SQL string made up of the
    1272. +
    1273. * concatenation of this array's elements. If an joiner is passed
    1274. +
    1275. * it is used in between each element of the array in the SQL
    1276. +
    1277. * concatenation.
    1278. +
    1279. *
    1280. +
    1281. * @example
    1282. +
    1283. * patio.sql.sqlStringJoin(["a"]); //=> a
    1284. +
    1285. * //you can use sql.* as a shortcut to get an identifier
    1286. +
    1287. * patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b
    1288. +
    1289. * patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'
    1290. +
    1291. * patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b
    1292. +
    1293. */
    1294. +
    1295. sqlStringJoin:function (arr, joiner) {
    1296. +
    1297. 6 joiner = joiner || null;
    1298. +
    1299. 6 var args;
    1300. +
    1301. 6 arr = arr.map(function (a) {
    1302. +
    1303. 12 return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)
    1304. +
    1305. });
    1306. +
    1307. 6 if (joiner) {
    1308. +
    1309. 4 var newJoiner = [];
    1310. +
    1311. 4 for (var i = 0; i < arr.length; i++) {
    1312. +
    1313. 9 newJoiner.push(joiner);
    1314. +
    1315. }
    1316. +
    1317. 4 args = array.flatten(array.zip(arr, newJoiner));
    1318. +
    1319. 4 args.pop();
    1320. +
    1321. } else {
    1322. +
    1323. 2 args = arr;
    1324. }
    1325. -
    1326. }
    1327. -
    1328. };
    1329. -
    1330. -
    1331. /**
    1332. -
    1333. * @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators
    1334. -
    1335. * that are defined on objects that can be used in a boolean context in SQL
    1336. -
    1337. * ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).
    1338. -
    1339. *
    1340. -
    1341. * @example
    1342. -
    1343. * sql.a.and(sql.b) //=> "a" AND "b"
    1344. -
    1345. * sql.a.or(sql.b) //=> "a" OR "b"
    1346. -
    1347. * sql.a.not() //=> NOT "a"
    1348. -
    1349. *
    1350. -
    1351. * @name BooleanMethods
    1352. -
    1353. * @memberOf patio.sql
    1354. -
    1355. */
    1356. -
    1357. 1var BooleanMethods = define(null, {
    1358. -
    1359. instance:{
    1360. -
    1361. /**@lends patio.sql.BooleanMethods.prototype*/
    1362. -
    1363. -
    1364. /**
    1365. -
    1366. *
    1367. -
    1368. * @function
    1369. -
    1370. * Logical AND
    1371. -
    1372. *
    1373. -
    1374. * @example
    1375. -
    1376. *
    1377. -
    1378. * sql.a.and(sql.b) //=> "a" AND "b"
    1379. -
    1380. *
    1381. -
    1382. * @return {patio.sql.BooleanExpression} a ANDed boolean expression.
    1383. -
    1384. */
    1385. -
    1386. and:booleanMethod("and"),
    1387. +
    1388. 6 args = args.map(function (a) {
    1389. +
    1390. 17 return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;
    1391. +
    1392. });
    1393. +
    1394. 6 return StringExpression.fromArgs(["||"].concat(args));
    1395. +
    1396. },
    1397. -
    1398. /**
    1399. -
    1400. * @function
    1401. -
    1402. * Logical OR
    1403. -
    1404. *
    1405. -
    1406. * @example
    1407. -
    1408. *
    1409. -
    1410. * sql.a.or(sql.b) //=> "a" OR "b"
    1411. -
    1412. *
    1413. -
    1414. * @return {patio.sql.BooleanExpression} a ORed boolean expression
    1415. -
    1416. */
    1417. -
    1418. or:booleanMethod("or"),
    1419. +
    1420. Year:Year,
    1421. +
    1422. TimeStamp:TimeStamp,
    1423. +
    1424. Time:Time,
    1425. +
    1426. DateTime:DateTime,
    1427. +
    1428. Float:Float,
    1429. +
    1430. Decimal:Decimal
    1431. -
    1432. /**
    1433. -
    1434. * Logical NOT
    1435. -
    1436. *
    1437. -
    1438. * @example
    1439. -
    1440. *
    1441. -
    1442. * sql.a.not() //=> NOT "a"
    1443. -
    1444. *
    1445. -
    1446. * @return {patio.sql.BooleanExpression} a inverted boolean expression.
    1447. -
    1448. */
    1449. -
    1450. not:function () {
    1451. -
    1452. 5 return BooleanExpression.invert(this);
    1453. -
    1454. }
    1455. +
    1456. };
    1457. -
    1458. }
    1459. -
    1460. }).as(sql, "BooleanMethods");
    1461. +
    1462. 1sql.__defineGetter__("patio", function () {
    1463. +
    1464. 0 !patio && (patio = require("./index"));
    1465. +
    1466. 0 return patio;
    1467. +
    1468. });
    1469. -
    1470. /**
    1471. -
    1472. * @class Defines case methods
    1473. -
    1474. *
    1475. -
    1476. * @name CastMethods
    1477. -
    1478. * @memberOf patio.sql
    1479. -
    1480. */
    1481. -
    1482. 1var CastMethods = define(null, {
    1483. -
    1484. instance:{
    1485. -
    1486. /**@lends patio.sql.CastMethods.prototype*/
    1487. -
    1488. /**
    1489. -
    1490. * Cast the reciever to the given SQL type.
    1491. -
    1492. *
    1493. -
    1494. * @example
    1495. -
    1496. *
    1497. -
    1498. * sql.a.cast("integer") //=> CAST(a AS integer)
    1499. -
    1500. * sql.a.cast(String) //=> CAST(a AS varchar(255))
    1501. -
    1502. *
    1503. -
    1504. * @return {patio.sql.Cast} the casted expression
    1505. -
    1506. */
    1507. -
    1508. cast:function (type) {
    1509. -
    1510. 2 return new Cast(this, type);
    1511. -
    1512. },
    1513. +
    1514. 1exports.sql = methodMissing(sql, function (name) {
    1515. +
    1516. 875 return virtualRow(name);
    1517. +
    1518. });
    1519. -
    1520. /**
    1521. -
    1522. * Cast the reciever to the given SQL type (or the database's default Number type if none given.
    1523. -
    1524. *
    1525. -
    1526. * @example
    1527. -
    1528. *
    1529. -
    1530. * sql.a.castNumeric() //=> CAST(a AS integer)
    1531. -
    1532. * sql.a.castNumeric("double") //=> CAST(a AS double precision)
    1533. -
    1534. *
    1535. -
    1536. * @param type the numeric type to cast to
    1537. -
    1538. *
    1539. -
    1540. * @return {patio.sql.NumericExpression} a casted numberic expression
    1541. -
    1542. */
    1543. -
    1544. castNumeric:function (type) {
    1545. -
    1546. 0 return this.cast(type || "integer").sqlNumber;
    1547. -
    1548. },
    1549. +
    1550. 1var OPERTATOR_INVERSIONS = {
    1551. +
    1552. AND:"OR",
    1553. +
    1554. OR:"AND",
    1555. +
    1556. GT:"lte",
    1557. +
    1558. GTE:"lt",
    1559. +
    1560. LT:"gte",
    1561. +
    1562. LTE:"gt",
    1563. +
    1564. EQ:"neq",
    1565. +
    1566. NEQ:"eq",
    1567. +
    1568. LIKE:'NOT LIKE',
    1569. +
    1570. "NOT LIKE":"LIKE",
    1571. +
    1572. '!~*':'~*',
    1573. +
    1574. '~*':'!~*',
    1575. +
    1576. "~":'!~',
    1577. +
    1578. "IN":'NOTIN',
    1579. +
    1580. "NOTIN":"IN",
    1581. +
    1582. "IS":'IS NOT',
    1583. +
    1584. "ISNOT":"IS",
    1585. +
    1586. NOT:"NOOP",
    1587. +
    1588. NOOP:"NOT",
    1589. +
    1590. ILIKE:'NOT ILIKE',
    1591. +
    1592. NOTILIKE:"ILIKE"
    1593. +
    1594. };
    1595. -
    1596. /**
    1597. -
    1598. * Cast the reciever to the given SQL type (or the database's default String type if none given),
    1599. -
    1600. * and return the result as a {@link patio.sql.StringExpression}.
    1601. -
    1602. *
    1603. -
    1604. * @example
    1605. -
    1606. *
    1607. -
    1608. * sql.a.castString() //=> CAST(a AS varchar(255))
    1609. -
    1610. * sql.a.castString("text") //=> CAST(a AS text)
    1611. -
    1612. * @param type the string type to cast to
    1613. -
    1614. *
    1615. -
    1616. * @return {patio.sql.StringExpression} the casted string expression
    1617. -
    1618. */
    1619. -
    1620. castString:function (type) {
    1621. -
    1622. 0 return this.cast(type || String).sqlString;
    1623. -
    1624. }
    1625. -
    1626. }
    1627. -
    1628. }).as(sql, "CastMethods");
    1629. +
    1630. // Standard mathematical operators used in +NumericMethods+
    1631. +
    1632. 1var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};
    1633. +
    1634. // Bitwise mathematical operators used in +NumericMethods+
    1635. +
    1636. 1var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};
    1637. -
    1638. /**
    1639. -
    1640. * @class Provides methods to assist in assigning a SQL type to
    1641. -
    1642. * particular types, i.e. Boolean, Function, Number or String.
    1643. -
    1644. *
    1645. -
    1646. * @name ComplexExpressionMethods
    1647. -
    1648. * @memberOf patio.sql
    1649. -
    1650. * @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.
    1651. -
    1652. * @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.
    1653. -
    1654. * @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.
    1655. -
    1656. * <pre class="code">
    1657. -
    1658. * sql.a.not("a") //=> NOT "a"
    1659. -
    1660. * sql.a.sqlNumber.not() //=> ~"a"
    1661. -
    1662. * </pre>
    1663. -
    1664. * @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.
    1665. -
    1666. * <pre class="code">
    1667. -
    1668. * sql.a.plus(sql.b); //=> "a" + "b"
    1669. -
    1670. * sql.a.sqlString.concat(sql.b) //=> "a" || "b"
    1671. -
    1672. * </pre>
    1673. -
    1674. */
    1675. -
    1676. 1var ComplexExpressionMethods = define(null, {
    1677. -
    1678. instance:{
    1679. -
    1680. /**@ignore*/
    1681. -
    1682. getters:{
    1683. -
    1684. /**
    1685. -
    1686. * @ignore
    1687. -
    1688. */
    1689. -
    1690. sqlBoolean:function () {
    1691. -
    1692. 0 return new BooleanExpression("noop", this);
    1693. -
    1694. },
    1695. +
    1696. 1var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};
    1697. +
    1698. //Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+
    1699. +
    1700. 1var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};
    1701. -
    1702. /**
    1703. -
    1704. * @ignore
    1705. -
    1706. */
    1707. -
    1708. sqlFunction:function () {
    1709. -
    1710. 13 return new SQLFunction(this);
    1711. -
    1712. },
    1713. +
    1714. //Operators that use IN/NOT IN for inclusion/exclusion
    1715. +
    1716. 1var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};
    1717. +
    1718. //Operators that use IS, used for special casing to override literal true/false values
    1719. +
    1720. 1var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};
    1721. -
    1722. /**
    1723. -
    1724. * @ignore
    1725. -
    1726. */
    1727. -
    1728. sqlNumber:function () {
    1729. -
    1730. 50 return new NumericExpression("noop", this);
    1731. -
    1732. },
    1733. +
    1734. //Operator symbols that take exactly two arguments
    1735. +
    1736. 1var TWO_ARITY_OPERATORS = merge({
    1737. +
    1738. EQ:'=',
    1739. +
    1740. NEQ:'!=', LIKE:"LIKE",
    1741. +
    1742. "NOT LIKE":'NOT LIKE',
    1743. +
    1744. ILIKE:"ILIKE",
    1745. +
    1746. "NOT ILIKE":'NOT ILIKE',
    1747. +
    1748. "~":"~",
    1749. +
    1750. '!~':"!~",
    1751. +
    1752. '~*':"~*",
    1753. +
    1754. '!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);
    1755. -
    1756. /**
    1757. -
    1758. * @ignore
    1759. -
    1760. */
    1761. -
    1762. sqlString:function () {
    1763. -
    1764. 0 return new StringExpression("noop", this);
    1765. -
    1766. }
    1767. -
    1768. }
    1769. -
    1770. }
    1771. -
    1772. }).as(sql, "ComplexExpressionMethods");
    1773. +
    1774. //Operator symbols that take one or more arguments
    1775. +
    1776. 1var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);
    1777. -
    1778. 1var inequalityMethod = function (op) {
    1779. -
    1780. 6 return function (expression) {
    1781. -
    1782. 88 if (isInstanceOf(expression, BooleanExpression)
    1783. -
    1784. || isBoolean(expression)
    1785. -
    1786. || isNull(expression)
    1787. -
    1788. || (isHash(expression))
    1789. -
    1790. || isArray(expression)) {
    1791. -
    1792. 0 throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
    1793. -
    1794. } else {
    1795. -
    1796. 88 return new BooleanExpression(op, this, expression);
    1797. -
    1798. }
    1799. -
    1800. }
    1801. -
    1802. };
    1803. +
    1804. //Operator symbols that take only a single argument
    1805. +
    1806. 1var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};
    1807. /**
    1808. -
    1809. * @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
    1810. -
    1811. * used in a numeric or string context in SQL.
    1812. -
    1813. *
    1814. -
    1815. * @example
    1816. -
    1817. * sql.a.gt("b") //=> a > "b"
    1818. -
    1819. * sql.a.lt("b") //=> a > "b"
    1820. -
    1821. * sql.a.gte("b") //=> a >= "b"
    1822. -
    1823. * sql.a.lte("b") //=> a <= "b"
    1824. -
    1825. * sql.a.eq("b") //=> a = "b"
    1826. +
    1827. * @class Mixin to provide alias methods to an expression.
    1828. *
    1829. -
    1830. * @name InequalityMethods
    1831. +
    1832. * @name AliasMethods
    1833. * @memberOf patio.sql
    1834. */
    1835. -
    1836. 1var InequalityMethods = define(null, {
    1837. +
    1838. 1var AliasMethods = define(null, {
    1839. instance:{
    1840. -
    1841. /**@lends patio.sql.InequalityMethods.prototype*/
    1842. -
    1843. -
    1844. /**
    1845. -
    1846. * @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.
    1847. -
    1848. * @example
    1849. -
    1850. *
    1851. -
    1852. * sql.a.gt("b") //=> a > "b"
    1853. -
    1854. *
    1855. -
    1856. * @return {patio.sql.BooleanExpression}
    1857. -
    1858. */
    1859. -
    1860. gt:inequalityMethod("gt"),
    1861. -
    1862. /**
    1863. -
    1864. * @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.
    1865. -
    1866. *
    1867. -
    1868. * @example
    1869. -
    1870. *
    1871. -
    1872. * sql.a.gte("b") //=> a >= "b"
    1873. -
    1874. *
    1875. -
    1876. * @return {patio.sql.BooleanExpression}
    1877. -
    1878. */
    1879. -
    1880. gte:inequalityMethod("gte"),
    1881. -
    1882. /**
    1883. -
    1884. * @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.
    1885. -
    1886. *
    1887. -
    1888. * @example
    1889. -
    1890. *
    1891. -
    1892. * sql.a.lt("b") //=> a < "b"
    1893. -
    1894. *
    1895. -
    1896. * @return {patio.sql.BooleanExpression}
    1897. -
    1898. */
    1899. -
    1900. lt:inequalityMethod("lt"),
    1901. -
    1902. /**
    1903. -
    1904. * @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.
    1905. -
    1906. *
    1907. -
    1908. * @example
    1909. -
    1910. *
    1911. -
    1912. * sql.a.lte("b") //=> a <= "b"
    1913. -
    1914. *
    1915. -
    1916. * @return {patio.sql.BooleanExpression}
    1917. -
    1918. */
    1919. -
    1920. lte:inequalityMethod("lte"),
    1921. -
    1922. /**
    1923. -
    1924. * @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.
    1925. -
    1926. *
    1927. -
    1928. * @example
    1929. -
    1930. *
    1931. -
    1932. * sql.a.eq("b") //=> a = "b"
    1933. -
    1934. *
    1935. -
    1936. * @return {patio.sql.BooleanExpression}
    1937. -
    1938. */
    1939. -
    1940. eq:inequalityMethod("eq"),
    1941. -
    1942. -
    1943. neq:inequalityMethod("neq"),
    1944. +
    1945. /**@lends patio.sql.AliasMethods.prototype*/
    1946. /**
    1947. -
    1948. * @private
    1949. -
    1950. *
    1951. -
    1952. * Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
    1953. +
    1954. * Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression
    1955. +
    1956. * to the given alias.
    1957. *
    1958. * @example
    1959. *
    1960. -
    1961. * sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))
    1962. -
    1963. * sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))
    1964. +
    1965. * sql.identifier("column").as("alias");
    1966. +
    1967. * //=> "column" AS "alias"
    1968. *
    1969. -
    1970. * @param {Object} items a two element array where the first element it the item to be gte and the second item lte.
    1971. +
    1972. * @param {String} alias the alias to assign to the expression.
    1973. *
    1974. -
    1975. * @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
    1976. +
    1977. * @return {patio.sql.AliasedExpression} the aliased expression.
    1978. */
    1979. -
    1980. between:function (items) {
    1981. -
    1982. 6 return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))
    1983. +
    1984. as:function (alias) {
    1985. +
    1986. 552 return new AliasedExpression(this, alias);
    1987. }
    1988. -
    1989. }
    1990. -
    1991. }).as(sql, "InequalityMethods");
    1992. -
    1993. /**
    1994. -
    1995. * @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},
    1996. -
    1997. * so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}
    1998. -
    1999. * or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>
    2000. -
    2001. *
    2002. -
    2003. * @name NoBooleanInputMethods
    2004. -
    2005. * @memberOf patio.sql
    2006. -
    2007. */
    2008. -
    2009. 1var NoBooleanInputMethods = define(null, {
    2010. -
    2011. instance:{
    2012. -
    2013. constructor:function (op) {
    2014. -
    2015. 22 var args = argsToArray(arguments, 1);
    2016. -
    2017. 22 args.forEach(function (expression) {
    2018. -
    2019. 26 if ((isInstanceOf(expression, BooleanExpression))
    2020. -
    2021. || isBoolean(expression)
    2022. -
    2023. || isNull(expression)
    2024. -
    2025. || (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))
    2026. -
    2027. || isArray(expression)) {
    2028. -
    2029. 0 throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
    2030. -
    2031. }
    2032. -
    2033. });
    2034. -
    2035. 22 this._super(arguments);
    2036. -
    2037. }
    2038. }
    2039. -
    2040. }).as(sql, "NoBooleanInputMethods");
    2041. +
    2042. }).as(sql, "AliasMethods");
    2043. -
    2044. 1var numericMethod = function (op) {
    2045. -
    2046. 4 return function (expression) {
    2047. -
    2048. 12 if (isInstanceOf(expression, BooleanExpression, StringExpression)) {
    2049. +
    2050. 1var bitWiseMethod = function (op) {
    2051. +
    2052. 5 return function (expression) {
    2053. +
    2054. 0 if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {
    2055. 0 throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
    2056. -
    2057. } else {
    2058. -
    2059. 12 return new NumericExpression(op, this, expression);
    2060. +
    2061. }
    2062. +
    2063. else {
    2064. +
    2065. 0 return new BooleanExpression(op, this, expression);
    2066. }
    2067. }
    2068. };
    2069. -
    2070. /**
    2071. -
    2072. * @class This mixin includes the standard mathematical methods (+, -, *, and /)
    2073. -
    2074. * that are defined on objects that can be used in a numeric context in SQL.
    2075. +
    2076. * @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These
    2077. +
    2078. * methods are only on {@link patio.sql.NumericExpression}
    2079. *
    2080. * @example
    2081. -
    2082. * sql.a.plus(sql.b) //=> "a" + "b"
    2083. -
    2084. * sql.a.minus(sql.b) //=> "a" - "b"
    2085. -
    2086. * sql.a.multiply(sql.b) //=> "a" * "b"
    2087. -
    2088. * sql.a.divide(sql.b) //=> "a" / "b"
    2089. +
    2090. * sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
    2091. +
    2092. * sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
    2093. +
    2094. * sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
    2095. +
    2096. * sql.a.sqlNumber.leftShift("b") // "a" << "b"
    2097. +
    2098. * sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
    2099. *
    2100. -
    2101. * @name NumericMethods
    2102. +
    2103. * @name BitWiseMethods
    2104. * @memberOf patio.sql
    2105. */
    2106. -
    2107. 1var NumericMethods = define(null, {
    2108. +
    2109. 1var BitWiseMethods = define(null, {
    2110. instance:{
    2111. -
    2112. /**@lends patio.sql.NumericMethods.prototype*/
    2113. -
    2114. -
    2115. -
    2116. /**
    2117. -
    2118. * @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.
    2119. -
    2120. *
    2121. -
    2122. * @example
    2123. -
    2124. *
    2125. -
    2126. * sql.a.plus(sql.b) //=> "a" + "b"
    2127. -
    2128. *
    2129. -
    2130. * @return {patio.sql.NumericExpression}
    2131. -
    2132. */
    2133. -
    2134. plus:numericMethod("plus"),
    2135. +
    2136. /**@lends patio.sql.BitWiseMethods.prototype*/
    2137. /**
    2138. -
    2139. * @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.
    2140. +
    2141. * Bitwise and
    2142. *
    2143. * @example
    2144. -
    2145. *
    2146. -
    2147. * sql.a.minus(sql.b) //=> "a" - "b"
    2148. -
    2149. *
    2150. -
    2151. * @return {patio.sql.NumericExpression}
    2152. +
    2153. * sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
    2154. */
    2155. -
    2156. minus:numericMethod("minus"),
    2157. +
    2158. bitWiseAnd:bitWiseMethod("bitWiseAnd"),
    2159. /**
    2160. -
    2161. * @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
    2162. +
    2163. * Bitwise or
    2164. *
    2165. * @example
    2166. -
    2167. *
    2168. -
    2169. * sql.a.divide(sql.b) //=> "a" / "b"
    2170. -
    2171. *
    2172. -
    2173. * @return {patio.sql.NumericExpression}
    2174. +
    2175. * sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
    2176. */
    2177. -
    2178. divide:numericMethod("divide"),
    2179. +
    2180. bitWiseOr:bitWiseMethod("bitWiseOr"),
    2181. /**
    2182. -
    2183. * @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
    2184. +
    2185. * Exclusive Or
    2186. *
    2187. * @example
    2188. *
    2189. -
    2190. * sql.a.multiply(sql.b) //=> "a" * "b"
    2191. -
    2192. *
    2193. -
    2194. * @return {patio.sql.NumericExpression}
    2195. +
    2196. * sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
    2197. */
    2198. -
    2199. multiply:numericMethod("multiply")
    2200. -
    2201. }
    2202. -
    2203. }).as(sql, "NumericMethods");
    2204. -
    2205. -
    2206. -
    2207. /**
    2208. -
    2209. * @class This mixin provides ordering methods ("asc", "desc") to expression.
    2210. -
    2211. *
    2212. -
    2213. * @example
    2214. -
    2215. *
    2216. -
    2217. * sql.name.asc(); //=> name ASC
    2218. -
    2219. * sql.price.desc(); //=> price DESC
    2220. -
    2221. * sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
    2222. -
    2223. * sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
    2224. -
    2225. *
    2226. -
    2227. * @name OrderedMethods
    2228. -
    2229. * @memberOf patio.sql
    2230. -
    2231. */
    2232. -
    2233. 1var OrderedMethods = define(null, {
    2234. -
    2235. instance:{
    2236. -
    2237. /**@lends patio.sql.OrderedMethods.prototype*/
    2238. +
    2239. exclusiveOr:bitWiseMethod("exclusiveOr"),
    2240. /**
    2241. -
    2242. * Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
    2243. +
    2244. * Bitwise shift left
    2245. *
    2246. -
    2247. * @example
    2248. -
    2249. * sql.name.asc(); //=> name ASC
    2250. -
    2251. * sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
    2252. +
    2253. * @example
    2254. *
    2255. -
    2256. * @param {Object} [options] options to use when sorting
    2257. -
    2258. * @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
    2259. -
    2260. * before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
    2261. -
    2262. * @return {patio.sql.OrderedExpression}
    2263. +
    2264. * sql.a.sqlNumber.leftShift("b") // "a" << "b"
    2265. */
    2266. -
    2267. asc:function (options) {
    2268. -
    2269. 7 return new OrderedExpression(this, false, options);
    2270. -
    2271. },
    2272. +
    2273. leftShift:bitWiseMethod("leftShift"),
    2274. /**
    2275. -
    2276. * Mark the receiving SQL column as sorting in a descending fashion.
    2277. -
    2278. * @example
    2279. +
    2280. * Bitwise shift right
    2281. *
    2282. -
    2283. * sql.price.desc(); //=> price DESC
    2284. -
    2285. * sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
    2286. +
    2287. * @example
    2288. *
    2289. -
    2290. * @param {Object} [options] options to use when sorting
    2291. -
    2292. * @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
    2293. -
    2294. * before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
    2295. -
    2296. * @return {patio.sql.OrderedExpression}
    2297. +
    2298. * sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
    2299. */
    2300. -
    2301. desc:function (options) {
    2302. -
    2303. 26 return new OrderedExpression(this, true, options);
    2304. -
    2305. }
    2306. +
    2307. rightShift:bitWiseMethod("rightShift")
    2308. }
    2309. -
    2310. }).as(sql, "OrderedMethods");
    2311. +
    2312. }).as(sql, "BitWiseMethods");
    2313. +
    2314. 1var booleanMethod = function (op) {
    2315. +
    2316. 2 return function (expression) {
    2317. +
    2318. 7 if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
    2319. +
    2320. 0 throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");
    2321. +
    2322. }
    2323. +
    2324. else {
    2325. +
    2326. 7 return new BooleanExpression(op, this, expression);
    2327. +
    2328. }
    2329. +
    2330. }
    2331. +
    2332. };
    2333. /**
    2334. -
    2335. * @class This mixin provides methods related to qualifying expression.
    2336. +
    2337. * @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators
    2338. +
    2339. * that are defined on objects that can be used in a boolean context in SQL
    2340. +
    2341. * ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).
    2342. *
    2343. * @example
    2344. +
    2345. * sql.a.and(sql.b) //=> "a" AND "b"
    2346. +
    2347. * sql.a.or(sql.b) //=> "a" OR "b"
    2348. +
    2349. * sql.a.not() //=> NOT "a"
    2350. *
    2351. -
    2352. * sql.column.qualify("table") //=> "table"."column"
    2353. -
    2354. * sql.table.qualify("schema") //=> "schema"."table"
    2355. -
    2356. * sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
    2357. -
    2358. *
    2359. -
    2360. * @name QualifyingMethods
    2361. +
    2362. * @name BooleanMethods
    2363. * @memberOf patio.sql
    2364. */
    2365. -
    2366. 1var QualifyingMethods = define(null, {
    2367. +
    2368. 1var BooleanMethods = define(null, {
    2369. instance:{
    2370. -
    2371. /**@lends patio.sql.QualifyingMethods.prototype*/
    2372. +
    2373. /**@lends patio.sql.BooleanMethods.prototype*/
    2374. /**
    2375. -
    2376. * Qualify the receiver with the given qualifier (table for column/schema for table).
    2377. +
    2378. *
    2379. +
    2380. * @function
    2381. +
    2382. * Logical AND
    2383. *
    2384. * @example
    2385. -
    2386. * sql.column.qualify("table") //=> "table"."column"
    2387. -
    2388. * sql.table.qualify("schema") //=> "schema"."table"
    2389. -
    2390. * sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
    2391. *
    2392. -
    2393. * @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.
    2394. +
    2395. * sql.a.and(sql.b) //=> "a" AND "b"
    2396. *
    2397. -
    2398. * @return {patio.sql.QualifiedIdentifier}
    2399. +
    2400. * @return {patio.sql.BooleanExpression} a ANDed boolean expression.
    2401. */
    2402. -
    2403. qualify:function (qualifier) {
    2404. -
    2405. 576 return new QualifiedIdentifier(qualifier, this);
    2406. -
    2407. },
    2408. +
    2409. and:booleanMethod("and"),
    2410. /**
    2411. -
    2412. * Use to create a .* expression.
    2413. +
    2414. * @function
    2415. +
    2416. * Logical OR
    2417. *
    2418. * @example
    2419. -
    2420. * sql.table.all() //=> "table".*
    2421. -
    2422. * sql.table.qualify("schema").all() //=> "schema"."table".*
    2423. *
    2424. +
    2425. * sql.a.or(sql.b) //=> "a" OR "b"
    2426. *
    2427. -
    2428. * @return {patio.sql.ColumnAll}
    2429. +
    2430. * @return {patio.sql.BooleanExpression} a ORed boolean expression
    2431. */
    2432. -
    2433. all:function () {
    2434. -
    2435. 3 return new ColumnAll(this);
    2436. -
    2437. }
    2438. +
    2439. or:booleanMethod("or"),
    2440. +
    2441. /**
    2442. +
    2443. * Logical NOT
    2444. +
    2445. *
    2446. +
    2447. * @example
    2448. +
    2449. *
    2450. +
    2451. * sql.a.not() //=> NOT "a"
    2452. +
    2453. *
    2454. +
    2455. * @return {patio.sql.BooleanExpression} a inverted boolean expression.
    2456. +
    2457. */
    2458. +
    2459. not:function () {
    2460. +
    2461. 5 return BooleanExpression.invert(this);
    2462. +
    2463. }
    2464. }
    2465. -
    2466. }).as(sql, "QualifyingMethods");
    2467. -
    2468. +
    2469. }).as(sql, "BooleanMethods");
    2470. /**
    2471. -
    2472. * @class This mixin provides SQL string methods such as (like and iLike).
    2473. -
    2474. *
    2475. -
    2476. * @example
    2477. -
    2478. *
    2479. -
    2480. * sql.a.like("A%"); //=> "a" LIKE 'A%'
    2481. -
    2482. * sql.a.iLike("A%"); //=> "a" LIKE 'A%'
    2483. -
    2484. * sql.a.like(/^a/); //=> "a" ~* '^a'
    2485. +
    2486. * @class Defines case methods
    2487. *
    2488. -
    2489. * @name StringMethods
    2490. +
    2491. * @name CastMethods
    2492. * @memberOf patio.sql
    2493. */
    2494. -
    2495. 1var StringMethods = define(null, {
    2496. +
    2497. 1var CastMethods = define(null, {
    2498. instance:{
    2499. -
    2500. /**@lends patio.sql.StringMethods.prototype*/
    2501. -
    2502. +
    2503. /**@lends patio.sql.CastMethods.prototype*/
    2504. /**
    2505. -
    2506. * Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver
    2507. -
    2508. * with the given patterns. See {@link patio.sql.StringExpression#like}.
    2509. +
    2510. * Cast the reciever to the given SQL type.
    2511. *
    2512. * @example
    2513. -
    2514. * sql.a.iLike("A%"); //=> "a" LIKE 'A%'
    2515. *
    2516. -
    2517. * @return {patio.sql.BooleanExpression}
    2518. +
    2519. * sql.a.cast("integer") //=> CAST(a AS integer)
    2520. +
    2521. * sql.a.cast(String) //=> CAST(a AS varchar(255))
    2522. +
    2523. *
    2524. +
    2525. * @return {patio.sql.Cast} the casted expression
    2526. */
    2527. -
    2528. ilike:function (expression) {
    2529. -
    2530. 310 expression = argsToArray(arguments);
    2531. -
    2532. 310 return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([
    2533. -
    2534. {caseInsensitive:true}
    2535. -
    2536. ]));
    2537. +
    2538. cast:function (type) {
    2539. +
    2540. 2 return new Cast(this, type);
    2541. },
    2542. /**
    2543. -
    2544. * Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with
    2545. -
    2546. * the given patterns. See {@link patio.sql.StringExpression#like}.
    2547. +
    2548. * Cast the reciever to the given SQL type (or the database's default Number type if none given.
    2549. *
    2550. * @example
    2551. -
    2552. * sql.a.like(/^a/); //=> "a" ~* '^a'
    2553. -
    2554. * sql.a.like("A%"); //=> "a" LIKE 'A%'
    2555. *
    2556. -
    2557. * @param expression
    2558. +
    2559. * sql.a.castNumeric() //=> CAST(a AS integer)
    2560. +
    2561. * sql.a.castNumeric("double") //=> CAST(a AS double precision)
    2562. +
    2563. *
    2564. +
    2565. * @param type the numeric type to cast to
    2566. +
    2567. *
    2568. +
    2569. * @return {patio.sql.NumericExpression} a casted numberic expression
    2570. */
    2571. -
    2572. like:function (expression) {
    2573. -
    2574. 11 expression = argsToArray(arguments);
    2575. -
    2576. 11 return StringExpression.like.apply(StringExpression, [this].concat(expression));
    2577. -
    2578. }
    2579. -
    2580. }
    2581. -
    2582. }).as(sql, "StringMethods");
    2583. -
    2584. -
    2585. /**
    2586. -
    2587. * @class This mixin provides string concatenation methods ("concat");
    2588. -
    2589. *
    2590. -
    2591. * @example
    2592. -
    2593. *
    2594. -
    2595. * sql.x.sqlString.concat("y"); //=> "x" || "y"
    2596. -
    2597. *
    2598. -
    2599. * @name StringConcatenationMethods
    2600. -
    2601. * @memberOf patio.sql
    2602. -
    2603. */
    2604. -
    2605. 1var StringConcatenationMethods = define(null, {
    2606. -
    2607. instance:{
    2608. -
    2609. /**@lends patio.sql.StringConcatenationMethods.prototype*/
    2610. +
    2611. castNumeric:function (type) {
    2612. +
    2613. 0 return this.cast(type || "integer").sqlNumber;
    2614. +
    2615. },
    2616. /**
    2617. -
    2618. * Return a {@link patio.sql.StringExpression} representing the concatenation of this expression
    2619. -
    2620. * with the given argument.
    2621. +
    2622. * Cast the reciever to the given SQL type (or the database's default String type if none given),
    2623. +
    2624. * and return the result as a {@link patio.sql.StringExpression}.
    2625. *
    2626. * @example
    2627. *
    2628. -
    2629. * sql.x.sqlString.concat("y"); //=> "x" || "y"
    2630. +
    2631. * sql.a.castString() //=> CAST(a AS varchar(255))
    2632. +
    2633. * sql.a.castString("text") //=> CAST(a AS text)
    2634. +
    2635. * @param type the string type to cast to
    2636. *
    2637. -
    2638. * @param expression expression to concatenate this expression with.
    2639. +
    2640. * @return {patio.sql.StringExpression} the casted string expression
    2641. */
    2642. -
    2643. concat:function (expression) {
    2644. -
    2645. 0 return new StringExpression("||", this, expression);
    2646. +
    2647. castString:function (type) {
    2648. +
    2649. 0 return this.cast(type || String).sqlString;
    2650. }
    2651. }
    2652. -
    2653. }).as(sql, "StringConcatenationMethods");
    2654. +
    2655. }).as(sql, "CastMethods");
    2656. +
    2657. /**
    2658. -
    2659. * @class This mixin provides the ability to access elements within a SQL array.
    2660. -
    2661. *
    2662. -
    2663. * @example
    2664. -
    2665. * sql.array.sqlSubscript(1) //=> array[1]
    2666. -
    2667. * sql.array.sqlSubscript(1, 2) //=> array[1, 2]
    2668. -
    2669. * sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
    2670. +
    2671. * @class Provides methods to assist in assigning a SQL type to
    2672. +
    2673. * particular types, i.e. Boolean, Function, Number or String.
    2674. *
    2675. -
    2676. * @name SubscriptMethods
    2677. +
    2678. * @name ComplexExpressionMethods
    2679. * @memberOf patio.sql
    2680. +
    2681. * @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.
    2682. +
    2683. * @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.
    2684. +
    2685. * @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.
    2686. +
    2687. * <pre class="code">
    2688. +
    2689. * sql.a.not("a") //=> NOT "a"
    2690. +
    2691. * sql.a.sqlNumber.not() //=> ~"a"
    2692. +
    2693. * </pre>
    2694. +
    2695. * @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.
    2696. +
    2697. * <pre class="code">
    2698. +
    2699. * sql.a.plus(sql.b); //=> "a" + "b"
    2700. +
    2701. * sql.a.sqlString.concat(sql.b) //=> "a" || "b"
    2702. +
    2703. * </pre>
    2704. */
    2705. -
    2706. 1var SubscriptMethods = define(null, {
    2707. +
    2708. 1var ComplexExpressionMethods = define(null, {
    2709. instance:{
    2710. +
    2711. /**@ignore*/
    2712. +
    2713. getters:{
    2714. -
    2715. /**
    2716. -
    2717. * Return a {@link patio.sql.Subscript} with the given arguments, representing an
    2718. -
    2719. * SQL array access.
    2720. -
    2721. *
    2722. -
    2723. * @example
    2724. -
    2725. * sql.array.sqlSubscript(1) //=> array[1]
    2726. -
    2727. * sql.array.sqlSubscript(1, 2) //=> array[1, 2]
    2728. -
    2729. * sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
    2730. -
    2731. *
    2732. -
    2733. * @param subscript
    2734. -
    2735. */
    2736. -
    2737. sqlSubscript:function (subscript) {
    2738. -
    2739. 108 var args = argsToArray(arguments);
    2740. -
    2741. 108 return new SubScript(this, flatten(args));
    2742. +
    2743. /**
    2744. +
    2745. * @ignore
    2746. +
    2747. */
    2748. +
    2749. sqlBoolean:function () {
    2750. +
    2751. 0 return new BooleanExpression("noop", this);
    2752. +
    2753. },
    2754. +
    2755. +
    2756. +
    2757. /**
    2758. +
    2759. * @ignore
    2760. +
    2761. */
    2762. +
    2763. sqlFunction:function () {
    2764. +
    2765. 13 return new SQLFunction(this);
    2766. +
    2767. },
    2768. +
    2769. +
    2770. +
    2771. /**
    2772. +
    2773. * @ignore
    2774. +
    2775. */
    2776. +
    2777. sqlNumber:function () {
    2778. +
    2779. 50 return new NumericExpression("noop", this);
    2780. +
    2781. },
    2782. +
    2783. +
    2784. /**
    2785. +
    2786. * @ignore
    2787. +
    2788. */
    2789. +
    2790. sqlString:function () {
    2791. +
    2792. 0 return new StringExpression("noop", this);
    2793. +
    2794. }
    2795. }
    2796. }
    2797. -
    2798. }).as(sql, "SubScriptMethods");
    2799. +
    2800. }).as(sql, "ComplexExpressionMethods");
    2801. +
    2802. 1var inequalityMethod = function (op) {
    2803. +
    2804. 6 return function (expression) {
    2805. +
    2806. 88 if (isInstanceOf(expression, BooleanExpression)
    2807. +
    2808. || isBoolean(expression)
    2809. +
    2810. || isNull(expression)
    2811. +
    2812. || (isHash(expression))
    2813. +
    2814. || isArray(expression)) {
    2815. +
    2816. 0 throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
    2817. +
    2818. } else {
    2819. +
    2820. 88 return new BooleanExpression(op, this, expression);
    2821. +
    2822. }
    2823. +
    2824. }
    2825. +
    2826. };
    2827. /**
    2828. -
    2829. * @class This is the parent of all expressions.
    2830. +
    2831. * @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
    2832. +
    2833. * used in a numeric or string context in SQL.
    2834. *
    2835. -
    2836. * @name Expression
    2837. +
    2838. * @example
    2839. +
    2840. * sql.a.gt("b") //=> a > "b"
    2841. +
    2842. * sql.a.lt("b") //=> a > "b"
    2843. +
    2844. * sql.a.gte("b") //=> a >= "b"
    2845. +
    2846. * sql.a.lte("b") //=> a <= "b"
    2847. +
    2848. * sql.a.eq("b") //=> a = "b"
    2849. +
    2850. *
    2851. +
    2852. * @name InequalityMethods
    2853. * @memberOf patio.sql
    2854. */
    2855. -
    2856. 1var Expression = define(null, {
    2857. -
    2858. +
    2859. 1var InequalityMethods = define(null, {
    2860. instance:{
    2861. -
    2862. /**@lends patio.sql.Expression.prototype*/
    2863. +
    2864. /**@lends patio.sql.InequalityMethods.prototype*/
    2865. /**
    2866. -
    2867. * Returns the string representation of this expression
    2868. +
    2869. * @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.
    2870. +
    2871. * @example
    2872. *
    2873. -
    2874. * @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.
    2875. -
    2876. * @return {String} a string literal version of this expression.
    2877. +
    2878. * sql.a.gt("b") //=> a > "b"
    2879. +
    2880. *
    2881. +
    2882. * @return {patio.sql.BooleanExpression}
    2883. */
    2884. -
    2885. sqlLiteral:function (ds) {
    2886. -
    2887. 0 return this.toString(ds);
    2888. -
    2889. }
    2890. -
    2891. -
    2892. },
    2893. -
    2894. -
    2895. static:{
    2896. -
    2897. /**@lends patio.sql.Expression*/
    2898. -
    2899. +
    2900. gt:inequalityMethod("gt"),
    2901. /**
    2902. -
    2903. * This is a helper method that will take in an array of arguments and return an expression.
    2904. +
    2905. * @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.
    2906. *
    2907. * @example
    2908. *
    2909. -
    2910. * QualifiedIdentifier.fromArgs(["table", "column"]);
    2911. +
    2912. * sql.a.gte("b") //=> a >= "b"
    2913. *
    2914. -
    2915. * @param {*[]} args array of arguments to pass into the constructor of the function.
    2916. +
    2917. * @return {patio.sql.BooleanExpression}
    2918. +
    2919. */
    2920. +
    2921. gte:inequalityMethod("gte"),
    2922. +
    2923. /**
    2924. +
    2925. * @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.
    2926. *
    2927. -
    2928. * @return {patio.sql.Expression} an expression.
    2929. +
    2930. * @example
    2931. +
    2932. *
    2933. +
    2934. * sql.a.lt("b") //=> a < "b"
    2935. +
    2936. *
    2937. +
    2938. * @return {patio.sql.BooleanExpression}
    2939. */
    2940. -
    2941. fromArgs:function (args) {
    2942. -
    2943. 2410 var ret;
    2944. -
    2945. 2410 try {
    2946. -
    2947. 2410 ret = new this();
    2948. -
    2949. } catch (ignore) {
    2950. -
    2951. }
    2952. -
    2953. 2410 this.apply(ret, args);
    2954. -
    2955. 2410 return ret;
    2956. -
    2957. },
    2958. +
    2959. lt:inequalityMethod("lt"),
    2960. +
    2961. /**
    2962. +
    2963. * @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.
    2964. +
    2965. *
    2966. +
    2967. * @example
    2968. +
    2969. *
    2970. +
    2971. * sql.a.lte("b") //=> a <= "b"
    2972. +
    2973. *
    2974. +
    2975. * @return {patio.sql.BooleanExpression}
    2976. +
    2977. */
    2978. +
    2979. lte:inequalityMethod("lte"),
    2980. +
    2981. /**
    2982. +
    2983. * @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.
    2984. +
    2985. *
    2986. +
    2987. * @example
    2988. +
    2989. *
    2990. +
    2991. * sql.a.eq("b") //=> a = "b"
    2992. +
    2993. *
    2994. +
    2995. * @return {patio.sql.BooleanExpression}
    2996. +
    2997. */
    2998. +
    2999. eq:inequalityMethod("eq"),
    3000. +
    3001. +
    3002. neq:inequalityMethod("neq"),
    3003. /**
    3004. -
    3005. * Helper to determine if something is a condition specifier. Returns true if the object
    3006. -
    3007. * is a Hash or is an array of two element arrays.
    3008. +
    3009. * @private
    3010. +
    3011. *
    3012. +
    3013. * Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
    3014. *
    3015. * @example
    3016. -
    3017. * Expression.isConditionSpecifier({a : "b"}); //=> true
    3018. -
    3019. * Expression.isConditionSpecifier("a"); //=> false
    3020. -
    3021. * Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true
    3022. -
    3023. * Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false
    3024. *
    3025. -
    3026. * @param {*} obj object to test if it is a condition specifier
    3027. -
    3028. * @return {Boolean} true if the object is a Hash or is an array of two element arrays.
    3029. +
    3030. * sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))
    3031. +
    3032. * sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))
    3033. +
    3034. *
    3035. +
    3036. * @param {Object} items a two element array where the first element it the item to be gte and the second item lte.
    3037. +
    3038. *
    3039. +
    3040. * @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
    3041. */
    3042. -
    3043. isConditionSpecifier:function (obj) {
    3044. -
    3045. 22715 return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
    3046. -
    3047. 9896 return isArray(i) && i.length === 2;
    3048. -
    3049. }));
    3050. +
    3051. between:function (items) {
    3052. +
    3053. 6 return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))
    3054. }
    3055. }
    3056. -
    3057. -
    3058. }).as(sql, "Expression");
    3059. +
    3060. }).as(sql, "InequalityMethods");
    3061. /**
    3062. -
    3063. * @class Base class for all GenericExpressions
    3064. -
    3065. *
    3066. -
    3067. * @augments patio.sql.Expression
    3068. -
    3069. * @augments patio.sql.AliasMethods
    3070. -
    3071. * @augments patio.sql.BooleanMethods
    3072. -
    3073. * @augments patio.sql.CastMethods
    3074. -
    3075. * @augments patio.sql.ComplexExpressionMethods
    3076. -
    3077. * @augments patio.sql.InequalityMethods
    3078. -
    3079. * @augments patio.sql.NumericMethods
    3080. -
    3081. * @augments patio.sql.OrderedMethods
    3082. -
    3083. * @augments patio.sql.StringMethods
    3084. -
    3085. * @augments patio.sql.SubscriptMethods
    3086. +
    3087. * @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},
    3088. +
    3089. * so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}
    3090. +
    3091. * or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>
    3092. *
    3093. -
    3094. * @name GenericExpression
    3095. +
    3096. * @name NoBooleanInputMethods
    3097. * @memberOf patio.sql
    3098. */
    3099. -
    3100. 1var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");
    3101. -
    3102. -
    3103. -
    3104. 1var AliasedExpression = define(Expression, {
    3105. -
    3106. instance:{
    3107. -
    3108. /**@lends patio.sql.AliasedExpression.prototype*/
    3109. -
    3110. -
    3111. /**
    3112. -
    3113. * This class reperesents an Aliased Expression
    3114. -
    3115. *
    3116. -
    3117. * @constructs
    3118. -
    3119. * @augments patio.sql.Expression
    3120. -
    3121. *
    3122. -
    3123. * @param expression the expression to alias.
    3124. -
    3125. * @param alias the alias to alias the expression to.
    3126. -
    3127. *
    3128. -
    3129. * @property expression the expression being aliased
    3130. -
    3131. * @property alias the alias of the expression
    3132. -
    3133. *
    3134. -
    3135. */
    3136. -
    3137. constructor:function (expression, alias) {
    3138. -
    3139. 945 this.expression = expression;
    3140. -
    3141. 945 this.alias = alias;
    3142. -
    3143. },
    3144. +
    3145. 1var NoBooleanInputMethods = define(null, {
    3146. +
    3147. instance:{
    3148. +
    3149. constructor:function (op) {
    3150. +
    3151. 22 var args = argsToArray(arguments, 1);
    3152. +
    3153. 22 args.forEach(function (expression) {
    3154. +
    3155. 26 if ((isInstanceOf(expression, BooleanExpression))
    3156. +
    3157. || isBoolean(expression)
    3158. +
    3159. || isNull(expression)
    3160. +
    3161. || (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))
    3162. +
    3163. || isArray(expression)) {
    3164. +
    3165. 0 throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
    3166. +
    3167. }
    3168. +
    3169. });
    3170. +
    3171. 22 this._super(arguments);
    3172. +
    3173. }
    3174. +
    3175. }
    3176. +
    3177. }).as(sql, "NoBooleanInputMethods");
    3178. -
    3179. /**
    3180. -
    3181. * Converts the aliased expression to a string
    3182. -
    3183. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3184. -
    3185. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3186. -
    3187. *
    3188. -
    3189. * @return String the SQL alias fragment.
    3190. -
    3191. */
    3192. -
    3193. toString:function (ds) {
    3194. -
    3195. 877 !Dataset && (Dataset = require("./dataset"));
    3196. -
    3197. 877 ds = ds || new Dataset();
    3198. -
    3199. 877 return ds.aliasedExpressionSql(this);
    3200. -
    3201. }
    3202. +
    3203. 1var numericMethod = function (op) {
    3204. +
    3205. 4 return function (expression) {
    3206. +
    3207. 12 if (isInstanceOf(expression, BooleanExpression, StringExpression)) {
    3208. +
    3209. 0 throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
    3210. +
    3211. } else {
    3212. +
    3213. 12 return new NumericExpression(op, this, expression);
    3214. }
    3215. }
    3216. -
    3217. ).as(sql, "AliasedExpression");
    3218. +
    3219. };
    3220. -
    3221. 1var CaseExpression = define(GenericExpression, {
    3222. +
    3223. /**
    3224. +
    3225. * @class This mixin includes the standard mathematical methods (+, -, *, and /)
    3226. +
    3227. * that are defined on objects that can be used in a numeric context in SQL.
    3228. +
    3229. *
    3230. +
    3231. * @example
    3232. +
    3233. * sql.a.plus(sql.b) //=> "a" + "b"
    3234. +
    3235. * sql.a.minus(sql.b) //=> "a" - "b"
    3236. +
    3237. * sql.a.multiply(sql.b) //=> "a" * "b"
    3238. +
    3239. * sql.a.divide(sql.b) //=> "a" / "b"
    3240. +
    3241. *
    3242. +
    3243. * @name NumericMethods
    3244. +
    3245. * @memberOf patio.sql
    3246. +
    3247. */
    3248. +
    3249. 1var NumericMethods = define(null, {
    3250. instance:{
    3251. -
    3252. /**@lends patio.sql.CaseExpression.prototype*/
    3253. +
    3254. /**@lends patio.sql.NumericMethods.prototype*/
    3255. +
    3256. /**
    3257. -
    3258. * Create an object with the given conditions and
    3259. -
    3260. * default value. An expression can be provided to
    3261. -
    3262. * test each condition against, instead of having
    3263. -
    3264. * all conditions represent their own boolean expression.
    3265. +
    3266. * @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.
    3267. *
    3268. -
    3269. * @constructs
    3270. -
    3271. * @augments patio.sql.GenericExpression
    3272. -
    3273. * @param {Array|Object} conditions conditions to create the case expression from
    3274. -
    3275. * @param def default value
    3276. -
    3277. * @param expression expression to create the CASE expression from
    3278. +
    3279. * @example
    3280. *
    3281. -
    3282. * @property {Boolean} hasExpression returns true if this case expression has a expression
    3283. -
    3284. * @property conditions the conditions of the {@link patio.sql.CaseExpression}.
    3285. -
    3286. * @property def the default value of the {@link patio.sql.CaseExpression}.
    3287. -
    3288. * @property expression the expression of the {@link patio.sql.CaseExpression}.
    3289. -
    3290. * @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.
    3291. +
    3292. * sql.a.plus(sql.b) //=> "a" + "b"
    3293. +
    3294. *
    3295. +
    3296. * @return {patio.sql.NumericExpression}
    3297. */
    3298. -
    3299. constructor:function (conditions, def, expression) {
    3300. -
    3301. 8 if (Expression.isConditionSpecifier(conditions)) {
    3302. -
    3303. 4 this.conditions = toArray(conditions);
    3304. -
    3305. 4 this.def = def;
    3306. -
    3307. 4 this.expression = expression;
    3308. -
    3309. 4 this.noExpression = isUndefined(expression);
    3310. -
    3311. }
    3312. -
    3313. },
    3314. +
    3315. plus:numericMethod("plus"),
    3316. /**
    3317. -
    3318. * Converts the case expression to a string
    3319. -
    3320. *
    3321. -
    3322. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3323. -
    3324. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3325. +
    3326. * @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.
    3327. *
    3328. -
    3329. * @return String the SQL case expression fragment.
    3330. -
    3331. */
    3332. -
    3333. toString:function (ds) {
    3334. -
    3335. 2 !Dataset && (Dataset = require("./dataset"));
    3336. -
    3337. 2 ds = ds || new Dataset();
    3338. -
    3339. 2 return ds.caseExpressionSql(this);
    3340. -
    3341. },
    3342. -
    3343. -
    3344. /**@ignore*/
    3345. -
    3346. getters:{
    3347. -
    3348. /**@ignore*/
    3349. -
    3350. hasExpression:function () {
    3351. -
    3352. 2 return !this.noExpression;
    3353. -
    3354. }
    3355. -
    3356. }
    3357. -
    3358. }
    3359. -
    3360. }).as(sql, "CaseExpression");
    3361. -
    3362. -
    3363. -
    3364. 1var Cast = define(GenericExpression, {
    3365. -
    3366. instance:{
    3367. -
    3368. /**@lends patio.sql.Cast*/
    3369. +
    3370. * @example
    3371. +
    3372. *
    3373. +
    3374. * sql.a.minus(sql.b) //=> "a" - "b"
    3375. +
    3376. *
    3377. +
    3378. * @return {patio.sql.NumericExpression}
    3379. +
    3380. */
    3381. +
    3382. minus:numericMethod("minus"),
    3383. /**
    3384. -
    3385. * Represents a cast of an SQL expression to a specific type.
    3386. -
    3387. * @constructs
    3388. -
    3389. * @augments patio.sql.GenericExpression
    3390. +
    3391. * @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
    3392. *
    3393. -
    3394. * @param expr the expression to CAST.
    3395. -
    3396. * @param type the type to CAST the expression to.
    3397. +
    3398. * @example
    3399. *
    3400. -
    3401. * @property expr the expression to CAST.
    3402. -
    3403. * @property type the type to CAST the expression to.
    3404. +
    3405. * sql.a.divide(sql.b) //=> "a" / "b"
    3406. +
    3407. *
    3408. +
    3409. * @return {patio.sql.NumericExpression}
    3410. */
    3411. -
    3412. constructor:function (expr, type) {
    3413. -
    3414. 3 this.expr = expr;
    3415. -
    3416. 3 this.type = type;
    3417. -
    3418. },
    3419. +
    3420. divide:numericMethod("divide"),
    3421. /**
    3422. -
    3423. * Converts the cast expression to a string
    3424. +
    3425. * @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
    3426. *
    3427. -
    3428. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3429. -
    3430. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3431. +
    3432. * @example
    3433. *
    3434. -
    3435. * @return String the SQL cast expression fragment.
    3436. +
    3437. * sql.a.multiply(sql.b) //=> "a" * "b"
    3438. +
    3439. *
    3440. +
    3441. * @return {patio.sql.NumericExpression}
    3442. */
    3443. -
    3444. toString:function (ds) {
    3445. -
    3446. 2 !Dataset && (Dataset = require("./dataset"));
    3447. -
    3448. 2 ds = ds || new Dataset();
    3449. -
    3450. 2 return ds.castSql(this.expr, this.type);
    3451. -
    3452. }
    3453. +
    3454. multiply:numericMethod("multiply")
    3455. }
    3456. -
    3457. }).as(sql, "Cast");
    3458. +
    3459. }).as(sql, "NumericMethods");
    3460. -
    3461. 1var ColumnAll = define(Expression, {
    3462. +
    3463. /**
    3464. +
    3465. * @class This mixin provides ordering methods ("asc", "desc") to expression.
    3466. +
    3467. *
    3468. +
    3469. * @example
    3470. +
    3471. *
    3472. +
    3473. * sql.name.asc(); //=> name ASC
    3474. +
    3475. * sql.price.desc(); //=> price DESC
    3476. +
    3477. * sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
    3478. +
    3479. * sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
    3480. +
    3481. *
    3482. +
    3483. * @name OrderedMethods
    3484. +
    3485. * @memberOf patio.sql
    3486. +
    3487. */
    3488. +
    3489. 1var OrderedMethods = define(null, {
    3490. instance:{
    3491. -
    3492. /**@lends patio.sql.ColumnAll.prototype*/
    3493. +
    3494. /**@lends patio.sql.OrderedMethods.prototype*/
    3495. /**
    3496. -
    3497. * Represents all columns in a given table, table.* in SQL
    3498. -
    3499. * @constructs
    3500. -
    3501. *
    3502. -
    3503. * @augments patio.sql.Expression
    3504. +
    3505. * Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
    3506. *
    3507. -
    3508. * @param table the table this expression is for.
    3509. +
    3510. * @example
    3511. +
    3512. * sql.name.asc(); //=> name ASC
    3513. +
    3514. * sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
    3515. *
    3516. -
    3517. * @property table the table this all column expression represents.
    3518. +
    3519. * @param {Object} [options] options to use when sorting
    3520. +
    3521. * @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
    3522. +
    3523. * before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
    3524. +
    3525. * @return {patio.sql.OrderedExpression}
    3526. */
    3527. -
    3528. constructor:function (table) {
    3529. -
    3530. 20 this.table = table;
    3531. +
    3532. asc:function (options) {
    3533. +
    3534. 7 return new OrderedExpression(this, false, options);
    3535. },
    3536. /**
    3537. -
    3538. * Converts the ColumnAll expression to a string
    3539. +
    3540. * Mark the receiving SQL column as sorting in a descending fashion.
    3541. +
    3542. * @example
    3543. *
    3544. -
    3545. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3546. -
    3547. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3548. +
    3549. * sql.price.desc(); //=> price DESC
    3550. +
    3551. * sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
    3552. *
    3553. -
    3554. * @return String the SQL columnAll expression fragment.
    3555. +
    3556. * @param {Object} [options] options to use when sorting
    3557. +
    3558. * @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
    3559. +
    3560. * before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
    3561. +
    3562. * @return {patio.sql.OrderedExpression}
    3563. */
    3564. -
    3565. toString:function (ds) {
    3566. -
    3567. 19 !Dataset && (Dataset = require("./dataset"));
    3568. -
    3569. 19 ds = ds || new Dataset();
    3570. -
    3571. 19 return ds.columnAllSql(this);
    3572. +
    3573. desc:function (options) {
    3574. +
    3575. 26 return new OrderedExpression(this, true, options);
    3576. }
    3577. }
    3578. -
    3579. }).as(sql, "ColumnAll");
    3580. +
    3581. }).as(sql, "OrderedMethods");
    3582. -
    3583. 1var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {
    3584. +
    3585. +
    3586. /**
    3587. +
    3588. * @class This mixin provides methods related to qualifying expression.
    3589. +
    3590. *
    3591. +
    3592. * @example
    3593. +
    3594. *
    3595. +
    3596. * sql.column.qualify("table") //=> "table"."column"
    3597. +
    3598. * sql.table.qualify("schema") //=> "schema"."table"
    3599. +
    3600. * sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
    3601. +
    3602. *
    3603. +
    3604. * @name QualifyingMethods
    3605. +
    3606. * @memberOf patio.sql
    3607. +
    3608. */
    3609. +
    3610. 1var QualifyingMethods = define(null, {
    3611. instance:{
    3612. -
    3613. /**@lends patio.sql.ComplexExpression.prototype*/
    3614. +
    3615. /**@lends patio.sql.QualifyingMethods.prototype*/
    3616. /**
    3617. -
    3618. * Represents a complex SQL expression, with a given operator and one
    3619. -
    3620. * or more attributes (which may also be ComplexExpressions, forming
    3621. -
    3622. * a tree).
    3623. -
    3624. *
    3625. -
    3626. * This is an abstract class that is not that useful by itself. The
    3627. -
    3628. * subclasses @link patio.sql.BooleanExpression},
    3629. -
    3630. * {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should
    3631. -
    3632. * be used instead of this class directly.
    3633. +
    3634. * Qualify the receiver with the given qualifier (table for column/schema for table).
    3635. *
    3636. -
    3637. * @constructs
    3638. -
    3639. * @augments patio.sql.Expression
    3640. -
    3641. * @augments patio.sql.AliasMethods
    3642. -
    3643. * @augments patio.sql.CastMethods
    3644. -
    3645. * @augments patio.sql.OrderedMethods
    3646. -
    3647. * @augments patio.sql.SubscriptMethods
    3648. +
    3649. * @example
    3650. +
    3651. * sql.column.qualify("table") //=> "table"."column"
    3652. +
    3653. * sql.table.qualify("schema") //=> "schema"."table"
    3654. +
    3655. * sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
    3656. *
    3657. -
    3658. * @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.
    3659. -
    3660. * @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.
    3661. +
    3662. * @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.
    3663. *
    3664. -
    3665. * @param {...} op The operator and arguments for this object to the ones given.
    3666. -
    3667. * <p>
    3668. -
    3669. * Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,
    3670. -
    3671. * other than the second arg for an IN/NOT IN operator.</li>
    3672. -
    3673. * </p>
    3674. +
    3675. * @return {patio.sql.QualifiedIdentifier}
    3676. */
    3677. -
    3678. constructor:function (op) {
    3679. -
    3680. 7952 if (op) {
    3681. -
    3682. 7164 var args = argsToArray(arguments,1 );
    3683. -
    3684. //make a copy of the args
    3685. -
    3686. 7164 var origArgs = args.slice(0);
    3687. -
    3688. 7164 args.forEach(function (a, i) {
    3689. -
    3690. 14676 if (Expression.isConditionSpecifier(a)) {
    3691. -
    3692. 6 args[i] = BooleanExpression.fromValuePairs(a);
    3693. -
    3694. }
    3695. -
    3696. });
    3697. -
    3698. 7164 op = op.toUpperCase();
    3699. -
    3700. -
    3701. 7164 if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
    3702. -
    3703. 1164 if (args.length < 1) {
    3704. -
    3705. 0 throw new ExpressionError("The " + op + " operator requires at least 1 argument")
    3706. -
    3707. }
    3708. -
    3709. 1164 var oldArgs = args.slice(0);
    3710. -
    3711. 1164 args = [];
    3712. -
    3713. 1164 oldArgs.forEach(function (a) {
    3714. -
    3715. 2739 a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
    3716. -
    3717. });
    3718. -
    3719. -
    3720. 6000 } else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
    3721. -
    3722. 5937 if (args.length != 2) {
    3723. -
    3724. 0 throw new ExpressionError("The " + op + " operator requires precisely 2 arguments");
    3725. -
    3726. }
    3727. -
    3728. //With IN/NOT IN, even if the second argument is an array of two element arrays,
    3729. -
    3730. //don't convert it into a boolean expression, since it's definitely being used
    3731. -
    3732. //as a value list.
    3733. -
    3734. 5937 if (IN_OPERATORS[op]) {
    3735. -
    3736. 23 args[1] = origArgs[1]
    3737. -
    3738. }
    3739. -
    3740. 63 } else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
    3741. -
    3742. 63 if (args.length != 1) {
    3743. -
    3744. 0 throw new ExpressionError("The " + op + " operator requires only one argument");
    3745. -
    3746. }
    3747. -
    3748. } else {
    3749. -
    3750. 0 throw new ExpressionError("Invalid operator " + op);
    3751. -
    3752. }
    3753. -
    3754. 7164 this.op = op;
    3755. -
    3756. 7164 this.args = args;
    3757. -
    3758. }
    3759. +
    3760. qualify:function (qualifier) {
    3761. +
    3762. 576 return new QualifiedIdentifier(qualifier, this);
    3763. },
    3764. /**
    3765. -
    3766. * Converts the ComplexExpression to a string.
    3767. +
    3768. * Use to create a .* expression.
    3769. *
    3770. -
    3771. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3772. -
    3773. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3774. +
    3775. * @example
    3776. +
    3777. * sql.table.all() //=> "table".*
    3778. +
    3779. * sql.table.qualify("schema").all() //=> "schema"."table".*
    3780. *
    3781. -
    3782. * @return String the SQL version of the {@link patio.sql.ComplexExpression}.
    3783. +
    3784. *
    3785. +
    3786. * @return {patio.sql.ColumnAll}
    3787. */
    3788. -
    3789. toString:function (ds) {
    3790. -
    3791. 6761 !Dataset && (Dataset = require("./dataset"));
    3792. -
    3793. 6761 ds = ds || new Dataset();
    3794. -
    3795. 6761 return ds.complexExpressionSql(this.op, this.args);
    3796. +
    3797. all:function () {
    3798. +
    3799. 3 return new ColumnAll(this);
    3800. }
    3801. -
    3802. },
    3803. -
    3804. static:{
    3805. -
    3806. /**@lends patio.sql.ComplexExpression*/
    3807. -
    3808. /**
    3809. -
    3810. * Hash of operator inversions
    3811. -
    3812. * @type Object
    3813. -
    3814. * @default {
    3815. -
    3816. * AND:"OR",
    3817. -
    3818. * OR:"AND",
    3819. -
    3820. * GT:"lte",
    3821. -
    3822. * GTE:"lt",
    3823. -
    3824. * LT:"gte",
    3825. -
    3826. * LTE:"gt",
    3827. -
    3828. * EQ:"neq",
    3829. -
    3830. * NEQ:"eq",
    3831. -
    3832. * LIKE:'NOT LIKE',
    3833. -
    3834. * "NOT LIKE":"LIKE",
    3835. -
    3836. * '!~*':'~*',
    3837. -
    3838. * '~*':'!~*',
    3839. -
    3840. * "~":'!~',
    3841. -
    3842. * "IN":'NOTIN',
    3843. -
    3844. * "NOTIN":"IN",
    3845. -
    3846. * "IS":'IS NOT',
    3847. -
    3848. * "ISNOT":"IS",
    3849. -
    3850. * NOT:"NOOP",
    3851. -
    3852. * NOOP:"NOT",
    3853. -
    3854. * ILIKE:'NOT ILIKE',
    3855. -
    3856. * NOTILIKE:"ILIKE"
    3857. -
    3858. * }
    3859. -
    3860. */
    3861. -
    3862. OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,
    3863. +
    3864. }
    3865. +
    3866. }).as(sql, "QualifyingMethods");
    3867. +
    3868. +
    3869. +
    3870. /**
    3871. +
    3872. * @class This mixin provides SQL string methods such as (like and iLike).
    3873. +
    3874. *
    3875. +
    3876. * @example
    3877. +
    3878. *
    3879. +
    3880. * sql.a.like("A%"); //=> "a" LIKE 'A%'
    3881. +
    3882. * sql.a.iLike("A%"); //=> "a" LIKE 'A%'
    3883. +
    3884. * sql.a.like(/^a/); //=> "a" ~* '^a'
    3885. +
    3886. *
    3887. +
    3888. * @name StringMethods
    3889. +
    3890. * @memberOf patio.sql
    3891. +
    3892. */
    3893. +
    3894. 1var StringMethods = define(null, {
    3895. +
    3896. instance:{
    3897. +
    3898. /**@lends patio.sql.StringMethods.prototype*/
    3899. /**
    3900. -
    3901. * Default mathematical operators.
    3902. +
    3903. * Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver
    3904. +
    3905. * with the given patterns. See {@link patio.sql.StringExpression#like}.
    3906. +
    3907. *
    3908. +
    3909. * @example
    3910. +
    3911. * sql.a.iLike("A%"); //=> "a" LIKE 'A%'
    3912. *
    3913. -
    3914. * @type Object
    3915. -
    3916. * @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}
    3917. +
    3918. * @return {patio.sql.BooleanExpression}
    3919. */
    3920. -
    3921. MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,
    3922. +
    3923. ilike:function (expression) {
    3924. +
    3925. 310 expression = argsToArray(arguments);
    3926. +
    3927. 310 return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([
    3928. +
    3929. {caseInsensitive:true}
    3930. +
    3931. ]));
    3932. +
    3933. },
    3934. /**
    3935. -
    3936. * Default bitwise operators.
    3937. +
    3938. * Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with
    3939. +
    3940. * the given patterns. See {@link patio.sql.StringExpression#like}.
    3941. *
    3942. -
    3943. * @type Object
    3944. -
    3945. * @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}
    3946. -
    3947. */
    3948. -
    3949. BITWISE_OPERATORS:BITWISE_OPERATORS,
    3950. -
    3951. /**
    3952. -
    3953. * Default inequality operators.
    3954. +
    3955. * @example
    3956. +
    3957. * sql.a.like(/^a/); //=> "a" ~* '^a'
    3958. +
    3959. * sql.a.like("A%"); //=> "a" LIKE 'A%'
    3960. *
    3961. -
    3962. * @type Object
    3963. -
    3964. * @default {GT:">",GTE:">=",LT:"<",LTE:"<="}
    3965. +
    3966. * @param expression
    3967. */
    3968. -
    3969. INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,
    3970. +
    3971. like:function (expression) {
    3972. +
    3973. 11 expression = argsToArray(arguments);
    3974. +
    3975. 11 return StringExpression.like.apply(StringExpression, [this].concat(expression));
    3976. +
    3977. }
    3978. +
    3979. }
    3980. +
    3981. }).as(sql, "StringMethods");
    3982. +
    3983. +
    3984. /**
    3985. +
    3986. * @class This mixin provides string concatenation methods ("concat");
    3987. +
    3988. *
    3989. +
    3990. * @example
    3991. +
    3992. *
    3993. +
    3994. * sql.x.sqlString.concat("y"); //=> "x" || "y"
    3995. +
    3996. *
    3997. +
    3998. * @name StringConcatenationMethods
    3999. +
    4000. * @memberOf patio.sql
    4001. +
    4002. */
    4003. +
    4004. 1var StringConcatenationMethods = define(null, {
    4005. +
    4006. instance:{
    4007. +
    4008. /**@lends patio.sql.StringConcatenationMethods.prototype*/
    4009. /**
    4010. -
    4011. * Default boolean operators.
    4012. +
    4013. * Return a {@link patio.sql.StringExpression} representing the concatenation of this expression
    4014. +
    4015. * with the given argument.
    4016. *
    4017. -
    4018. * @type Object
    4019. -
    4020. * @default {AND:"AND",OR:"OR"}
    4021. +
    4022. * @example
    4023. +
    4024. *
    4025. +
    4026. * sql.x.sqlString.concat("y"); //=> "x" || "y"
    4027. +
    4028. *
    4029. +
    4030. * @param expression expression to concatenate this expression with.
    4031. */
    4032. -
    4033. BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,
    4034. +
    4035. concat:function (expression) {
    4036. +
    4037. 0 return new StringExpression("||", this, expression);
    4038. +
    4039. }
    4040. +
    4041. }
    4042. +
    4043. }).as(sql, "StringConcatenationMethods");
    4044. +
    4045. +
    4046. /**
    4047. +
    4048. * @class This mixin provides the ability to access elements within a SQL array.
    4049. +
    4050. *
    4051. +
    4052. * @example
    4053. +
    4054. * sql.array.sqlSubscript(1) //=> array[1]
    4055. +
    4056. * sql.array.sqlSubscript(1, 2) //=> array[1, 2]
    4057. +
    4058. * sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
    4059. +
    4060. *
    4061. +
    4062. * @name SubscriptMethods
    4063. +
    4064. * @memberOf patio.sql
    4065. +
    4066. */
    4067. +
    4068. 1var SubscriptMethods = define(null, {
    4069. +
    4070. instance:{
    4071. /**
    4072. -
    4073. * Default IN operators.
    4074. +
    4075. * Return a {@link patio.sql.Subscript} with the given arguments, representing an
    4076. +
    4077. * SQL array access.
    4078. *
    4079. -
    4080. * @type Object
    4081. -
    4082. * @default {IN:"IN",NOTIN:'NOT IN'}
    4083. -
    4084. */
    4085. -
    4086. IN_OPERATORS:IN_OPERATORS,
    4087. -
    4088. /**
    4089. -
    4090. * Default IS operators.
    4091. +
    4092. * @example
    4093. +
    4094. * sql.array.sqlSubscript(1) //=> array[1]
    4095. +
    4096. * sql.array.sqlSubscript(1, 2) //=> array[1, 2]
    4097. +
    4098. * sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
    4099. *
    4100. -
    4101. * @type Object
    4102. -
    4103. * @default {IS:"IS", ISNOT:'IS NOT'}
    4104. +
    4105. * @param subscript
    4106. */
    4107. -
    4108. IS_OPERATORS:IS_OPERATORS,
    4109. +
    4110. sqlSubscript:function (subscript) {
    4111. +
    4112. 108 var args = argsToArray(arguments);
    4113. +
    4114. 108 return new SubScript(this, flatten(args));
    4115. +
    4116. }
    4117. +
    4118. }
    4119. +
    4120. }).as(sql, "SubScriptMethods");
    4121. +
    4122. +
    4123. +
    4124. /**
    4125. +
    4126. * @class This is the parent of all expressions.
    4127. +
    4128. *
    4129. +
    4130. * @name Expression
    4131. +
    4132. * @memberOf patio.sql
    4133. +
    4134. */
    4135. +
    4136. 1var Expression = define(null, {
    4137. +
    4138. +
    4139. instance:{
    4140. +
    4141. /**@lends patio.sql.Expression.prototype*/
    4142. +
    4143. /**
    4144. -
    4145. * Default two arity operators.
    4146. +
    4147. * Returns the string representation of this expression
    4148. *
    4149. -
    4150. * @type Object
    4151. -
    4152. * @default {
    4153. -
    4154. * EQ:'=',
    4155. -
    4156. * NEQ:'!=', LIKE:"LIKE",
    4157. -
    4158. * "NOT LIKE":'NOT LIKE',
    4159. -
    4160. * ILIKE:"ILIKE",
    4161. -
    4162. * "NOT ILIKE":'NOT ILIKE',
    4163. -
    4164. * "~":"~",
    4165. -
    4166. * '!~':"!~",
    4167. -
    4168. * '~*':"~*",
    4169. -
    4170. * '!~*':"!~*",
    4171. -
    4172. * GT:">",
    4173. -
    4174. * GTE:">=",
    4175. -
    4176. * LT:"<",
    4177. -
    4178. * LTE:"<=",
    4179. -
    4180. * bitWiseAnd:"&",
    4181. -
    4182. * bitWiseOr:"|",
    4183. -
    4184. * exclusiveOr:"^",
    4185. -
    4186. * leftShift:"<<",
    4187. -
    4188. * rightShift:">>",
    4189. -
    4190. * IS:"IS",
    4191. -
    4192. * ISNOT:'IS NOT',
    4193. -
    4194. * IN:"IN",
    4195. -
    4196. * NOTIN:'NOT IN'
    4197. -
    4198. * }
    4199. +
    4200. * @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.
    4201. +
    4202. * @return {String} a string literal version of this expression.
    4203. */
    4204. -
    4205. TWO_ARITY_OPERATORS:TWO_ARITY_OPERATORS,
    4206. +
    4207. sqlLiteral:function (ds) {
    4208. +
    4209. 0 return this.toString(ds);
    4210. +
    4211. }
    4212. +
    4213. +
    4214. },
    4215. +
    4216. +
    4217. static:{
    4218. +
    4219. /**@lends patio.sql.Expression*/
    4220. /**
    4221. -
    4222. * Default N(multi) arity operators.
    4223. +
    4224. * This is a helper method that will take in an array of arguments and return an expression.
    4225. *
    4226. -
    4227. * @type Object
    4228. -
    4229. * @default {
    4230. -
    4231. * "||":"||",
    4232. -
    4233. * AND:"AND",
    4234. -
    4235. * OR:"OR",
    4236. -
    4237. * PLUS:"+",
    4238. -
    4239. * MINUS:"-",
    4240. -
    4241. * DIVIDE:"/", MULTIPLY:"*"
    4242. -
    4243. * }
    4244. +
    4245. * @example
    4246. +
    4247. *
    4248. +
    4249. * QualifiedIdentifier.fromArgs(["table", "column"]);
    4250. +
    4251. *
    4252. +
    4253. * @param {*[]} args array of arguments to pass into the constructor of the function.
    4254. +
    4255. *
    4256. +
    4257. * @return {patio.sql.Expression} an expression.
    4258. */
    4259. -
    4260. N_ARITY_OPERATORS:N_ARITY_OPERATORS,
    4261. +
    4262. fromArgs:function (args) {
    4263. +
    4264. 2216 var ret;
    4265. +
    4266. 2216 try {
    4267. +
    4268. 2216 ret = new this();
    4269. +
    4270. } catch (ignore) {
    4271. +
    4272. }
    4273. +
    4274. 2216 this.apply(ret, args);
    4275. +
    4276. 2216 return ret;
    4277. +
    4278. },
    4279. /**
    4280. -
    4281. * Default ONE operators.
    4282. +
    4283. * Helper to determine if something is a condition specifier. Returns true if the object
    4284. +
    4285. * is a Hash or is an array of two element arrays.
    4286. *
    4287. -
    4288. * @type Object
    4289. -
    4290. * @default {
    4291. -
    4292. * "NOT":"NOT",
    4293. -
    4294. * "NOOP":"NOOP"
    4295. -
    4296. * }
    4297. +
    4298. * @example
    4299. +
    4300. * Expression.isConditionSpecifier({a : "b"}); //=> true
    4301. +
    4302. * Expression.isConditionSpecifier("a"); //=> false
    4303. +
    4304. * Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true
    4305. +
    4306. * Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false
    4307. +
    4308. *
    4309. +
    4310. * @param {*} obj object to test if it is a condition specifier
    4311. +
    4312. * @return {Boolean} true if the object is a Hash or is an array of two element arrays.
    4313. */
    4314. -
    4315. ONE_ARITY_OPERATORS:ONE_ARITY_OPERATORS
    4316. +
    4317. isConditionSpecifier:function (obj) {
    4318. +
    4319. 22715 return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
    4320. +
    4321. 9896 return isArray(i) && i.length === 2;
    4322. +
    4323. }));
    4324. +
    4325. }
    4326. }
    4327. -
    4328. }).as(sql, "ComplexExpression");
    4329. +
    4330. +
    4331. }).as(sql, "Expression");
    4332. +
    4333. +
    4334. /**
    4335. +
    4336. * @class Base class for all GenericExpressions
    4337. +
    4338. *
    4339. +
    4340. * @augments patio.sql.Expression
    4341. +
    4342. * @augments patio.sql.AliasMethods
    4343. +
    4344. * @augments patio.sql.BooleanMethods
    4345. +
    4346. * @augments patio.sql.CastMethods
    4347. +
    4348. * @augments patio.sql.ComplexExpressionMethods
    4349. +
    4350. * @augments patio.sql.InequalityMethods
    4351. +
    4352. * @augments patio.sql.NumericMethods
    4353. +
    4354. * @augments patio.sql.OrderedMethods
    4355. +
    4356. * @augments patio.sql.StringMethods
    4357. +
    4358. * @augments patio.sql.SubscriptMethods
    4359. +
    4360. *
    4361. +
    4362. * @name GenericExpression
    4363. +
    4364. * @memberOf patio.sql
    4365. +
    4366. */
    4367. +
    4368. 1var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");
    4369. +
    4370. +
    4371. +
    4372. 1var AliasedExpression = define(Expression, {
    4373. +
    4374. instance:{
    4375. +
    4376. /**@lends patio.sql.AliasedExpression.prototype*/
    4377. +
    4378. +
    4379. /**
    4380. +
    4381. * This class reperesents an Aliased Expression
    4382. +
    4383. *
    4384. +
    4385. * @constructs
    4386. +
    4387. * @augments patio.sql.Expression
    4388. +
    4389. *
    4390. +
    4391. * @param expression the expression to alias.
    4392. +
    4393. * @param alias the alias to alias the expression to.
    4394. +
    4395. *
    4396. +
    4397. * @property expression the expression being aliased
    4398. +
    4399. * @property alias the alias of the expression
    4400. +
    4401. *
    4402. +
    4403. */
    4404. +
    4405. constructor:function (expression, alias) {
    4406. +
    4407. 945 this.expression = expression;
    4408. +
    4409. 945 this.alias = alias;
    4410. +
    4411. },
    4412. +
    4413. +
    4414. /**
    4415. +
    4416. * Converts the aliased expression to a string
    4417. +
    4418. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    4419. +
    4420. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    4421. +
    4422. *
    4423. +
    4424. * @return String the SQL alias fragment.
    4425. +
    4426. */
    4427. +
    4428. toString:function (ds) {
    4429. +
    4430. 877 !Dataset && (Dataset = require("./dataset"));
    4431. +
    4432. 877 ds = ds || new Dataset();
    4433. +
    4434. 877 return ds.aliasedExpressionSql(this);
    4435. +
    4436. }
    4437. +
    4438. }
    4439. +
    4440. }
    4441. +
    4442. ).as(sql, "AliasedExpression");
    4443. -
    4444. /**
    4445. -
    4446. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    4447. -
    4448. * in a boolean value in SQL.
    4449. -
    4450. *
    4451. -
    4452. * @augments patio.sql.ComplexExpression
    4453. -
    4454. * @augments patio.sql.BooleanMethods
    4455. -
    4456. * @name BooleanExpression
    4457. -
    4458. * @memberOf patio.sql
    4459. -
    4460. */
    4461. -
    4462. 1var BooleanExpression = define([ComplexExpression, BooleanMethods], {
    4463. -
    4464. static:{
    4465. -
    4466. /**@lends patio.sql.BooleanExpression*/
    4467. +
    4468. 1var CaseExpression = define(GenericExpression, {
    4469. +
    4470. instance:{
    4471. +
    4472. /**@lends patio.sql.CaseExpression.prototype*/
    4473. /**
    4474. -
    4475. * Invert the expression, if possible. If the expression cannot
    4476. -
    4477. * be inverted, it throws an {@link patio.error.ExpressionError}. An inverted expression should match
    4478. -
    4479. * everything that the uninverted expression did not match, and vice-versa, except for possible issues with
    4480. -
    4481. * SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
    4482. -
    4483. *
    4484. -
    4485. * @example
    4486. -
    4487. * BooleanExpression.invert(sql.a) //=> NOT "a"
    4488. +
    4489. * Create an object with the given conditions and
    4490. +
    4491. * default value. An expression can be provided to
    4492. +
    4493. * test each condition against, instead of having
    4494. +
    4495. * all conditions represent their own boolean expression.
    4496. *
    4497. -
    4498. * @param {patio.sql.BooleanExpression} expression
    4499. -
    4500. * the expression to invert.
    4501. +
    4502. * @constructs
    4503. +
    4504. * @augments patio.sql.GenericExpression
    4505. +
    4506. * @param {Array|Object} conditions conditions to create the case expression from
    4507. +
    4508. * @param def default value
    4509. +
    4510. * @param expression expression to create the CASE expression from
    4511. *
    4512. -
    4513. * @return {patio.sql.BooleanExpression} the inverted expression.
    4514. +
    4515. * @property {Boolean} hasExpression returns true if this case expression has a expression
    4516. +
    4517. * @property conditions the conditions of the {@link patio.sql.CaseExpression}.
    4518. +
    4519. * @property def the default value of the {@link patio.sql.CaseExpression}.
    4520. +
    4521. * @property expression the expression of the {@link patio.sql.CaseExpression}.
    4522. +
    4523. * @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.
    4524. */
    4525. -
    4526. invert:function (expression) {
    4527. -
    4528. 95 if (isInstanceOf(expression, BooleanExpression)) {
    4529. -
    4530. 90 var op = expression.op, newArgs;
    4531. -
    4532. 90 if (op == "AND" || op == "OR") {
    4533. -
    4534. 3 newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args.map(function (arg) {
    4535. -
    4536. 6 return BooleanExpression.invert(arg);
    4537. -
    4538. }));
    4539. -
    4540. 3 return BooleanExpression.fromArgs(newArgs);
    4541. -
    4542. } else {
    4543. -
    4544. 87 newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);
    4545. -
    4546. 87 return BooleanExpression.fromArgs(newArgs);
    4547. -
    4548. }
    4549. -
    4550. 5 } else if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
    4551. -
    4552. 0 throw new ExpressionError(format("Cannot invert %4j", [expression]));
    4553. -
    4554. } else {
    4555. -
    4556. 5 return new BooleanExpression("NOT", expression);
    4557. +
    4558. constructor:function (conditions, def, expression) {
    4559. +
    4560. 8 if (Expression.isConditionSpecifier(conditions)) {
    4561. +
    4562. 4 this.conditions = toArray(conditions);
    4563. +
    4564. 4 this.def = def;
    4565. +
    4566. 4 this.expression = expression;
    4567. +
    4568. 4 this.noExpression = isUndefined(expression);
    4569. }
    4570. },
    4571. +
    4572. /**
    4573. -
    4574. * Take pairs of values (e.g. a hash or array of two element arrays)
    4575. -
    4576. * and converts it to a {@link patio.sql.BooleanExpression}. The operator and args
    4577. -
    4578. * used depends on the case of the right (2nd) argument:
    4579. +
    4580. * Converts the case expression to a string
    4581. *
    4582. -
    4583. * <pre class='code'>
    4584. -
    4585. * BooleanExpression.fromValuePairs({a : [1,2,3]}) //=> a IN (1,2,3)
    4586. -
    4587. * BooleanExpression.fromValuePairs({a : true}); // a IS TRUE;
    4588. -
    4589. * BooleanExpression.fromValuePairs({a : /^A/i}); // a *~ '^A'
    4590. -
    4591. * </pre>
    4592. +
    4593. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    4594. +
    4595. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    4596. *
    4597. -
    4598. * If multiple arguments are given, they are joined with the op given (AND
    4599. -
    4600. * by default, OR possible). If negate is set to true,
    4601. -
    4602. * all subexpressions are inverted before used.
    4603. -
    4604. * <pre class='code'>
    4605. -
    4606. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}) //=> a IN (1,2,3) AND b IS TRUE
    4607. -
    4608. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
    4609. -
    4610. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
    4611. -
    4612. * </pre>
    4613. -
    4614. * @param {Object} a object to convert to a {@link patio.sql.BooleanExpression}
    4615. -
    4616. * @param {String} [op="AND"] Boolean operator to join each subexpression with.
    4617. -
    4618. * <pre class="code">
    4619. -
    4620. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
    4621. -
    4622. * </pre>
    4623. -
    4624. * @param {Boolean} [negate=false] set to try to invert the {@link patio.sql.BooleanExpression}.
    4625. -
    4626. * <pre class="code">
    4627. -
    4628. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
    4629. -
    4630. * </pre>
    4631. -
    4632. * @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
    4633. +
    4634. * @return String the SQL case expression fragment.
    4635. */
    4636. -
    4637. fromValuePairs:function (a, op, negate) {
    4638. -
    4639. 7610 !Dataset && (Dataset = require("./dataset"));
    4640. -
    4641. 7610 op = op || "AND", negate = negate || false;
    4642. -
    4643. 7610 var pairArr = [];
    4644. -
    4645. 7610 var isArr = isArray(a) && Expression.isConditionSpecifier(a);
    4646. -
    4647. 7610 if (isHash(a)) {
    4648. -
    4649. 3385 pairArr.push(this.__filterObject(a));
    4650. -
    4651. } else {
    4652. -
    4653. 4225 for (var k in a) {
    4654. -
    4655. 5203 var v = isArr ? a[k][1] : a[k], ret;
    4656. -
    4657. 5203 k = isArr ? a[k][0] : k;
    4658. -
    4659. 5203 if (isArray(v) || isInstanceOf(v, Dataset)) {
    4660. -
    4661. 17 k = isArray(k) ? k.map(function (i) {
    4662. -
    4663. 12 return isString(i) ? sql.stringToIdentifier(i) : i
    4664. -
    4665. }) : isString(k) ? sql.stringToIdentifier(k) : k;
    4666. -
    4667. 17 ret = new BooleanExpression("IN", k, v);
    4668. -
    4669. 5186 } else if (isInstanceOf(v, NegativeBooleanConstant)) {
    4670. -
    4671. 0 ret = new BooleanExpression("ISNOT", k, v.constant);
    4672. -
    4673. 5186 } else if (isInstanceOf(v, BooleanConstant)) {
    4674. -
    4675. 0 ret = new BooleanExpression("IS", k, v.constant);
    4676. -
    4677. 5186 } else if (isNull(v) || isBoolean(v)) {
    4678. -
    4679. 240 ret = new BooleanExpression("IS", k, v);
    4680. -
    4681. 4946 } else if (isHash(v)) {
    4682. -
    4683. 0 ret = BooleanExpression.__filterObject(v, k);
    4684. -
    4685. 4946 } else if (isRegExp(v)) {
    4686. -
    4687. 69 ret = StringExpression.like(sql.stringToIdentifier(k), v);
    4688. -
    4689. } else {
    4690. -
    4691. 4877 ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
    4692. -
    4693. }
    4694. -
    4695. 5203 negate && (ret = BooleanExpression.invert(ret));
    4696. -
    4697. 5203 pairArr.push(ret);
    4698. -
    4699. }
    4700. -
    4701. }
    4702. -
    4703. //if We just have one then return the first otherwise create a new Boolean expression
    4704. -
    4705. 7610 return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
    4706. +
    4707. toString:function (ds) {
    4708. +
    4709. 2 !Dataset && (Dataset = require("./dataset"));
    4710. +
    4711. 2 ds = ds || new Dataset();
    4712. +
    4713. 2 return ds.caseExpressionSql(this);
    4714. },
    4715. +
    4716. /**@ignore*/
    4717. +
    4718. getters:{
    4719. +
    4720. /**@ignore*/
    4721. +
    4722. hasExpression:function () {
    4723. +
    4724. 2 return !this.noExpression;
    4725. +
    4726. }
    4727. +
    4728. }
    4729. +
    4730. }
    4731. +
    4732. }).as(sql, "CaseExpression");
    4733. +
    4734. +
    4735. +
    4736. 1var Cast = define(GenericExpression, {
    4737. +
    4738. instance:{
    4739. +
    4740. /**@lends patio.sql.Cast*/
    4741. +
    4742. /**
    4743. -
    4744. * @private
    4745. -
    4746. *
    4747. -
    4748. * This builds an expression from a hash
    4749. +
    4750. * Represents a cast of an SQL expression to a specific type.
    4751. +
    4752. * @constructs
    4753. +
    4754. * @augments patio.sql.GenericExpression
    4755. *
    4756. -
    4757. * @example
    4758. +
    4759. * @param expr the expression to CAST.
    4760. +
    4761. * @param type the type to CAST the expression to.
    4762. *
    4763. -
    4764. * Dataset._filterObject({a : 1}) //=> WHERE (a = 1)
    4765. -
    4766. * Dataset._filterObject({x : {gt : 1}}) //=> WHERE (x > 1)
    4767. -
    4768. * Dataset._filterObject({x : {gt : 1}, a : 1}) //=> WHERE ((x > 1) AND (a = 1))
    4769. -
    4770. * Dataset._filterObject({x : {like : "name"}}) //=> WHERE (x LIKE 'name')
    4771. -
    4772. * Dataset._filterObject({x : {iLike : "name"}}) //=> WHERE (x LIKE 'name')
    4773. -
    4774. * Dataset._filterObject({x : {between : [1,10]}}) //=> WHERE ((x >= 1) AND (x <= 10))
    4775. -
    4776. * Dataset._filterObject({x : {notBetween : [1,10]}}) //=> WHERE ((x < 1) OR (x > 10))
    4777. -
    4778. * Dataset._filterObject({x : {neq : 1}}) //=> WHERE (x != 1)
    4779. +
    4780. * @property expr the expression to CAST.
    4781. +
    4782. * @property type the type to CAST the expression to.
    4783. +
    4784. */
    4785. +
    4786. constructor:function (expr, type) {
    4787. +
    4788. 3 this.expr = expr;
    4789. +
    4790. 3 this.type = type;
    4791. +
    4792. },
    4793. +
    4794. +
    4795. /**
    4796. +
    4797. * Converts the cast expression to a string
    4798. *
    4799. -
    4800. * @param {Object} expr the expression we need to create an expression out of
    4801. -
    4802. * @param {String} [key=null] the key that the hash corresponds to
    4803. +
    4804. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    4805. +
    4806. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    4807. *
    4808. -
    4809. * @return {patio.sql.Expression} an expression to use in the filter
    4810. +
    4811. * @return String the SQL cast expression fragment.
    4812. */
    4813. -
    4814. __filterObject:function (expr, key) {
    4815. -
    4816. 3502 var pairs = [], opts, newKey;
    4817. -
    4818. 3502 var twoArityOperators = this.TWO_ARITY_OPERATORS;
    4819. -
    4820. 3502 for (var k in expr) {
    4821. -
    4822. 3541 var v = expr[k];
    4823. -
    4824. 3541 if (isHash(v)) { //its a hash too filter it too!
    4825. -
    4826. 115 pairs.push(this.__filterObject(v, k));
    4827. -
    4828. 3426 } else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
    4829. -
    4830. //its a two arrity operator (e.g. '=', '>')
    4831. -
    4832. 118 newKey = isString(key) ? key.split(",") : [key];
    4833. -
    4834. 118 if (newKey.length > 1) {
    4835. -
    4836. //this represents a hash where the key represents two columns
    4837. -
    4838. //(e.g. {"col1,col2" : 1}) => WHERE (col1 = 1 AND col2 = 1)
    4839. -
    4840. 1 pairs = pairs.concat(newKey.map(function (k) {
    4841. -
    4842. //filter each column with the expression
    4843. -
    4844. 2 return this.__filterObject(expr, k);
    4845. -
    4846. }, this));
    4847. -
    4848. } else {
    4849. -
    4850. 117 newKey = [sql.stringToIdentifier(newKey[0])];
    4851. -
    4852. 117 if (k.match(/^like$/)) {
    4853. -
    4854. //its a like clause {col : {like : "hello"}}
    4855. -
    4856. -
    4857. 3 pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));
    4858. -
    4859. 114 } else if (k.match(/^iLike$/)) {
    4860. -
    4861. //its a like clause {col : {iLike : "hello"}}
    4862. -
    4863. 2 pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]).concat({caseInsensitive:true}))));
    4864. -
    4865. 112 } else if (k.match(/between/i)) {
    4866. -
    4867. //its a like clause {col : {between : [1,10]}}
    4868. -
    4869. 6 var between = sql.stringToIdentifier(newKey[0]).between(v);
    4870. -
    4871. 6 k == "notBetween" && (between = between.not());
    4872. -
    4873. 6 pairs.push(between);
    4874. -
    4875. } else {
    4876. -
    4877. //otherwise is just a boolean expressio
    4878. -
    4879. //it its not a valid operator then we
    4880. -
    4881. //BooleanExpression with throw an error
    4882. -
    4883. 106 pairs.push(new BooleanExpression(k, newKey[0], v));
    4884. -
    4885. }
    4886. -
    4887. }
    4888. -
    4889. } else {
    4890. -
    4891. //we're not a twoarity operator
    4892. -
    4893. //so we create a boolean expression out of it
    4894. -
    4895. 3308 newKey = k.split(",");
    4896. -
    4897. 3308 if (newKey.length == 1) {
    4898. -
    4899. 3302 newKey = sql.stringToIdentifier(newKey[0]);
    4900. -
    4901. }
    4902. -
    4903. 3308 opts = [
    4904. -
    4905. [newKey, v]
    4906. -
    4907. ];
    4908. -
    4909. 3308 pairs.push(BooleanExpression.fromValuePairs(opts));
    4910. -
    4911. }
    4912. -
    4913. }
    4914. -
    4915. //if the total of pairs is one then we just return the first element
    4916. -
    4917. //otherwise we join them all with an AND
    4918. -
    4919. 3502 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
    4920. +
    4921. toString:function (ds) {
    4922. +
    4923. 2 !Dataset && (Dataset = require("./dataset"));
    4924. +
    4925. 2 ds = ds || new Dataset();
    4926. +
    4927. 2 return ds.castSql(this.expr, this.type);
    4928. }
    4929. }
    4930. -
    4931. }).as(sql, "BooleanExpression");
    4932. +
    4933. }).as(sql, "Cast");
    4934. -
    4935. 1var Constant = define(GenericExpression, {
    4936. +
    4937. 1var ColumnAll = define(Expression, {
    4938. instance:{
    4939. -
    4940. /**@lends patio.sql.Constant.prototype*/
    4941. +
    4942. /**@lends patio.sql.ColumnAll.prototype*/
    4943. +
    4944. /**
    4945. -
    4946. * Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.
    4947. -
    4948. *
    4949. +
    4950. * Represents all columns in a given table, table.* in SQL
    4951. * @constructs
    4952. -
    4953. * @augments patio.sql.GenericExpression
    4954. -
    4955. * @property {String} constant <b>READ ONLY</b> the contant.
    4956. +
    4957. *
    4958. +
    4959. * @augments patio.sql.Expression
    4960. +
    4961. *
    4962. +
    4963. * @param table the table this expression is for.
    4964. +
    4965. *
    4966. +
    4967. * @property table the table this all column expression represents.
    4968. */
    4969. -
    4970. constructor:function (constant) {
    4971. -
    4972. 18 this.__constant = constant;
    4973. +
    4974. constructor:function (table) {
    4975. +
    4976. 20 this.table = table;
    4977. },
    4978. /**
    4979. -
    4980. * Converts the {@link patio.sql.Constant} to a string.
    4981. +
    4982. * Converts the ColumnAll expression to a string
    4983. *
    4984. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    4985. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    4986. *
    4987. -
    4988. * @return String the SQL version of the {@link patio.sql.Constant}.
    4989. +
    4990. * @return String the SQL columnAll expression fragment.
    4991. */
    4992. toString:function (ds) {
    4993. -
    4994. 6 !Dataset && (Dataset = require("./dataset"));
    4995. -
    4996. 6 ds = ds || new Dataset();
    4997. -
    4998. 6 return ds.constantSql(this.__constant);
    4999. -
    5000. },
    5001. -
    5002. -
    5003. getters:{
    5004. -
    5005. constant:function () {
    5006. -
    5007. 0 return this.__constant;
    5008. -
    5009. }
    5010. +
    5011. 19 !Dataset && (Dataset = require("./dataset"));
    5012. +
    5013. 19 ds = ds || new Dataset();
    5014. +
    5015. 19 return ds.columnAllSql(this);
    5016. }
    5017. }
    5018. -
    5019. }).as(sql, "Constant");
    5020. +
    5021. }).as(sql, "ColumnAll");
    5022. -
    5023. /**
    5024. -
    5025. * @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
    5026. -
    5027. * @auments patio.sql.Constant
    5028. -
    5029. * @name BooleanConstant
    5030. -
    5031. * @memberOf patio.sql
    5032. -
    5033. */
    5034. -
    5035. 1var BooleanConstant = define(Constant, {
    5036. +
    5037. 1var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {
    5038. instance:{
    5039. -
    5040. /**@lends patio.sql.BooleanConstant.prototype*/
    5041. +
    5042. /**@lends patio.sql.ComplexExpression.prototype*/
    5043. /**
    5044. -
    5045. * Converts the {@link patio.sql.BooleanConstant} to a string.
    5046. +
    5047. * Represents a complex SQL expression, with a given operator and one
    5048. +
    5049. * or more attributes (which may also be ComplexExpressions, forming
    5050. +
    5051. * a tree).
    5052. *
    5053. -
    5054. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5055. -
    5056. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5057. +
    5058. * This is an abstract class that is not that useful by itself. The
    5059. +
    5060. * subclasses @link patio.sql.BooleanExpression},
    5061. +
    5062. * {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should
    5063. +
    5064. * be used instead of this class directly.
    5065. *
    5066. -
    5067. * @return String the SQL version of the {@link patio.sql.BooleanConstant}.
    5068. +
    5069. * @constructs
    5070. +
    5071. * @augments patio.sql.Expression
    5072. +
    5073. * @augments patio.sql.AliasMethods
    5074. +
    5075. * @augments patio.sql.CastMethods
    5076. +
    5077. * @augments patio.sql.OrderedMethods
    5078. +
    5079. * @augments patio.sql.SubscriptMethods
    5080. +
    5081. *
    5082. +
    5083. * @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.
    5084. +
    5085. * @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.
    5086. +
    5087. *
    5088. +
    5089. * @param {...} op The operator and arguments for this object to the ones given.
    5090. +
    5091. * <p>
    5092. +
    5093. * Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,
    5094. +
    5095. * other than the second arg for an IN/NOT IN operator.</li>
    5096. +
    5097. * </p>
    5098. */
    5099. -
    5100. toString:function (ds) {
    5101. -
    5102. 10 !Dataset && (Dataset = require("./dataset"));
    5103. -
    5104. 10 ds = ds || new Dataset();
    5105. -
    5106. 10 return ds.booleanConstantSql(this.__constant);
    5107. -
    5108. }
    5109. -
    5110. }
    5111. -
    5112. }).as(sql, "BooleanConstant");
    5113. +
    5114. constructor:function (op) {
    5115. +
    5116. 7952 if (op) {
    5117. +
    5118. 7164 var args = argsToArray(arguments,1 );
    5119. +
    5120. //make a copy of the args
    5121. +
    5122. 7164 var origArgs = args.slice(0);
    5123. +
    5124. 7164 args.forEach(function (a, i) {
    5125. +
    5126. 14676 if (Expression.isConditionSpecifier(a)) {
    5127. +
    5128. 6 args[i] = BooleanExpression.fromValuePairs(a);
    5129. +
    5130. }
    5131. +
    5132. });
    5133. +
    5134. 7164 op = op.toUpperCase();
    5135. -
    5136. /**
    5137. -
    5138. * Represents inverse boolean constants (currently only NOTNULL). A
    5139. -
    5140. * special class to allow for special behavior.
    5141. -
    5142. *
    5143. -
    5144. * @augments patio.sql.BooleanConstant
    5145. -
    5146. * @name NegativeBooleanConstant
    5147. -
    5148. * @memberOf patio.sql
    5149. -
    5150. */
    5151. -
    5152. 1var NegativeBooleanConstant = define(BooleanConstant, {
    5153. -
    5154. instance:{
    5155. -
    5156. /**@lends patio.sql.NegativeBooleanConstant.prototype*/
    5157. +
    5158. 7164 if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
    5159. +
    5160. 1164 if (args.length < 1) {
    5161. +
    5162. 0 throw new ExpressionError("The " + op + " operator requires at least 1 argument")
    5163. +
    5164. }
    5165. +
    5166. 1164 var oldArgs = args.slice(0);
    5167. +
    5168. 1164 args = [];
    5169. +
    5170. 1164 oldArgs.forEach(function (a) {
    5171. +
    5172. 2739 a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
    5173. +
    5174. });
    5175. +
    5176. +
    5177. 6000 } else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
    5178. +
    5179. 5937 if (args.length != 2) {
    5180. +
    5181. 0 throw new ExpressionError("The " + op + " operator requires precisely 2 arguments");
    5182. +
    5183. }
    5184. +
    5185. //With IN/NOT IN, even if the second argument is an array of two element arrays,
    5186. +
    5187. //don't convert it into a boolean expression, since it's definitely being used
    5188. +
    5189. //as a value list.
    5190. +
    5191. 5937 if (IN_OPERATORS[op]) {
    5192. +
    5193. 23 args[1] = origArgs[1]
    5194. +
    5195. }
    5196. +
    5197. 63 } else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
    5198. +
    5199. 63 if (args.length != 1) {
    5200. +
    5201. 0 throw new ExpressionError("The " + op + " operator requires only one argument");
    5202. +
    5203. }
    5204. +
    5205. } else {
    5206. +
    5207. 0 throw new ExpressionError("Invalid operator " + op);
    5208. +
    5209. }
    5210. +
    5211. 7164 this.op = op;
    5212. +
    5213. 7164 this.args = args;
    5214. +
    5215. }
    5216. +
    5217. },
    5218. /**
    5219. -
    5220. * Converts the {@link patio.sql.NegativeBooleanConstant} to a string.
    5221. +
    5222. * Converts the ComplexExpression to a string.
    5223. *
    5224. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5225. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5226. *
    5227. -
    5228. * @return String the SQL version of the {@link patio.sql.NegativeBooleanConstant}.
    5229. +
    5230. * @return String the SQL version of the {@link patio.sql.ComplexExpression}.
    5231. */
    5232. toString:function (ds) {
    5233. -
    5234. 2 !Dataset && (Dataset = require("./dataset"));
    5235. -
    5236. 2 ds = ds || new Dataset();
    5237. -
    5238. 2 return ds.negativeBooleanConstantSql(this.__constant);
    5239. +
    5240. 6761 !Dataset && (Dataset = require("./dataset"));
    5241. +
    5242. 6761 ds = ds || new Dataset();
    5243. +
    5244. 6761 return ds.complexExpressionSql(this.op, this.args);
    5245. }
    5246. -
    5247. }
    5248. -
    5249. }).as(sql, "NegativeBooleanConstant");
    5250. -
    5251. -
    5252. /**
    5253. -
    5254. * @namespace Holds default generic constants that can be referenced. These
    5255. -
    5256. * are included in {@link patio}
    5257. -
    5258. * @name Constants
    5259. -
    5260. * @memberOf patio.sql
    5261. -
    5262. */
    5263. -
    5264. 1sql.Constants = {
    5265. -
    5266. /**@lends patio.sql.Constants*/
    5267. -
    5268. -
    5269. /**
    5270. -
    5271. * Constant for CURRENT DATE
    5272. -
    5273. * @type patio.sql.Constant
    5274. -
    5275. */
    5276. -
    5277. CURRENT_DATE:new Constant("CURRENT_DATE"),
    5278. -
    5279. -
    5280. /**
    5281. -
    5282. * Constant for CURRENT TIME
    5283. -
    5284. * @type patio.sql.Constant
    5285. -
    5286. */
    5287. -
    5288. CURRENT_TIME:new Constant("CURRENT_TIME"),
    5289. -
    5290. -
    5291. /**
    5292. -
    5293. * Constant for CURRENT TIMESTAMP
    5294. -
    5295. * @type patio.sql.Constant
    5296. -
    5297. */
    5298. -
    5299. CURRENT_TIMESTAMP:new Constant("CURRENT_TIMESTAMP"),
    5300. -
    5301. -
    5302. /**
    5303. -
    5304. * Constant for TRUE
    5305. -
    5306. * @type patio.sql.BooleanConstant
    5307. -
    5308. */
    5309. -
    5310. SQLTRUE:new BooleanConstant(1),
    5311. -
    5312. -
    5313. /**
    5314. -
    5315. * Constant for TRUE
    5316. -
    5317. * @type patio.sql.BooleanConstant
    5318. -
    5319. */
    5320. -
    5321. TRUE:new BooleanConstant(1),
    5322. -
    5323. -
    5324. /**
    5325. -
    5326. * Constant for FALSE.
    5327. -
    5328. * @type patio.sql.BooleanConstant
    5329. -
    5330. */
    5331. -
    5332. SQLFALSE:new BooleanConstant(0),
    5333. -
    5334. -
    5335. /**
    5336. -
    5337. * Constant for FALSE
    5338. -
    5339. * @type patio.sql.BooleanConstant
    5340. -
    5341. */
    5342. -
    5343. FALSE:new BooleanConstant(0),
    5344. -
    5345. /**
    5346. -
    5347. * Constant for NULL
    5348. -
    5349. * @type patio.sql.BooleanConstant
    5350. -
    5351. */
    5352. -
    5353. NULL:new BooleanConstant(null),
    5354. -
    5355. -
    5356. /**
    5357. -
    5358. * Constant for NOT NULL
    5359. -
    5360. * @type patio.sql.NegativeBooleanConstant
    5361. -
    5362. */
    5363. -
    5364. NOTNULL:new NegativeBooleanConstant(null)
    5365. -
    5366. -
    5367. };
    5368. +
    5369. },
    5370. -
    5371. 1var Constants = sql.Constants
    5372. +
    5373. static:{
    5374. +
    5375. /**@lends patio.sql.ComplexExpression*/
    5376. -
    5377. 1var Identifier = define([GenericExpression, QualifyingMethods], {
    5378. -
    5379. instance:{
    5380. -
    5381. /**@lends patio.sql.Identifier.prototype*/
    5382. +
    5383. /**
    5384. +
    5385. * Hash of operator inversions
    5386. +
    5387. * @type Object
    5388. +
    5389. * @default {
    5390. +
    5391. * AND:"OR",
    5392. +
    5393. * OR:"AND",
    5394. +
    5395. * GT:"lte",
    5396. +
    5397. * GTE:"lt",
    5398. +
    5399. * LT:"gte",
    5400. +
    5401. * LTE:"gt",
    5402. +
    5403. * EQ:"neq",
    5404. +
    5405. * NEQ:"eq",
    5406. +
    5407. * LIKE:'NOT LIKE',
    5408. +
    5409. * "NOT LIKE":"LIKE",
    5410. +
    5411. * '!~*':'~*',
    5412. +
    5413. * '~*':'!~*',
    5414. +
    5415. * "~":'!~',
    5416. +
    5417. * "IN":'NOTIN',
    5418. +
    5419. * "NOTIN":"IN",
    5420. +
    5421. * "IS":'IS NOT',
    5422. +
    5423. * "ISNOT":"IS",
    5424. +
    5425. * NOT:"NOOP",
    5426. +
    5427. * NOOP:"NOT",
    5428. +
    5429. * ILIKE:'NOT ILIKE',
    5430. +
    5431. * NOTILIKE:"ILIKE"
    5432. +
    5433. * }
    5434. +
    5435. */
    5436. +
    5437. OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,
    5438. /**
    5439. -
    5440. * Represents an identifier (column or table). Can be used
    5441. -
    5442. * to specify a String with multiple underscores that should not be
    5443. -
    5444. * split, or for creating an implicit identifier without using a String.
    5445. +
    5446. * Default mathematical operators.
    5447. *
    5448. -
    5449. * @constructs
    5450. -
    5451. * @augments patio.sql.GenericExpression
    5452. -
    5453. * @augments patio.sql.QualifyingMethods
    5454. +
    5455. * @type Object
    5456. +
    5457. * @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}
    5458. +
    5459. */
    5460. +
    5461. MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,
    5462. +
    5463. +
    5464. /**
    5465. +
    5466. * Default bitwise operators.
    5467. *
    5468. -
    5469. * @param {String}value the identifier.
    5470. +
    5471. * @type Object
    5472. +
    5473. * @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}
    5474. +
    5475. */
    5476. +
    5477. BITWISE_OPERATORS:BITWISE_OPERATORS,
    5478. +
    5479. /**
    5480. +
    5481. * Default inequality operators.
    5482. *
    5483. -
    5484. * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
    5485. +
    5486. * @type Object
    5487. +
    5488. * @default {GT:">",GTE:">=",LT:"<",LTE:"<="}
    5489. */
    5490. -
    5491. constructor:function (value) {
    5492. -
    5493. 16802 this.__value = value;
    5494. -
    5495. },
    5496. +
    5497. INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,
    5498. /**
    5499. -
    5500. * Converts the {@link patio.sql.Identifier} to a string.
    5501. -
    5502. *
    5503. -
    5504. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5505. -
    5506. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5507. +
    5508. * Default boolean operators.
    5509. *
    5510. -
    5511. * @return String the SQL version of the {@link patio.sql.Identifier}.
    5512. +
    5513. * @type Object
    5514. +
    5515. * @default {AND:"AND",OR:"OR"}
    5516. */
    5517. -
    5518. toString:function (ds) {
    5519. -
    5520. 21906 !Dataset && (Dataset = require("./dataset"));
    5521. -
    5522. 21906 ds = ds || new Dataset();
    5523. -
    5524. 21906 return ds.quoteIdentifier(this);
    5525. -
    5526. },
    5527. -
    5528. -
    5529. /**@ignore*/
    5530. -
    5531. getters:{
    5532. -
    5533. value:function () {
    5534. -
    5535. 25154 return this.__value;
    5536. -
    5537. }
    5538. -
    5539. }
    5540. -
    5541. }
    5542. -
    5543. }).as(sql, "Identifier");
    5544. -
    5545. -
    5546. 1var JoinClause = define(Expression, {
    5547. -
    5548. instance:{
    5549. -
    5550. /**@lends patio.sql.JoinClause.prototype*/
    5551. +
    5552. BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,
    5553. /**
    5554. -
    5555. * Represents an SQL JOIN clause, used for joining tables.
    5556. -
    5557. * Created by {@link patio.Dataset} join methods.
    5558. -
    5559. * @constructs
    5560. -
    5561. * @augments patio.sql.Expression
    5562. -
    5563. *
    5564. -
    5565. * @param {String} joinType the type of join this JoinClause should use
    5566. -
    5567. * @param table the table to join with
    5568. -
    5569. * @param tableAlias the alias to use for this join clause
    5570. +
    5571. * Default IN operators.
    5572. *
    5573. -
    5574. * @property {String} joinType <b>READ ONLY</b> the type of join this JoinClause should use
    5575. -
    5576. * @property table <b>READ ONLY</b> the table to join with
    5577. -
    5578. * @property joinType <b>READ ONLY</b> the alias to use for this join clause
    5579. -
    5580. * */
    5581. -
    5582. constructor:function (joinType, table, tableAlias) {
    5583. -
    5584. 791 this.__joinType = joinType;
    5585. -
    5586. 791 this.__table = table;
    5587. -
    5588. 791 this.__tableAlias = tableAlias || null;
    5589. -
    5590. },
    5591. -
    5592. +
    5593. * @type Object
    5594. +
    5595. * @default {IN:"IN",NOTIN:'NOT IN'}
    5596. +
    5597. */
    5598. +
    5599. IN_OPERATORS:IN_OPERATORS,
    5600. /**
    5601. -
    5602. * Converts the {@link patio.sql.JoinClause} to a string.
    5603. +
    5604. * Default IS operators.
    5605. *
    5606. -
    5607. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5608. -
    5609. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5610. +
    5611. * @type Object
    5612. +
    5613. * @default {IS:"IS", ISNOT:'IS NOT'}
    5614. +
    5615. */
    5616. +
    5617. IS_OPERATORS:IS_OPERATORS,
    5618. +
    5619. /**
    5620. +
    5621. * Default two arity operators.
    5622. *
    5623. -
    5624. * @return String the SQL version of the {@link patio.sql.JoinClause}.
    5625. +
    5626. * @type Object
    5627. +
    5628. * @default {
    5629. +
    5630. * EQ:'=',
    5631. +
    5632. * NEQ:'!=', LIKE:"LIKE",
    5633. +
    5634. * "NOT LIKE":'NOT LIKE',
    5635. +
    5636. * ILIKE:"ILIKE",
    5637. +
    5638. * "NOT ILIKE":'NOT ILIKE',
    5639. +
    5640. * "~":"~",
    5641. +
    5642. * '!~':"!~",
    5643. +
    5644. * '~*':"~*",
    5645. +
    5646. * '!~*':"!~*",
    5647. +
    5648. * GT:">",
    5649. +
    5650. * GTE:">=",
    5651. +
    5652. * LT:"<",
    5653. +
    5654. * LTE:"<=",
    5655. +
    5656. * bitWiseAnd:"&",
    5657. +
    5658. * bitWiseOr:"|",
    5659. +
    5660. * exclusiveOr:"^",
    5661. +
    5662. * leftShift:"<<",
    5663. +
    5664. * rightShift:">>",
    5665. +
    5666. * IS:"IS",
    5667. +
    5668. * ISNOT:'IS NOT',
    5669. +
    5670. * IN:"IN",
    5671. +
    5672. * NOTIN:'NOT IN'
    5673. +
    5674. * }
    5675. */
    5676. -
    5677. toString:function (ds) {
    5678. -
    5679. 17 !Dataset && (Dataset = require("./dataset"));
    5680. -
    5681. 17 ds = ds || new Dataset();
    5682. -
    5683. 17 return ds.joinClauseSql(this);
    5684. -
    5685. },
    5686. -
    5687. -
    5688. /**@ignore*/
    5689. -
    5690. getters:{
    5691. -
    5692. joinType:function () {
    5693. -
    5694. 927 return this.__joinType;
    5695. -
    5696. },
    5697. -
    5698. -
    5699. table:function () {
    5700. -
    5701. 928 return this.__table;
    5702. -
    5703. },
    5704. -
    5705. -
    5706. tableAlias:function () {
    5707. -
    5708. 926 return this.__tableAlias;
    5709. -
    5710. }
    5711. -
    5712. }
    5713. -
    5714. }
    5715. -
    5716. }).as(sql, "JoinClause");
    5717. -
    5718. +
    5719. TWO_ARITY_OPERATORS:TWO_ARITY_OPERATORS,
    5720. -
    5721. 1var JoinOnClause = define(JoinClause, {
    5722. -
    5723. instance:{
    5724. -
    5725. /**@lends patio.sql.JoinOnClause.prototype*/
    5726. /**
    5727. -
    5728. * Represents an SQL JOIN clause with ON conditions. Created by {@link patio.Dataset} join methods.
    5729. -
    5730. * See {@link patio.sql.JoinClause} for other argument parameters.
    5731. -
    5732. * @constructs
    5733. -
    5734. * @augments patio.sql.JoinClause
    5735. +
    5736. * Default N(multi) arity operators.
    5737. *
    5738. -
    5739. * @param on the expression to filter with. See {@link patio.Dataset#filter}
    5740. -
    5741. * @property on <b>READ ONLY</b> the filter to use with joining the datasets.
    5742. +
    5743. * @type Object
    5744. +
    5745. * @default {
    5746. +
    5747. * "||":"||",
    5748. +
    5749. * AND:"AND",
    5750. +
    5751. * OR:"OR",
    5752. +
    5753. * PLUS:"+",
    5754. +
    5755. * MINUS:"-",
    5756. +
    5757. * DIVIDE:"/", MULTIPLY:"*"
    5758. +
    5759. * }
    5760. */
    5761. -
    5762. constructor:function (on, joinType, table, tableAlias) {
    5763. -
    5764. 761 this.__on = on;
    5765. -
    5766. 761 this._super(arguments, [joinType, table, tableAlias]);
    5767. -
    5768. },
    5769. +
    5770. N_ARITY_OPERATORS:N_ARITY_OPERATORS,
    5771. /**
    5772. -
    5773. * Converts the {@link patio.sql.JoinOnClause} to a string.
    5774. -
    5775. *
    5776. -
    5777. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5778. -
    5779. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5780. +
    5781. * Default ONE operators.
    5782. *
    5783. -
    5784. * @return String the SQL version of the {@link patio.sql.JoinOnClause}.
    5785. +
    5786. * @type Object
    5787. +
    5788. * @default {
    5789. +
    5790. * "NOT":"NOT",
    5791. +
    5792. * "NOOP":"NOOP"
    5793. +
    5794. * }
    5795. */
    5796. -
    5797. toString:function (ds) {
    5798. -
    5799. 813 !Dataset && (Dataset = require("./dataset"));
    5800. -
    5801. 813 ds = ds || new Dataset();
    5802. -
    5803. 813 return ds.joinOnClauseSql(this);
    5804. -
    5805. },
    5806. -
    5807. -
    5808. /**@ignore*/
    5809. -
    5810. getters:{
    5811. -
    5812. on:function () {
    5813. -
    5814. 813 return this.__on;
    5815. -
    5816. }
    5817. -
    5818. }
    5819. +
    5820. ONE_ARITY_OPERATORS:ONE_ARITY_OPERATORS
    5821. }
    5822. -
    5823. }).as(sql, "JoinOnClause");
    5824. +
    5825. }).as(sql, "ComplexExpression");
    5826. -
    5827. 1var JoinUsingClause = define(JoinClause, {
    5828. -
    5829. instance:{
    5830. -
    5831. /**@lends patio.sql.JoinUsingClause.prototype*/
    5832. +
    5833. /**
    5834. +
    5835. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    5836. +
    5837. * in a boolean value in SQL.
    5838. +
    5839. *
    5840. +
    5841. * @augments patio.sql.ComplexExpression
    5842. +
    5843. * @augments patio.sql.BooleanMethods
    5844. +
    5845. * @name BooleanExpression
    5846. +
    5847. * @memberOf patio.sql
    5848. +
    5849. */
    5850. +
    5851. 1var BooleanExpression = define([ComplexExpression, BooleanMethods], {
    5852. +
    5853. static:{
    5854. +
    5855. /**@lends patio.sql.BooleanExpression*/
    5856. /**
    5857. -
    5858. * Represents an SQL JOIN clause with USING conditions.
    5859. -
    5860. * Created by {@link patio.Dataset} join methods.
    5861. -
    5862. * See {@link patio.sql.JoinClause} for other argument parameters.
    5863. +
    5864. * Invert the expression, if possible. If the expression cannot
    5865. +
    5866. * be inverted, it throws an {@link patio.error.ExpressionError}. An inverted expression should match
    5867. +
    5868. * everything that the uninverted expression did not match, and vice-versa, except for possible issues with
    5869. +
    5870. * SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
    5871. *
    5872. -
    5873. * @constructs
    5874. -
    5875. * @augments patio.sql.JoinClause
    5876. +
    5877. * @example
    5878. +
    5879. * BooleanExpression.invert(sql.a) //=> NOT "a"
    5880. *
    5881. -
    5882. * @param using the column/s to use when joining.
    5883. -
    5884. * @property using <b>READ ONLY</b> the column/s to use when joining.
    5885. +
    5886. * @param {patio.sql.BooleanExpression} expression
    5887. +
    5888. * the expression to invert.
    5889. +
    5890. *
    5891. +
    5892. * @return {patio.sql.BooleanExpression} the inverted expression.
    5893. */
    5894. -
    5895. constructor:function (using, joinType, table, tableAlias) {
    5896. -
    5897. 8 this.__using = using.map(function (u) {
    5898. -
    5899. 9 return isString(u) ? new Identifier(u) : u;
    5900. -
    5901. });
    5902. -
    5903. 8 this._super(arguments, [joinType, table, tableAlias]);
    5904. +
    5905. invert:function (expression) {
    5906. +
    5907. 95 if (isInstanceOf(expression, BooleanExpression)) {
    5908. +
    5909. 90 var op = expression.op, newArgs;
    5910. +
    5911. 90 if (op == "AND" || op == "OR") {
    5912. +
    5913. 3 newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args.map(function (arg) {
    5914. +
    5915. 6 return BooleanExpression.invert(arg);
    5916. +
    5917. }));
    5918. +
    5919. 3 return BooleanExpression.fromArgs(newArgs);
    5920. +
    5921. } else {
    5922. +
    5923. 87 newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);
    5924. +
    5925. 87 return BooleanExpression.fromArgs(newArgs);
    5926. +
    5927. }
    5928. +
    5929. 5 } else if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
    5930. +
    5931. 0 throw new ExpressionError(format("Cannot invert %4j", [expression]));
    5932. +
    5933. } else {
    5934. +
    5935. 5 return new BooleanExpression("NOT", expression);
    5936. +
    5937. }
    5938. },
    5939. -
    5940. /**
    5941. -
    5942. * Converts the {@link patio.sql.JoinUsingClause} to a string.
    5943. +
    5944. * Take pairs of values (e.g. a hash or array of two element arrays)
    5945. +
    5946. * and converts it to a {@link patio.sql.BooleanExpression}. The operator and args
    5947. +
    5948. * used depends on the case of the right (2nd) argument:
    5949. *
    5950. -
    5951. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5952. -
    5953. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5954. +
    5955. * <pre class='code'>
    5956. +
    5957. * BooleanExpression.fromValuePairs({a : [1,2,3]}) //=> a IN (1,2,3)
    5958. +
    5959. * BooleanExpression.fromValuePairs({a : true}); // a IS TRUE;
    5960. +
    5961. * BooleanExpression.fromValuePairs({a : /^A/i}); // a *~ '^A'
    5962. +
    5963. * </pre>
    5964. *
    5965. -
    5966. * @return String the SQL version of the {@link patio.sql.JoinUsingClause}.
    5967. -
    5968. */
    5969. -
    5970. toString:function (ds) {
    5971. -
    5972. 95 !Dataset && (Dataset = require("./dataset"));
    5973. -
    5974. 95 ds = ds || new Dataset();
    5975. -
    5976. 95 return ds.joinUsingClauseSql(this);
    5977. -
    5978. },
    5979. -
    5980. -
    5981. /**@ignore*/
    5982. -
    5983. getters:{
    5984. -
    5985. using:function () {
    5986. -
    5987. 95 return this.__using;
    5988. +
    5989. * If multiple arguments are given, they are joined with the op given (AND
    5990. +
    5991. * by default, OR possible). If negate is set to true,
    5992. +
    5993. * all subexpressions are inverted before used.
    5994. +
    5995. * <pre class='code'>
    5996. +
    5997. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}) //=> a IN (1,2,3) AND b IS TRUE
    5998. +
    5999. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
    6000. +
    6001. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
    6002. +
    6003. * </pre>
    6004. +
    6005. * @param {Object} a object to convert to a {@link patio.sql.BooleanExpression}
    6006. +
    6007. * @param {String} [op="AND"] Boolean operator to join each subexpression with.
    6008. +
    6009. * <pre class="code">
    6010. +
    6011. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
    6012. +
    6013. * </pre>
    6014. +
    6015. * @param {Boolean} [negate=false] set to try to invert the {@link patio.sql.BooleanExpression}.
    6016. +
    6017. * <pre class="code">
    6018. +
    6019. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
    6020. +
    6021. * </pre>
    6022. +
    6023. * @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
    6024. +
    6025. */
    6026. +
    6027. fromValuePairs:function (a, op, negate) {
    6028. +
    6029. 7610 !Dataset && (Dataset = require("./dataset"));
    6030. +
    6031. 7610 op = op || "AND", negate = negate || false;
    6032. +
    6033. 7610 var pairArr = [];
    6034. +
    6035. 7610 var isArr = isArray(a) && Expression.isConditionSpecifier(a);
    6036. +
    6037. 7610 if (isHash(a)) {
    6038. +
    6039. 3385 pairArr.push(this.__filterObject(a));
    6040. +
    6041. } else {
    6042. +
    6043. 4225 for (var k in a) {
    6044. +
    6045. 5203 var v = isArr ? a[k][1] : a[k], ret;
    6046. +
    6047. 5203 k = isArr ? a[k][0] : k;
    6048. +
    6049. 5203 if (isArray(v) || isInstanceOf(v, Dataset)) {
    6050. +
    6051. 17 k = isArray(k) ? k.map(function (i) {
    6052. +
    6053. 12 return isString(i) ? sql.stringToIdentifier(i) : i
    6054. +
    6055. }) : isString(k) ? sql.stringToIdentifier(k) : k;
    6056. +
    6057. 17 ret = new BooleanExpression("IN", k, v);
    6058. +
    6059. 5186 } else if (isInstanceOf(v, NegativeBooleanConstant)) {
    6060. +
    6061. 0 ret = new BooleanExpression("ISNOT", k, v.constant);
    6062. +
    6063. 5186 } else if (isInstanceOf(v, BooleanConstant)) {
    6064. +
    6065. 0 ret = new BooleanExpression("IS", k, v.constant);
    6066. +
    6067. 5186 } else if (isNull(v) || isBoolean(v)) {
    6068. +
    6069. 240 ret = new BooleanExpression("IS", k, v);
    6070. +
    6071. 4946 } else if (isHash(v)) {
    6072. +
    6073. 0 ret = BooleanExpression.__filterObject(v, k);
    6074. +
    6075. 4946 } else if (isRegExp(v)) {
    6076. +
    6077. 69 ret = StringExpression.like(sql.stringToIdentifier(k), v);
    6078. +
    6079. } else {
    6080. +
    6081. 4877 ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
    6082. +
    6083. }
    6084. +
    6085. 5203 negate && (ret = BooleanExpression.invert(ret));
    6086. +
    6087. 5203 pairArr.push(ret);
    6088. +
    6089. }
    6090. }
    6091. -
    6092. }
    6093. -
    6094. }
    6095. -
    6096. }).as(sql, "JoinUsingClause");
    6097. -
    6098. -
    6099. -
    6100. 1var PlaceHolderLiteralString = define(GenericExpression, {
    6101. -
    6102. instance:{
    6103. -
    6104. /**@lends patio.sql.PlaceHolderLiteralString.prototype*/
    6105. +
    6106. //if We just have one then return the first otherwise create a new Boolean expression
    6107. +
    6108. 7610 return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
    6109. +
    6110. },
    6111. /**
    6112. -
    6113. * Represents a literal string with placeholders and arguments.
    6114. -
    6115. * This is necessary to ensure delayed literalization of the arguments
    6116. -
    6117. * required for the prepared statement support and for database-specific
    6118. -
    6119. * literalization.
    6120. +
    6121. * @private
    6122. *
    6123. -
    6124. * @constructs
    6125. -
    6126. * @augments patio.sql.GenericExpression
    6127. +
    6128. * This builds an expression from a hash
    6129. *
    6130. -
    6131. * @param {String} str the string that contains placeholders.
    6132. -
    6133. * @param {Array} args array of arguments that will be literalized using {@link patio.Dataset#literal}, and
    6134. -
    6135. * replaced in the string.
    6136. -
    6137. * @param {Boolean} [parens=false] set to true to wrap the string in parens.
    6138. +
    6139. * @example
    6140. *
    6141. -
    6142. * @property {String} str <b>READ ONLY</b> the string that contains placeholders.
    6143. -
    6144. * @property {Array} args <b>READ ONLY</b> array of arguments that will be literalized using {@link patio.Dataset#literal}, and
    6145. -
    6146. * replaced in the string.
    6147. -
    6148. * @property {String} parens <b>READ ONLY</b> set to true to wrap the string in parens.
    6149. -
    6150. */
    6151. -
    6152. constructor:function (str, args, parens) {
    6153. -
    6154. 53 parens = parens || false;
    6155. -
    6156. 53 var v;
    6157. -
    6158. 53 this.__str = str;
    6159. -
    6160. 53 this.__args = isArray(args) && args.length == 1 && isHash((v = args[0])) ? v : args;
    6161. -
    6162. 53 this.__parens = parens;
    6163. -
    6164. },
    6165. -
    6166. -
    6167. /**
    6168. -
    6169. * Converts the {@link patio.sql.PlaceHolderLiteralString} to a string.
    6170. +
    6171. * Dataset._filterObject({a : 1}) //=> WHERE (a = 1)
    6172. +
    6173. * Dataset._filterObject({x : {gt : 1}}) //=> WHERE (x > 1)
    6174. +
    6175. * Dataset._filterObject({x : {gt : 1}, a : 1}) //=> WHERE ((x > 1) AND (a = 1))
    6176. +
    6177. * Dataset._filterObject({x : {like : "name"}}) //=> WHERE (x LIKE 'name')
    6178. +
    6179. * Dataset._filterObject({x : {iLike : "name"}}) //=> WHERE (x LIKE 'name')
    6180. +
    6181. * Dataset._filterObject({x : {between : [1,10]}}) //=> WHERE ((x >= 1) AND (x <= 10))
    6182. +
    6183. * Dataset._filterObject({x : {notBetween : [1,10]}}) //=> WHERE ((x < 1) OR (x > 10))
    6184. +
    6185. * Dataset._filterObject({x : {neq : 1}}) //=> WHERE (x != 1)
    6186. *
    6187. -
    6188. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6189. -
    6190. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6191. +
    6192. * @param {Object} expr the expression we need to create an expression out of
    6193. +
    6194. * @param {String} [key=null] the key that the hash corresponds to
    6195. *
    6196. -
    6197. * @return String the SQL version of the {@link patio.sql.PlaceHolderLiteralString}.
    6198. +
    6199. * @return {patio.sql.Expression} an expression to use in the filter
    6200. */
    6201. -
    6202. toString:function (ds) {
    6203. -
    6204. 52 !Dataset && (Dataset = require("./dataset"));
    6205. -
    6206. 52 ds = ds || new Dataset();
    6207. -
    6208. 52 return ds.placeholderLiteralStringSql(this);
    6209. -
    6210. },
    6211. -
    6212. -
    6213. /**@ignore*/
    6214. -
    6215. getters:{
    6216. -
    6217. str:function () {
    6218. -
    6219. 55 return this.__str;
    6220. -
    6221. },
    6222. -
    6223. args:function () {
    6224. -
    6225. 55 return this.__args;
    6226. -
    6227. },
    6228. +
    6229. __filterObject:function (expr, key) {
    6230. +
    6231. 3502 var pairs = [], opts, newKey;
    6232. +
    6233. 3502 var twoArityOperators = this.TWO_ARITY_OPERATORS;
    6234. +
    6235. 3502 for (var k in expr) {
    6236. +
    6237. 3541 var v = expr[k];
    6238. +
    6239. 3541 if (isHash(v)) { //its a hash too filter it too!
    6240. +
    6241. 115 pairs.push(this.__filterObject(v, k));
    6242. +
    6243. 3426 } else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
    6244. +
    6245. //its a two arrity operator (e.g. '=', '>')
    6246. +
    6247. 118 newKey = isString(key) ? key.split(",") : [key];
    6248. +
    6249. 118 if (newKey.length > 1) {
    6250. +
    6251. //this represents a hash where the key represents two columns
    6252. +
    6253. //(e.g. {"col1,col2" : 1}) => WHERE (col1 = 1 AND col2 = 1)
    6254. +
    6255. 1 pairs = pairs.concat(newKey.map(function (k) {
    6256. +
    6257. //filter each column with the expression
    6258. +
    6259. 2 return this.__filterObject(expr, k);
    6260. +
    6261. }, this));
    6262. +
    6263. } else {
    6264. +
    6265. 117 newKey = [sql.stringToIdentifier(newKey[0])];
    6266. +
    6267. 117 if (k.match(/^like$/)) {
    6268. +
    6269. //its a like clause {col : {like : "hello"}}
    6270. -
    6271. parens:function () {
    6272. -
    6273. 55 return this.__parens;
    6274. +
    6275. 3 pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));
    6276. +
    6277. 114 } else if (k.match(/^iLike$/)) {
    6278. +
    6279. //its a like clause {col : {iLike : "hello"}}
    6280. +
    6281. 2 pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]).concat({caseInsensitive:true}))));
    6282. +
    6283. 112 } else if (k.match(/between/i)) {
    6284. +
    6285. //its a like clause {col : {between : [1,10]}}
    6286. +
    6287. 6 var between = sql.stringToIdentifier(newKey[0]).between(v);
    6288. +
    6289. 6 k == "notBetween" && (between = between.not());
    6290. +
    6291. 6 pairs.push(between);
    6292. +
    6293. } else {
    6294. +
    6295. //otherwise is just a boolean expressio
    6296. +
    6297. //it its not a valid operator then we
    6298. +
    6299. //BooleanExpression with throw an error
    6300. +
    6301. 106 pairs.push(new BooleanExpression(k, newKey[0], v));
    6302. +
    6303. }
    6304. +
    6305. }
    6306. +
    6307. } else {
    6308. +
    6309. //we're not a twoarity operator
    6310. +
    6311. //so we create a boolean expression out of it
    6312. +
    6313. 3308 newKey = k.split(",");
    6314. +
    6315. 3308 if (newKey.length == 1) {
    6316. +
    6317. 3302 newKey = sql.stringToIdentifier(newKey[0]);
    6318. +
    6319. }
    6320. +
    6321. 3308 opts = [
    6322. +
    6323. [newKey, v]
    6324. +
    6325. ];
    6326. +
    6327. 3308 pairs.push(BooleanExpression.fromValuePairs(opts));
    6328. +
    6329. }
    6330. }
    6331. -
    6332. +
    6333. //if the total of pairs is one then we just return the first element
    6334. +
    6335. //otherwise we join them all with an AND
    6336. +
    6337. 3502 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
    6338. }
    6339. }
    6340. -
    6341. }).as(sql, "PlaceHolderLiteralString");
    6342. +
    6343. }).as(sql, "BooleanExpression");
    6344. -
    6345. 1var SQLFunction = define(GenericExpression, {
    6346. +
    6347. 1var Constant = define(GenericExpression, {
    6348. instance:{
    6349. -
    6350. /**@lends patio.sql.SQLFunction.prototype*/
    6351. -
    6352. +
    6353. /**@lends patio.sql.Constant.prototype*/
    6354. /**
    6355. -
    6356. * Represents an SQL function call.
    6357. +
    6358. * Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.
    6359. *
    6360. * @constructs
    6361. * @augments patio.sql.GenericExpression
    6362. -
    6363. *
    6364. -
    6365. * @param {...} f variable number of arguments where the first argument is the name
    6366. -
    6367. * of the SQL function to invoke. The rest of the arguments will be literalized through
    6368. -
    6369. * {@link patio.Dataset#literal} and placed into the SQL function call.
    6370. -
    6371. *
    6372. -
    6373. * @property {String} f <b>READ ONLY</b> the SQL function to call.
    6374. -
    6375. * @property {Array} args <b>READ ONLY</b> args arguments will be literalized through
    6376. -
    6377. * {@link patio.Dataset#literal} and placed into the SQL function call.
    6378. -
    6379. * */
    6380. -
    6381. constructor:function (f) {
    6382. -
    6383. 1109 var args = argsToArray(arguments).slice(1);
    6384. -
    6385. 1109 this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {
    6386. -
    6387. 773 return isString(a) ? sql.stringToIdentifier(a) : a;
    6388. -
    6389. });
    6390. +
    6391. * @property {String} constant <b>READ ONLY</b> the contant.
    6392. +
    6393. */
    6394. +
    6395. constructor:function (constant) {
    6396. +
    6397. 18 this.__constant = constant;
    6398. },
    6399. /**
    6400. -
    6401. * Converts the {@link patio.sql.SQLFunction} to a string.
    6402. +
    6403. * Converts the {@link patio.sql.Constant} to a string.
    6404. *
    6405. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6406. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6407. *
    6408. -
    6409. * @return String the SQL version of the {@link patio.sql.SQLFunction}.
    6410. +
    6411. * @return String the SQL version of the {@link patio.sql.Constant}.
    6412. */
    6413. toString:function (ds) {
    6414. -
    6415. 565 !Dataset && (Dataset = require("./dataset"));
    6416. -
    6417. 565 ds = ds || new Dataset();
    6418. -
    6419. 565 return ds.functionSql(this);
    6420. +
    6421. 6 !Dataset && (Dataset = require("./dataset"));
    6422. +
    6423. 6 ds = ds || new Dataset();
    6424. +
    6425. 6 return ds.constantSql(this.__constant);
    6426. },
    6427. -
    6428. /**@ignore*/
    6429. getters:{
    6430. -
    6431. f:function () {
    6432. -
    6433. 567 return this.__f;
    6434. -
    6435. },
    6436. -
    6437. -
    6438. args:function () {
    6439. -
    6440. 567 return this.__args;
    6441. +
    6442. constant:function () {
    6443. +
    6444. 0 return this.__constant;
    6445. }
    6446. }
    6447. }
    6448. -
    6449. }).as(sql, "SQLFunction");
    6450. +
    6451. }).as(sql, "Constant");
    6452. /**
    6453. -
    6454. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    6455. -
    6456. * in a numeric value in SQL.
    6457. -
    6458. *
    6459. -
    6460. * @name NumericExpression
    6461. +
    6462. * @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
    6463. +
    6464. * @auments patio.sql.Constant
    6465. +
    6466. * @name BooleanConstant
    6467. * @memberOf patio.sql
    6468. -
    6469. * @augments patio.sql.ComplexExpression
    6470. -
    6471. * @augments patio.sql.BitWiseMethods
    6472. -
    6473. * @augments patio.sql.NumericMethods
    6474. -
    6475. * @augments patio.sql.InequalityMethods
    6476. */
    6477. -
    6478. 1var NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");
    6479. -
    6480. -
    6481. -
    6482. 1var OrderedExpression = define(Expression, {
    6483. +
    6484. 1var BooleanConstant = define(Constant, {
    6485. instance:{
    6486. -
    6487. /**@lends patio.sql.OrderedExpression.prototype*/
    6488. -
    6489. -
    6490. /**
    6491. -
    6492. * Represents a column/expression to order the result set by.
    6493. -
    6494. * @constructs
    6495. -
    6496. * @augments patio.sql.Expression
    6497. -
    6498. *
    6499. -
    6500. * @param expression the expression to order
    6501. -
    6502. * @param {Boolean}[descending=true] set to false to order ASC
    6503. -
    6504. * @param {String|Object} [opts=null] additional options
    6505. -
    6506. * <ul>
    6507. -
    6508. * <li>String: if value is "first" the null values will be first, if "last" then null values
    6509. -
    6510. * will be last</li>
    6511. -
    6512. * <li>Object: will pull the nulls property off of the object use use the same rules as if it
    6513. -
    6514. * were a string</li>
    6515. -
    6516. * </ul>
    6517. -
    6518. * @property expression <b>READ ONLY</b> the expression to order.
    6519. -
    6520. * @property {Boolean} [descending=true] <b>READ ONLY</b> true if decending, false otherwise.
    6521. -
    6522. * @property {String} [nulls=null] if value is "first" the null values will be first, if "last" then null values
    6523. -
    6524. * will be last
    6525. -
    6526. */
    6527. -
    6528. constructor:function (expression, descending, opts) {
    6529. -
    6530. 92 descending = isBoolean(descending) ? descending : true;
    6531. -
    6532. 92 opts = opts || {};
    6533. -
    6534. 92 this.__expression = expression;
    6535. -
    6536. 92 this.__descending = descending;
    6537. -
    6538. 92 var nulls = isString(opts) ? opts : opts.nulls;
    6539. -
    6540. 92 this.__nulls = isString(nulls) ? nulls.toLowerCase() : null;
    6541. -
    6542. },
    6543. -
    6544. -
    6545. /**
    6546. -
    6547. * @return {patio.sql.OrderedExpression} a copy that is ordered ASC
    6548. -
    6549. */
    6550. -
    6551. asc:function () {
    6552. -
    6553. 0 return new OrderedExpression(this.__expression, false, {nulls:this.__nulls});
    6554. -
    6555. },
    6556. +
    6557. /**@lends patio.sql.BooleanConstant.prototype*/
    6558. /**
    6559. -
    6560. * @return {patio.sql.OrderedExpression} Return a copy that is ordered DESC
    6561. +
    6562. * Converts the {@link patio.sql.BooleanConstant} to a string.
    6563. +
    6564. *
    6565. +
    6566. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6567. +
    6568. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6569. +
    6570. *
    6571. +
    6572. * @return String the SQL version of the {@link patio.sql.BooleanConstant}.
    6573. */
    6574. -
    6575. desc:function () {
    6576. -
    6577. 0 return new OrderedExpression(this.__expression, true, {nulls:this.__nulls});
    6578. -
    6579. },
    6580. +
    6581. toString:function (ds) {
    6582. +
    6583. 10 !Dataset && (Dataset = require("./dataset"));
    6584. +
    6585. 10 ds = ds || new Dataset();
    6586. +
    6587. 10 return ds.booleanConstantSql(this.__constant);
    6588. +
    6589. }
    6590. +
    6591. }
    6592. +
    6593. }).as(sql, "BooleanConstant");
    6594. -
    6595. /**
    6596. -
    6597. * * @return {patio.sql.OrderedExpression} an inverted expression, changing ASC to DESC and NULLS FIRST to NULLS LAST.
    6598. -
    6599. * */
    6600. -
    6601. invert:function () {
    6602. -
    6603. 17 return new OrderedExpression(this.__expression, !this.__descending, {nulls:this._static.INVERT_NULLS[this.__nulls] || this.__nulls});
    6604. -
    6605. },
    6606. +
    6607. /**
    6608. +
    6609. * Represents inverse boolean constants (currently only NOTNULL). A
    6610. +
    6611. * special class to allow for special behavior.
    6612. +
    6613. *
    6614. +
    6615. * @augments patio.sql.BooleanConstant
    6616. +
    6617. * @name NegativeBooleanConstant
    6618. +
    6619. * @memberOf patio.sql
    6620. +
    6621. */
    6622. +
    6623. 1var NegativeBooleanConstant = define(BooleanConstant, {
    6624. +
    6625. instance:{
    6626. +
    6627. /**@lends patio.sql.NegativeBooleanConstant.prototype*/
    6628. /**
    6629. -
    6630. * Converts the {@link patio.sql.OrderedExpression} to a string.
    6631. +
    6632. * Converts the {@link patio.sql.NegativeBooleanConstant} to a string.
    6633. *
    6634. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6635. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6636. *
    6637. -
    6638. * @return String the SQL version of the {@link patio.sql.OrderedExpression}.
    6639. +
    6640. * @return String the SQL version of the {@link patio.sql.NegativeBooleanConstant}.
    6641. */
    6642. toString:function (ds) {
    6643. -
    6644. 73 !Dataset && (Dataset = require("./dataset"));
    6645. -
    6646. 73 ds = ds || new Dataset();
    6647. -
    6648. 73 return ds.orderedExpressionSql(this);
    6649. -
    6650. },
    6651. -
    6652. -
    6653. /**@ignore*/
    6654. -
    6655. getters:{
    6656. -
    6657. expression:function () {
    6658. -
    6659. 75 return this.__expression;
    6660. -
    6661. },
    6662. -
    6663. descending:function () {
    6664. -
    6665. 75 return this.__descending;
    6666. -
    6667. },
    6668. -
    6669. nulls:function () {
    6670. -
    6671. 82 return this.__nulls;
    6672. -
    6673. }
    6674. +
    6675. 2 !Dataset && (Dataset = require("./dataset"));
    6676. +
    6677. 2 ds = ds || new Dataset();
    6678. +
    6679. 2 return ds.negativeBooleanConstantSql(this.__constant);
    6680. }
    6681. -
    6682. },
    6683. -
    6684. static:{
    6685. -
    6686. /**@lends patio.sql.OrderedExpression*/
    6687. -
    6688. /**
    6689. -
    6690. * Hash that contains the inversions for "first" and "last".
    6691. -
    6692. * @type Object
    6693. -
    6694. * @default {first:"last", last:"first"}
    6695. -
    6696. */
    6697. -
    6698. INVERT_NULLS:{first:"last", last:"first"}
    6699. }
    6700. -
    6701. }).as(sql, "OrderedExpression");
    6702. +
    6703. }).as(sql, "NegativeBooleanConstant");
    6704. -
    6705. 1var QualifiedIdentifier = define([GenericExpression, QualifyingMethods], {
    6706. +
    6707. /**
    6708. +
    6709. * @namespace Holds default generic constants that can be referenced. These
    6710. +
    6711. * are included in {@link patio}
    6712. +
    6713. * @name Constants
    6714. +
    6715. * @memberOf patio.sql
    6716. +
    6717. */
    6718. +
    6719. 1sql.Constants = {
    6720. +
    6721. /**@lends patio.sql.Constants*/
    6722. +
    6723. +
    6724. /**
    6725. +
    6726. * Constant for CURRENT DATE
    6727. +
    6728. * @type patio.sql.Constant
    6729. +
    6730. */
    6731. +
    6732. CURRENT_DATE:new Constant("CURRENT_DATE"),
    6733. +
    6734. +
    6735. /**
    6736. +
    6737. * Constant for CURRENT TIME
    6738. +
    6739. * @type patio.sql.Constant
    6740. +
    6741. */
    6742. +
    6743. CURRENT_TIME:new Constant("CURRENT_TIME"),
    6744. +
    6745. +
    6746. /**
    6747. +
    6748. * Constant for CURRENT TIMESTAMP
    6749. +
    6750. * @type patio.sql.Constant
    6751. +
    6752. */
    6753. +
    6754. CURRENT_TIMESTAMP:new Constant("CURRENT_TIMESTAMP"),
    6755. +
    6756. +
    6757. /**
    6758. +
    6759. * Constant for TRUE
    6760. +
    6761. * @type patio.sql.BooleanConstant
    6762. +
    6763. */
    6764. +
    6765. SQLTRUE:new BooleanConstant(1),
    6766. +
    6767. +
    6768. /**
    6769. +
    6770. * Constant for TRUE
    6771. +
    6772. * @type patio.sql.BooleanConstant
    6773. +
    6774. */
    6775. +
    6776. TRUE:new BooleanConstant(1),
    6777. +
    6778. +
    6779. /**
    6780. +
    6781. * Constant for FALSE.
    6782. +
    6783. * @type patio.sql.BooleanConstant
    6784. +
    6785. */
    6786. +
    6787. SQLFALSE:new BooleanConstant(0),
    6788. +
    6789. +
    6790. /**
    6791. +
    6792. * Constant for FALSE
    6793. +
    6794. * @type patio.sql.BooleanConstant
    6795. +
    6796. */
    6797. +
    6798. FALSE:new BooleanConstant(0),
    6799. +
    6800. /**
    6801. +
    6802. * Constant for NULL
    6803. +
    6804. * @type patio.sql.BooleanConstant
    6805. +
    6806. */
    6807. +
    6808. NULL:new BooleanConstant(null),
    6809. +
    6810. +
    6811. /**
    6812. +
    6813. * Constant for NOT NULL
    6814. +
    6815. * @type patio.sql.NegativeBooleanConstant
    6816. +
    6817. */
    6818. +
    6819. NOTNULL:new NegativeBooleanConstant(null)
    6820. +
    6821. +
    6822. };
    6823. +
    6824. +
    6825. 1var Constants = sql.Constants
    6826. +
    6827. +
    6828. 1var Identifier = define([GenericExpression, QualifyingMethods], {
    6829. instance:{
    6830. -
    6831. /**@lends patio.sql.QualifiedIdentifier.prototype*/
    6832. +
    6833. /**@lends patio.sql.Identifier.prototype*/
    6834. /**
    6835. -
    6836. * Represents a qualified identifier (column with table or table with schema).
    6837. +
    6838. * Represents an identifier (column or table). Can be used
    6839. +
    6840. * to specify a String with multiple underscores that should not be
    6841. +
    6842. * split, or for creating an implicit identifier without using a String.
    6843. *
    6844. * @constructs
    6845. * @augments patio.sql.GenericExpression
    6846. * @augments patio.sql.QualifyingMethods
    6847. *
    6848. -
    6849. * @param table the table or schema to qualify the column or table to.
    6850. -
    6851. * @param column the column or table to qualify.
    6852. +
    6853. * @param {String}value the identifier.
    6854. *
    6855. -
    6856. * @property table <b>READ ONLY</b> the table or schema to qualify the column or table to.
    6857. -
    6858. * @property column <b>READ ONLY</b> he column or table to qualify.
    6859. +
    6860. * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
    6861. */
    6862. -
    6863. constructor:function (table, column) {
    6864. -
    6865. 4451 this.__table = table;
    6866. -
    6867. 4451 this.__column = column;
    6868. +
    6869. constructor:function (value) {
    6870. +
    6871. 16414 this.__value = value;
    6872. },
    6873. /**
    6874. -
    6875. * Converts the {@link patio.sql.QualifiedIdentifier} to a string.
    6876. +
    6877. * Converts the {@link patio.sql.Identifier} to a string.
    6878. *
    6879. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6880. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6881. *
    6882. -
    6883. * @return String the SQL version of the {@link patio.sql.QualifiedIdentifier}.
    6884. +
    6885. * @return String the SQL version of the {@link patio.sql.Identifier}.
    6886. */
    6887. toString:function (ds) {
    6888. -
    6889. 4532 !Dataset && (Dataset = require("./dataset"));
    6890. -
    6891. 4532 ds = ds || new Dataset();
    6892. -
    6893. 4532 return ds.qualifiedIdentifierSql(this);
    6894. +
    6895. 21906 !Dataset && (Dataset = require("./dataset"));
    6896. +
    6897. 21906 ds = ds || new Dataset();
    6898. +
    6899. 21906 return ds.quoteIdentifier(this);
    6900. },
    6901. /**@ignore*/
    6902. getters:{
    6903. -
    6904. table:function () {
    6905. -
    6906. 4554 return this.__table;
    6907. -
    6908. },
    6909. -
    6910. -
    6911. column:function () {
    6912. -
    6913. 4559 return this.__column;
    6914. +
    6915. value:function () {
    6916. +
    6917. 25154 return this.__value;
    6918. }
    6919. }
    6920. }
    6921. -
    6922. }).as(sql, "QualifiedIdentifier");
    6923. -
    6924. +
    6925. }).as(sql, "Identifier");
    6926. -
    6927. 1var likeElement = function (re) {
    6928. -
    6929. 875 var ret;
    6930. -
    6931. 875 if (isRegExp(re)) {
    6932. -
    6933. 80 ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase]
    6934. -
    6935. } else {
    6936. -
    6937. 795 ret = [re, false, false];
    6938. -
    6939. }
    6940. -
    6941. 875 return ret;
    6942. -
    6943. };
    6944. -
    6945. /**
    6946. -
    6947. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    6948. -
    6949. * in a text/string/varchar value in SQL.
    6950. -
    6951. *
    6952. -
    6953. * @augments patio.sql.ComplexExpression
    6954. -
    6955. * @augments patio.sql.StringMethods
    6956. -
    6957. * @augments patio.sql.StringConcatenationMethods
    6958. -
    6959. * @augments patio.sql.InequalityMethods
    6960. -
    6961. * @augments patio.sql.NoBooleanInputMethods
    6962. -
    6963. * @name StringExpression
    6964. -
    6965. * @memberOf patio.sql
    6966. -
    6967. */
    6968. -
    6969. 1var StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {
    6970. -
    6971. static:{
    6972. -
    6973. /**@lends patio.sql.StringExpression*/
    6974. +
    6975. 1var JoinClause = define(Expression, {
    6976. +
    6977. instance:{
    6978. +
    6979. /**@lends patio.sql.JoinClause.prototype*/
    6980. /**
    6981. -
    6982. * <p>Creates a SQL pattern match expression. left (l) is the SQL string we
    6983. -
    6984. * are matching against, and ces are the patterns we are matching.
    6985. -
    6986. * The match succeeds if any of the patterns match (SQL OR).</p>
    6987. +
    6988. * Represents an SQL JOIN clause, used for joining tables.
    6989. +
    6990. * Created by {@link patio.Dataset} join methods.
    6991. +
    6992. * @constructs
    6993. +
    6994. * @augments patio.sql.Expression
    6995. *
    6996. -
    6997. * <p>If a regular expression is used as a pattern, an SQL regular expression will be
    6998. -
    6999. * used, which is currently only supported on MySQL and PostgreSQL. Be aware
    7000. -
    7001. * that MySQL and PostgreSQL regular expression syntax is similar to javascript
    7002. -
    7003. * regular expression syntax, but it not exactly the same, especially for
    7004. -
    7005. * advanced regular expression features. Patio just uses the source of the
    7006. -
    7007. * regular expression verbatim as the SQL regular expression string.</p>
    7008. +
    7009. * @param {String} joinType the type of join this JoinClause should use
    7010. +
    7011. * @param table the table to join with
    7012. +
    7013. * @param tableAlias the alias to use for this join clause
    7014. *
    7015. -
    7016. * <p>If any other object is used as a regular expression, the SQL LIKE operator will
    7017. -
    7018. * be used, and should be supported by most databases.</p>
    7019. +
    7020. * @property {String} joinType <b>READ ONLY</b> the type of join this JoinClause should use
    7021. +
    7022. * @property table <b>READ ONLY</b> the table to join with
    7023. +
    7024. * @property joinType <b>READ ONLY</b> the alias to use for this join clause
    7025. +
    7026. * */
    7027. +
    7028. constructor:function (joinType, table, tableAlias) {
    7029. +
    7030. 791 this.__joinType = joinType;
    7031. +
    7032. 791 this.__table = table;
    7033. +
    7034. 791 this.__tableAlias = tableAlias || null;
    7035. +
    7036. },
    7037. +
    7038. +
    7039. /**
    7040. +
    7041. * Converts the {@link patio.sql.JoinClause} to a string.
    7042. *
    7043. -
    7044. * <p>The pattern match will be case insensitive if the last argument is a hash
    7045. -
    7046. * with a key of caseInsensitive that is not false or null. Also,
    7047. -
    7048. * if a case insensitive regular expression is used (//i), that particular
    7049. -
    7050. * pattern which will always be case insensitive.</p>
    7051. +
    7052. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    7053. +
    7054. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    7055. *
    7056. -
    7057. * @example
    7058. -
    7059. * StringExpression.like(sql.a, 'a%') //=> "a" LIKE 'a%'
    7060. -
    7061. * StringExpression.like(sql.a, 'a%', {caseInsensitive : true}) //=> "a" ILIKE 'a%'
    7062. -
    7063. * StringExpression.like(sql.a, 'a%', /^a/i) //=> "a" LIKE 'a%' OR "a" ~* '^a'
    7064. +
    7065. * @return String the SQL version of the {@link patio.sql.JoinClause}.
    7066. */
    7067. -
    7068. like:function (l) {
    7069. -
    7070. 437 var args = argsToArray(arguments, 1);
    7071. -
    7072. 437 var params = likeElement(l);
    7073. -
    7074. 437 var likeMap = this.likeMap;
    7075. -
    7076. 437 var lh = params[0], lre = params[1], lci = params[2];
    7077. -
    7078. 437 var last = args[args.length - 1];
    7079. -
    7080. 437 lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;
    7081. -
    7082. 437 args = args.map(function (ce) {
    7083. -
    7084. 438 var r, rre, rci;
    7085. -
    7086. 438 var ceArr = likeElement(ce);
    7087. -
    7088. 438 r = ceArr[0], rre = ceArr[1], rci = ceArr[2];
    7089. -
    7090. 438 return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r)
    7091. -
    7092. }, this);
    7093. -
    7094. 437 return args.length == 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));
    7095. +
    7096. toString:function (ds) {
    7097. +
    7098. 17 !Dataset && (Dataset = require("./dataset"));
    7099. +
    7100. 17 ds = ds || new Dataset();
    7101. +
    7102. 17 return ds.joinClauseSql(this);
    7103. },
    7104. -
    7105. /**
    7106. -
    7107. * Like map used to by {@link patio.sql.StringExpression.like} to create the
    7108. -
    7109. * LIKE expression.
    7110. -
    7111. * @type Object
    7112. -
    7113. */
    7114. -
    7115. likeMap:{"truetrue":'~*', "truefalse":"~", "falsetrue":"ILIKE", "falsefalse":"LIKE"}
    7116. +
    7117. /**@ignore*/
    7118. +
    7119. getters:{
    7120. +
    7121. joinType:function () {
    7122. +
    7123. 927 return this.__joinType;
    7124. +
    7125. },
    7126. +
    7127. table:function () {
    7128. +
    7129. 928 return this.__table;
    7130. +
    7131. },
    7132. +
    7133. tableAlias:function () {
    7134. +
    7135. 926 return this.__tableAlias;
    7136. +
    7137. }
    7138. +
    7139. }
    7140. }
    7141. -
    7142. }).as(sql, "StringExpression");
    7143. +
    7144. }).as(sql, "JoinClause");
    7145. -
    7146. 1var SubScript = define(GenericExpression, {
    7147. -
    7148. instance:{
    7149. -
    7150. /**@lends patio.sql.SubScript.prototype*/
    7151. +
    7152. 1var JoinOnClause = define(JoinClause, {
    7153. +
    7154. instance:{
    7155. +
    7156. /**@lends patio.sql.JoinOnClause.prototype*/
    7157. /**
    7158. -
    7159. * Represents an SQL array access, with multiple possible arguments.
    7160. +
    7161. * Represents an SQL JOIN clause with ON conditions. Created by {@link patio.Dataset} join methods.
    7162. +
    7163. * See {@link patio.sql.JoinClause} for other argument parameters.
    7164. * @constructs
    7165. -
    7166. * @augments patio.sql.GenericExpression
    7167. +
    7168. * @augments patio.sql.JoinClause
    7169. *
    7170. -
    7171. * @param arrCol the SQL array column
    7172. -
    7173. * @param sub The array of subscripts to use (should be an array of numbers)
    7174. -
    7175. */
    7176. -
    7177. constructor:function (arrCol, sub) {
    7178. -
    7179. //The SQL array column
    7180. -
    7181. 109 this.__arrCol = arrCol;
    7182. -
    7183. //The array of subscripts to use (should be an array of numbers)
    7184. -
    7185. 109 this.__sub = sub;
    7186. -
    7187. },
    7188. -
    7189. -
    7190. /**
    7191. -
    7192. * Create a new {@link patio.sql.Subscript} appending the given subscript(s)
    7193. -
    7194. * the the current array of subscripts.
    7195. +
    7196. * @param on the expression to filter with. See {@link patio.Dataset#filter}
    7197. +
    7198. * @property on <b>READ ONLY</b> the filter to use with joining the datasets.
    7199. */
    7200. -
    7201. addSub:function (sub) {
    7202. -
    7203. 0 return new SubScript(this.__arrCol, this.__sub.concat(sub));
    7204. +
    7205. constructor:function (on, joinType, table, tableAlias) {
    7206. +
    7207. 761 this.__on = on;
    7208. +
    7209. 761 this._super(arguments, [joinType, table, tableAlias]);
    7210. },
    7211. /**
    7212. -
    7213. * Converts the {@link patio.sql.SubScript} to a string.
    7214. +
    7215. * Converts the {@link patio.sql.JoinOnClause} to a string.
    7216. *
    7217. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    7218. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    7219. *
    7220. -
    7221. * @return String the SQL version of the {@link patio.sql.SubScript}.
    7222. +
    7223. * @return String the SQL version of the {@link patio.sql.JoinOnClause}.
    7224. */
    7225. toString:function (ds) {
    7226. -
    7227. 109 !Dataset && (Dataset = require("./dataset"));
    7228. -
    7229. 109 ds = ds || new Dataset();
    7230. -
    7231. 109 return ds.subscriptSql(this);
    7232. +
    7233. 813 !Dataset && (Dataset = require("./dataset"));
    7234. +
    7235. 813 ds = ds || new Dataset();
    7236. +
    7237. 813 return ds.joinOnClauseSql(this);
    7238. },
    7239. /**@ignore*/
    7240. getters:{
    7241. -
    7242. f:function () {
    7243. -
    7244. 110 return this.__arrCol;
    7245. -
    7246. },
    7247. -
    7248. -
    7249. sub:function () {
    7250. -
    7251. 110 return this.__sub;
    7252. +
    7253. on:function () {
    7254. +
    7255. 813 return this.__on;
    7256. }
    7257. }
    7258. }
    7259. -
    7260. }).as(sql, "SubScript");
    7261. -
    7262. -
    7263. 1var STRING_METHODS = ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "quote",
    7264. -
    7265. "replace", "search", "slice", "split", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase",
    7266. -
    7267. "toSource", "toString", "toUpperCase", "trim", "trimLeft", "trimRight", "valueOf"];
    7268. -
    7269. +
    7270. }).as(sql, "JoinOnClause");
    7271. -
    7272. 1var addStringMethod = function (op) {
    7273. -
    7274. 24 return function () {
    7275. -
    7276. 4294 return this.__str[op].apply(this.__str, arguments);
    7277. -
    7278. }
    7279. -
    7280. };
    7281. -
    7282. 1var LiteralString = define([OrderedMethods, ComplexExpressionMethods, BooleanMethods, NumericMethods, StringMethods, InequalityMethods, AliasMethods], {
    7283. +
    7284. 1var JoinUsingClause = define(JoinClause, {
    7285. instance:{
    7286. -
    7287. /**@lends patio.sql.LiteralString*/
    7288. +
    7289. /**@lends patio.sql.JoinUsingClause.prototype*/
    7290. /**
    7291. -
    7292. * Represents a string that should be placed into a SQL query literally.
    7293. -
    7294. * <b>This class has all methods that a normal javascript String has.</b>
    7295. +
    7296. * Represents an SQL JOIN clause with USING conditions.
    7297. +
    7298. * Created by {@link patio.Dataset} join methods.
    7299. +
    7300. * See {@link patio.sql.JoinClause} for other argument parameters.
    7301. +
    7302. *
    7303. * @constructs
    7304. -
    7305. * @augments patio.sql.OrderedMethods
    7306. -
    7307. * @augments patio.sql.ComplexExpressionMethods
    7308. -
    7309. * @augments patio.sql.BooleanMethods
    7310. -
    7311. * @augments patio.sql.NumericMethods
    7312. -
    7313. * @augments patio.sql.StringMethods
    7314. -
    7315. * @augments patio.sql.InequalityMethods
    7316. -
    7317. * @augments patio.sql.AliasMethods
    7318. +
    7319. * @augments patio.sql.JoinClause
    7320. *
    7321. -
    7322. * @param {String} str the literal string.
    7323. +
    7324. * @param using the column/s to use when joining.
    7325. +
    7326. * @property using <b>READ ONLY</b> the column/s to use when joining.
    7327. */
    7328. -
    7329. constructor:function (str) {
    7330. -
    7331. 3677 this.__str = str;
    7332. -
    7333. }
    7334. -
    7335. }
    7336. -
    7337. }).as(sql, "LiteralString");
    7338. -
    7339. -
    7340. 1STRING_METHODS.forEach(function (op) {
    7341. -
    7342. 24 LiteralString.prototype[op] = addStringMethod(op);
    7343. -
    7344. }, this);
    7345. -
    7346. -
    7347. -
    7348. -
    7349. -
    7350. -
    7351. -
    7352. -
    7353. -
    7354. -
    7355. -
    7356. -
    7357. +
    7358. constructor:function (using, joinType, table, tableAlias) {
    7359. +
    7360. 8 this.__using = using.map(function (u) {
    7361. +
    7362. 9 return isString(u) ? new Identifier(u) : u;
    7363. +
    7364. });
    7365. +
    7366. 8 this._super(arguments, [joinType, table, tableAlias]);
    7367. +
    7368. },
    7369. +
    7370. /**
    7371. +
    7372. * Converts the {@link patio.sql.JoinUsingClause} to a string.
    7373. +
    7374. *
    7375. +
    7376. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    7377. +
    7378. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    7379. +
    7380. *
    7381. +
    7382. * @return String the SQL version of the {@link patio.sql.JoinUsingClause}.
    7383. +
    7384. */
    7385. +
    7386. toString:function (ds) {
    7387. +
    7388. 95 !Dataset && (Dataset = require("./dataset"));
    7389. +
    7390. 95 ds = ds || new Dataset();
    7391. +
    7392. 95 return ds.joinUsingClauseSql(this);
    7393. +
    7394. },
    7395. -
    -
    - -
    - - - - - -
    -
    migration.js
    -
    -
    - Coverage91.97 - SLOC607 - LOC274 - Missed22 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. hitch = comb.hitch,
    4. -
    5. Promise = comb.Promise,
    6. -
    7. errors = require("./errors"),
    8. -
    9. MigrationError = errors.MigrationError,
    10. -
    11. NotImplemented = errors.NotImplemented(),
    12. -
    13. format = comb.string.format,
    14. -
    15. define = comb.define,
    16. -
    17. isFunction = comb.isFunction,
    18. -
    19. serial = comb.serial,
    20. -
    21. isNumber = comb.isNumber,
    22. -
    23. when = comb.when,
    24. -
    25. isUndefined = comb.isUndefined,
    26. -
    27. fs = require("fs"),
    28. -
    29. path = require("path");
    30. +
    31. /**@ignore*/
    32. +
    33. getters:{
    34. +
    35. using:function () {
    36. +
    37. 95 return this.__using;
    38. +
    39. }
    40. +
    41. }
    42. +
    43. }
    44. +
    45. }).as(sql, "JoinUsingClause");
    46. -
    47. 1var Migrator = define(null, {
    48. +
    49. 1var PlaceHolderLiteralString = define(GenericExpression, {
    50. instance:{
    51. -
    52. /**@lends patio.migrations.Migrator.prototype*/
    53. -
    54. column:null,
    55. -
    56. db:null,
    57. -
    58. directory:null,
    59. -
    60. ds:null,
    61. -
    62. files:null,
    63. -
    64. table:null,
    65. -
    66. target:null,
    67. +
    68. /**@lends patio.sql.PlaceHolderLiteralString.prototype*/
    69. /**
    70. -
    71. * Abstract Migrator class. This class should be be instantiated directly.
    72. +
    73. * Represents a literal string with placeholders and arguments.
    74. +
    75. * This is necessary to ensure delayed literalization of the arguments
    76. +
    77. * required for the prepared statement support and for database-specific
    78. +
    79. * literalization.
    80. *
    81. * @constructs
    82. -
    83. * @param {patio.Database} db the database to migrate
    84. -
    85. * @param {String} directory directory that the migration files reside in
    86. -
    87. * @param {Object} [opts={}] optional parameters.
    88. -
    89. * @param {String} [opts.column] the column in the table that version information should be stored.
    90. -
    91. * @param {String} [opts.table] the table that version information should be stored.
    92. -
    93. * @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
    94. -
    95. * @param {String} [opts.current] the version that the database is currently at if the current version
    96. +
    97. * @augments patio.sql.GenericExpression
    98. +
    99. *
    100. +
    101. * @param {String} str the string that contains placeholders.
    102. +
    103. * @param {Array} args array of arguments that will be literalized using {@link patio.Dataset#literal}, and
    104. +
    105. * replaced in the string.
    106. +
    107. * @param {Boolean} [parens=false] set to true to wrap the string in parens.
    108. +
    109. *
    110. +
    111. * @property {String} str <b>READ ONLY</b> the string that contains placeholders.
    112. +
    113. * @property {Array} args <b>READ ONLY</b> array of arguments that will be literalized using {@link patio.Dataset#literal}, and
    114. +
    115. * replaced in the string.
    116. +
    117. * @property {String} parens <b>READ ONLY</b> set to true to wrap the string in parens.
    118. */
    119. -
    120. constructor:function (db, directory, opts) {
    121. -
    122. 31 this.db = db;
    123. -
    124. 31 this.directory = directory;
    125. -
    126. 31 opts = opts || {};
    127. -
    128. 31 this.table = opts.table || this._static.DEFAULT_SCHEMA_TABLE;
    129. -
    130. 31 this.column = opts.column || this._static.DEFAULT_SCHEMA_COLUMN;
    131. -
    132. 31 this._opts = opts;
    133. +
    134. constructor:function (str, args, parens) {
    135. +
    136. 53 parens = parens || false;
    137. +
    138. 53 var v;
    139. +
    140. 53 this.__str = str;
    141. +
    142. 53 this.__args = isArray(args) && args.length == 1 && isHash((v = args[0])) ? v : args;
    143. +
    144. 53 this.__parens = parens;
    145. },
    146. /**
    147. -
    148. * Runs the migration and returns a promise.
    149. +
    150. * Converts the {@link patio.sql.PlaceHolderLiteralString} to a string.
    151. +
    152. *
    153. +
    154. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    155. +
    156. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    157. +
    158. *
    159. +
    160. * @return String the SQL version of the {@link patio.sql.PlaceHolderLiteralString}.
    161. */
    162. -
    163. run:function () {
    164. -
    165. 0 throw new NotImplemented("patio.migrations.Migrator#run");
    166. +
    167. toString:function (ds) {
    168. +
    169. 52 !Dataset && (Dataset = require("./dataset"));
    170. +
    171. 52 ds = ds || new Dataset();
    172. +
    173. 52 return ds.placeholderLiteralStringSql(this);
    174. },
    175. -
    176. getFileNames:function () {
    177. -
    178. 50 if (!this.__files) {
    179. -
    180. 49 return this._static.getFileNames(this.directory).addCallback(hitch(this, function (files) {
    181. -
    182. 49 this.__files = files;
    183. -
    184. }));
    185. -
    186. } else {
    187. -
    188. 1 return new Promise().callback(this.__files).promise();
    189. +
    190. /**@ignore*/
    191. +
    192. getters:{
    193. +
    194. str:function () {
    195. +
    196. 55 return this.__str;
    197. +
    198. },
    199. +
    200. args:function () {
    201. +
    202. 55 return this.__args;
    203. +
    204. },
    205. +
    206. +
    207. parens:function () {
    208. +
    209. 55 return this.__parens;
    210. }
    211. -
    212. },
    213. -
    214. getMigrationVersionFromFile:function (filename) {
    215. -
    216. 476 return parseInt(path.basename(filename).split(this._static.MIGRATION_SPLITTER)[0], 10);
    217. }
    218. -
    219. },
    220. -
    221. -
    222. "static":{
    223. -
    224. /**@lends patio.migrations.Migrator*/
    225. +
    226. }
    227. +
    228. }).as(sql, "PlaceHolderLiteralString");
    229. -
    230. MIGRATION_FILE_PATTERN:/^\d+\..+\.js$/i,
    231. -
    232. MIGRATION_SPLITTER:'.',
    233. -
    234. MINIMUM_TIMESTAMP:20000101,
    235. -
    236. getFileNames:function (directory) {
    237. -
    238. 80 var ret = new Promise();
    239. -
    240. 80 fs.readdir(directory, hitch(this, function (err, files) {
    241. -
    242. 80 if (err) {
    243. -
    244. 0 ret.errback(err);
    245. -
    246. } else {
    247. -
    248. 80 files = files.filter(function (file) {
    249. -
    250. 394 return file.match(this.MIGRATION_FILE_PATTERN) !== null;
    251. -
    252. }, this);
    253. -
    254. 80 ret.callback(files.map(function (file) {
    255. -
    256. 394 return path.resolve(directory, file);
    257. -
    258. }));
    259. -
    260. }
    261. -
    262. }));
    263. -
    264. 80 return ret.promise();
    265. -
    266. },
    267. +
    268. 1var SQLFunction = define(GenericExpression, {
    269. +
    270. instance:{
    271. +
    272. /**@lends patio.sql.SQLFunction.prototype*/
    273. /**
    274. -
    275. * Migrates the database using migration files found in the supplied directory.
    276. -
    277. * See {@link patio#migrate}
    278. +
    279. * Represents an SQL function call.
    280. *
    281. -
    282. * @example
    283. -
    284. * var DB = patio.connect("my://connection/string");
    285. -
    286. * patio. migrate(DB, __dirname + "/timestamp_migration").then(function(){
    287. -
    288. * console.log("done migrating!");
    289. -
    290. * });
    291. +
    292. * @constructs
    293. +
    294. * @augments patio.sql.GenericExpression
    295. *
    296. -
    297. * patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).then(function(){
    298. -
    299. * console.log("done migrating down!");
    300. -
    301. * });
    302. +
    303. * @param {...} f variable number of arguments where the first argument is the name
    304. +
    305. * of the SQL function to invoke. The rest of the arguments will be literalized through
    306. +
    307. * {@link patio.Dataset#literal} and placed into the SQL function call.
    308. *
    309. +
    310. * @property {String} f <b>READ ONLY</b> the SQL function to call.
    311. +
    312. * @property {Array} args <b>READ ONLY</b> args arguments will be literalized through
    313. +
    314. * {@link patio.Dataset#literal} and placed into the SQL function call.
    315. +
    316. * */
    317. +
    318. constructor:function (f) {
    319. +
    320. 1109 var args = argsToArray(arguments).slice(1);
    321. +
    322. 1109 this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {
    323. +
    324. 773 return isString(a) ? sql.stringToIdentifier(a) : a;
    325. +
    326. });
    327. +
    328. },
    329. +
    330. +
    331. /**
    332. +
    333. * Converts the {@link patio.sql.SQLFunction} to a string.
    334. *
    335. -
    336. * @param {patio.Database} db the database to migrate
    337. -
    338. * @param {String} directory directory that the migration files reside in
    339. -
    340. * @param {Object} [opts={}] optional parameters.
    341. -
    342. * @param {String} [opts.column] the column in the table that version information should be stored.
    343. -
    344. * @param {String} [opts.table] the table that version information should be stored.
    345. -
    346. * @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
    347. -
    348. * @param {String} [opts.current] the version that the database is currently at if the current version
    349. -
    350. * is not provided it is retrieved from the database.
    351. +
    352. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    353. +
    354. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    355. *
    356. -
    357. * @return {Promise} a promise that is resolved once the migration is complete.
    358. +
    359. * @return String the SQL version of the {@link patio.sql.SQLFunction}.
    360. */
    361. -
    362. run:function (db, directory, opts, cb) {
    363. -
    364. 31 if (isFunction(opts)) {
    365. -
    366. 0 cb = opts;
    367. -
    368. 0 opts = {};
    369. -
    370. } else {
    371. -
    372. 31 opts = opts || {};
    373. -
    374. }
    375. -
    376. 31 opts = opts || {};
    377. -
    378. 31 var ret = new Promise();
    379. -
    380. 31 this.__getMigrator(directory).then(function (migrator) {
    381. -
    382. 31 new migrator(db, directory, opts).run().then(ret);
    383. -
    384. }, ret);
    385. -
    386. 31 ret.classic(cb);
    387. -
    388. 31 return ret.promise();
    389. +
    390. toString:function (ds) {
    391. +
    392. 565 !Dataset && (Dataset = require("./dataset"));
    393. +
    394. 565 ds = ds || new Dataset();
    395. +
    396. 565 return ds.functionSql(this);
    397. },
    398. -
    399. // Choose the Migrator subclass to use. Uses the TimestampMigrator
    400. -
    401. // // if the version number appears to be a unix time integer for a year
    402. -
    403. // after 2005, otherwise uses the IntegerMigrator.
    404. -
    405. __getMigrator:function (directory) {
    406. -
    407. 31 var ret = new Promise();
    408. -
    409. 31 var retClass = IntegerMigrator;
    410. -
    411. 31 this.getFileNames(directory).then(hitch(this, function (files) {
    412. -
    413. 31 var l = files.length;
    414. -
    415. 31 if (l) {
    416. -
    417. 31 for (var i = 0; i < l; i++) {
    418. -
    419. 81 var file = files[i];
    420. -
    421. 81 if (parseInt(path.basename(file).split(this.MIGRATION_SPLITTER)[0], 10) > this.MINIMUM_TIMESTAMP) {
    422. -
    423. 18 retClass = TimestampMigrator;
    424. -
    425. 18 break;
    426. -
    427. }
    428. -
    429. }
    430. -
    431. }
    432. -
    433. 31 ret.callback(retClass);
    434. +
    435. /**@ignore*/
    436. +
    437. getters:{
    438. +
    439. f:function () {
    440. +
    441. 567 return this.__f;
    442. +
    443. },
    444. -
    445. }), ret);
    446. -
    447. 31 return ret.promise();
    448. +
    449. args:function () {
    450. +
    451. 567 return this.__args;
    452. +
    453. }
    454. }
    455. }
    456. -
    457. });
    458. -
    459. +
    460. }).as(sql, "SQLFunction");
    461. /**
    462. -
    463. * @class Migrator that uses the file format {migrationName}.{version}.js, where version starts at 0.
    464. -
    465. * <b>Missing migrations are not allowed</b>
    466. +
    467. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    468. +
    469. * in a numeric value in SQL.
    470. *
    471. -
    472. * @augments patio.migrations.Migrator
    473. -
    474. * @name IntegerMigrator
    475. -
    476. * @memberOf patio.migrations
    477. +
    478. * @name NumericExpression
    479. +
    480. * @memberOf patio.sql
    481. +
    482. * @augments patio.sql.ComplexExpression
    483. +
    484. * @augments patio.sql.BitWiseMethods
    485. +
    486. * @augments patio.sql.NumericMethods
    487. +
    488. * @augments patio.sql.InequalityMethods
    489. */
    490. -
    491. 1var IntegerMigrator = define(Migrator, {
    492. -
    493. instance:{
    494. -
    495. /**@lends patio.migrations.IntegerMigrator.prototype*/
    496. -
    497. current:null,
    498. -
    499. direction:null,
    500. -
    501. migrations:null,
    502. +
    503. 1var NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");
    504. -
    505. _migrationFiles:null,
    506. -
    507. run:function () {
    508. -
    509. 13 var ret = new Promise(), DB = this.db;
    510. -
    511. 13 serial([this._getLatestMigrationVersion.bind(this), this._getCurrentMigrationVersion.bind(this)]).then(hitch(this, function (res) {
    512. -
    513. 11 var target = res[0], current = res[1];
    514. -
    515. 11 if (current !== target) {
    516. -
    517. 11 var direction = this.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;
    518. -
    519. 11 this._getMigrations(current, target, direction).then(hitch(this, function (migrations) {
    520. -
    521. 11 var runMigration = hitch(this, function (index) {
    522. -
    523. 60 if (index >= migrations.length) {
    524. -
    525. 11 ret.callback(version);
    526. -
    527. } else {
    528. -
    529. 49 var curr = migrations[index], migration = curr[0];
    530. -
    531. 49 version = curr[1];
    532. -
    533. 49 var now = new Date();
    534. -
    535. 49 var lv = isUp ? version : version - 1;
    536. -
    537. 49 DB.logInfo("Begin applying migration version %d, direction: %s", lv, direction);
    538. -
    539. 49 DB.transaction(hitch(this, function () {
    540. -
    541. 49 var ret = new Promise();
    542. -
    543. 49 if (!isFunction(migration[direction])) {
    544. -
    545. 0 this._setMigrationVersion(lv).then(ret);
    546. -
    547. } else {
    548. -
    549. 49 when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
    550. -
    551. 49 this._setMigrationVersion(lv).then(ret);
    552. -
    553. }), ret);
    554. -
    555. }
    556. -
    557. 49 return ret.promise();
    558. -
    559. })).then(function () {
    560. -
    561. 49 DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);
    562. -
    563. 49 runMigration(index + 1);
    564. -
    565. }, ret);
    566. -
    567. }
    568. +
    569. 1var OrderedExpression = define(Expression, {
    570. +
    571. instance:{
    572. +
    573. /**@lends patio.sql.OrderedExpression.prototype*/
    574. -
    575. });
    576. -
    577. 11 runMigration(0);
    578. -
    579. }), ret);
    580. -
    581. } else {
    582. -
    583. 0 ret.callback(target);
    584. -
    585. }
    586. +
    587. /**
    588. +
    589. * Represents a column/expression to order the result set by.
    590. +
    591. * @constructs
    592. +
    593. * @augments patio.sql.Expression
    594. +
    595. *
    596. +
    597. * @param expression the expression to order
    598. +
    599. * @param {Boolean}[descending=true] set to false to order ASC
    600. +
    601. * @param {String|Object} [opts=null] additional options
    602. +
    603. * <ul>
    604. +
    605. * <li>String: if value is "first" the null values will be first, if "last" then null values
    606. +
    607. * will be last</li>
    608. +
    609. * <li>Object: will pull the nulls property off of the object use use the same rules as if it
    610. +
    611. * were a string</li>
    612. +
    613. * </ul>
    614. +
    615. * @property expression <b>READ ONLY</b> the expression to order.
    616. +
    617. * @property {Boolean} [descending=true] <b>READ ONLY</b> true if decending, false otherwise.
    618. +
    619. * @property {String} [nulls=null] if value is "first" the null values will be first, if "last" then null values
    620. +
    621. * will be last
    622. +
    623. */
    624. +
    625. constructor:function (expression, descending, opts) {
    626. +
    627. 92 descending = isBoolean(descending) ? descending : true;
    628. +
    629. 92 opts = opts || {};
    630. +
    631. 92 this.__expression = expression;
    632. +
    633. 92 this.__descending = descending;
    634. +
    635. 92 var nulls = isString(opts) ? opts : opts.nulls;
    636. +
    637. 92 this.__nulls = isString(nulls) ? nulls.toLowerCase() : null;
    638. +
    639. },
    640. -
    641. }), ret);
    642. -
    643. 13 return ret.promise();
    644. +
    645. /**
    646. +
    647. * @return {patio.sql.OrderedExpression} a copy that is ordered ASC
    648. +
    649. */
    650. +
    651. asc:function () {
    652. +
    653. 0 return new OrderedExpression(this.__expression, false, {nulls:this.__nulls});
    654. },
    655. -
    656. _getMigrations:function (current, target, direction) {
    657. -
    658. 11 var ret = new Promise(), isUp = direction === "up", migrations = [];
    659. -
    660. 11 when(this._getMigrationFiles()).then(function (files) {
    661. -
    662. 11 try {
    663. -
    664. 11 if ((isUp ? target : current - 1) < files.length) {
    665. -
    666. 11 if (isUp) {
    667. -
    668. 8 current++;
    669. -
    670. }
    671. -
    672. 11 for (; isUp ? current <= target : current > target; isUp ? current++ : current--) {
    673. -
    674. 49 migrations.push([require(files[current]), current]);
    675. -
    676. }
    677. -
    678. } else {
    679. -
    680. 0 return ret.errback(new MigrationError("Invalid target " + target));
    681. -
    682. }
    683. -
    684. } catch (e) {
    685. -
    686. 0 return ret.errback(e);
    687. -
    688. }
    689. -
    690. 11 ret.callback(migrations);
    691. -
    692. }, ret);
    693. -
    694. 11 return ret.promise();
    695. +
    696. /**
    697. +
    698. * @return {patio.sql.OrderedExpression} Return a copy that is ordered DESC
    699. +
    700. */
    701. +
    702. desc:function () {
    703. +
    704. 0 return new OrderedExpression(this.__expression, true, {nulls:this.__nulls});
    705. +
    706. },
    707. +
    708. +
    709. /**
    710. +
    711. * * @return {patio.sql.OrderedExpression} an inverted expression, changing ASC to DESC and NULLS FIRST to NULLS LAST.
    712. +
    713. * */
    714. +
    715. invert:function () {
    716. +
    717. 17 return new OrderedExpression(this.__expression, !this.__descending, {nulls:this._static.INVERT_NULLS[this.__nulls] || this.__nulls});
    718. },
    719. +
    720. /**
    721. +
    722. * Converts the {@link patio.sql.OrderedExpression} to a string.
    723. +
    724. *
    725. +
    726. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    727. +
    728. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    729. +
    730. *
    731. +
    732. * @return String the SQL version of the {@link patio.sql.OrderedExpression}.
    733. +
    734. */
    735. +
    736. toString:function (ds) {
    737. +
    738. 73 !Dataset && (Dataset = require("./dataset"));
    739. +
    740. 73 ds = ds || new Dataset();
    741. +
    742. 73 return ds.orderedExpressionSql(this);
    743. +
    744. },
    745. -
    746. _getMigrationFiles:function () {
    747. -
    748. 19 var ret = new Promise();
    749. -
    750. 19 if (!this._migrationFiles) {
    751. -
    752. 13 var retFiles = [];
    753. -
    754. 13 var directory = this.directory;
    755. -
    756. 13 this.getFileNames().then(hitch(this, function (files) {
    757. -
    758. 13 var l = files.length;
    759. -
    760. 13 if (l) {
    761. -
    762. 13 for (var i = 0; i < l; i++) {
    763. -
    764. 59 var file = files[i];
    765. -
    766. 59 var version = this.getMigrationVersionFromFile(file);
    767. -
    768. 59 if (isUndefined(retFiles[version])) {
    769. -
    770. 58 retFiles[version] = file;
    771. -
    772. } else {
    773. -
    774. 1 return ret.errback(new MigrationError("Duplicate migration number " + version));
    775. -
    776. }
    777. -
    778. }
    779. -
    780. 12 if (isUndefined(retFiles[0])) {
    781. -
    782. 0 retFiles.shift();
    783. -
    784. }
    785. -
    786. 12 for (var j = 0; j < l; j++) {
    787. -
    788. 57 if (isUndefined(retFiles[j])) {
    789. -
    790. 1 return ret.errback(new MigrationError("Missing migration for " + j));
    791. -
    792. }
    793. -
    794. }
    795. -
    796. }
    797. -
    798. 11 this._migrationFiles = retFiles;
    799. -
    800. 11 ret.callback(retFiles);
    801. -
    802. }), ret);
    803. -
    804. } else {
    805. -
    806. 6 ret.callback(this._migrationFiles);
    807. +
    808. /**@ignore*/
    809. +
    810. getters:{
    811. +
    812. expression:function () {
    813. +
    814. 75 return this.__expression;
    815. +
    816. },
    817. +
    818. descending:function () {
    819. +
    820. 75 return this.__descending;
    821. +
    822. },
    823. +
    824. nulls:function () {
    825. +
    826. 82 return this.__nulls;
    827. }
    828. -
    829. 19 return ret.promise();
    830. +
    831. }
    832. +
    833. },
    834. +
    835. static:{
    836. +
    837. /**@lends patio.sql.OrderedExpression*/
    838. +
    839. /**
    840. +
    841. * Hash that contains the inversions for "first" and "last".
    842. +
    843. * @type Object
    844. +
    845. * @default {first:"last", last:"first"}
    846. +
    847. */
    848. +
    849. INVERT_NULLS:{first:"last", last:"first"}
    850. +
    851. }
    852. +
    853. }).as(sql, "OrderedExpression");
    854. +
    855. +
    856. 1var QualifiedIdentifier = define([GenericExpression, QualifyingMethods], {
    857. +
    858. instance:{
    859. +
    860. /**@lends patio.sql.QualifiedIdentifier.prototype*/
    861. +
    862. +
    863. /**
    864. +
    865. * Represents a qualified identifier (column with table or table with schema).
    866. +
    867. *
    868. +
    869. * @constructs
    870. +
    871. * @augments patio.sql.GenericExpression
    872. +
    873. * @augments patio.sql.QualifyingMethods
    874. +
    875. *
    876. +
    877. * @param table the table or schema to qualify the column or table to.
    878. +
    879. * @param column the column or table to qualify.
    880. +
    881. *
    882. +
    883. * @property table <b>READ ONLY</b> the table or schema to qualify the column or table to.
    884. +
    885. * @property column <b>READ ONLY</b> he column or table to qualify.
    886. +
    887. */
    888. +
    889. constructor:function (table, column) {
    890. +
    891. 4451 this.__table = table;
    892. +
    893. 4451 this.__column = column;
    894. },
    895. -
    896. _getLatestMigrationVersion:function () {
    897. -
    898. 13 var ret = new Promise();
    899. -
    900. 13 if (!isUndefined(this._opts.target)) {
    901. -
    902. 5 ret.callback(this._opts.target);
    903. -
    904. } else {
    905. -
    906. 8 this._getMigrationFiles().then(hitch(this, function (files) {
    907. -
    908. 6 var l = files[files.length - 1];
    909. -
    910. 6 ret.callback(l ? this.getMigrationVersionFromFile(path.basename(l)) : null);
    911. -
    912. }), ret);
    913. -
    914. }
    915. -
    916. 13 return ret.promise();
    917. +
    918. /**
    919. +
    920. * Converts the {@link patio.sql.QualifiedIdentifier} to a string.
    921. +
    922. *
    923. +
    924. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    925. +
    926. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    927. +
    928. *
    929. +
    930. * @return String the SQL version of the {@link patio.sql.QualifiedIdentifier}.
    931. +
    932. */
    933. +
    934. toString:function (ds) {
    935. +
    936. 4532 !Dataset && (Dataset = require("./dataset"));
    937. +
    938. 4532 ds = ds || new Dataset();
    939. +
    940. 4532 return ds.qualifiedIdentifierSql(this);
    941. },
    942. -
    943. _getCurrentMigrationVersion:function () {
    944. -
    945. 11 var ret = new Promise();
    946. -
    947. 11 if (!isUndefined(this._opts.current)) {
    948. -
    949. 2 ret.callback(this._opts.current);
    950. -
    951. } else {
    952. -
    953. 9 when(this._getSchemaDataset()).then(hitch(this, function (ds) {
    954. -
    955. 9 ds.get(this.column).then(ret);
    956. -
    957. }), ret);
    958. +
    959. /**@ignore*/
    960. +
    961. getters:{
    962. +
    963. table:function () {
    964. +
    965. 4554 return this.__table;
    966. +
    967. },
    968. +
    969. +
    970. column:function () {
    971. +
    972. 4559 return this.__column;
    973. }
    974. -
    975. 11 return ret.promise();
    976. -
    977. },
    978. +
    979. }
    980. +
    981. }
    982. +
    983. }).as(sql, "QualifiedIdentifier");
    984. -
    985. _setMigrationVersion:function (version) {
    986. -
    987. 49 var ret = new Promise(), c = this.column;
    988. -
    989. 49 this._getSchemaDataset().then(function (ds) {
    990. -
    991. 49 var item = {};
    992. -
    993. 49 item[c] = version;
    994. -
    995. 49 ds.update(item).both(ret);
    996. -
    997. }, ret);
    998. -
    999. 49 return ret.promise();
    1000. -
    1001. },
    1002. +
    1003. 1var likeElement = function (re) {
    1004. +
    1005. 875 var ret;
    1006. +
    1007. 875 if (isRegExp(re)) {
    1008. +
    1009. 80 ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase]
    1010. +
    1011. } else {
    1012. +
    1013. 795 ret = [re, false, false];
    1014. +
    1015. }
    1016. +
    1017. 875 return ret;
    1018. +
    1019. };
    1020. +
    1021. /**
    1022. +
    1023. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    1024. +
    1025. * in a text/string/varchar value in SQL.
    1026. +
    1027. *
    1028. +
    1029. * @augments patio.sql.ComplexExpression
    1030. +
    1031. * @augments patio.sql.StringMethods
    1032. +
    1033. * @augments patio.sql.StringConcatenationMethods
    1034. +
    1035. * @augments patio.sql.InequalityMethods
    1036. +
    1037. * @augments patio.sql.NoBooleanInputMethods
    1038. +
    1039. * @name StringExpression
    1040. +
    1041. * @memberOf patio.sql
    1042. +
    1043. */
    1044. +
    1045. 1var StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {
    1046. +
    1047. static:{
    1048. +
    1049. /**@lends patio.sql.StringExpression*/
    1050. -
    1051. _getSchemaDataset:function () {
    1052. -
    1053. 58 var c = this.column, table = this.table;
    1054. -
    1055. 58 var ret = new Promise();
    1056. -
    1057. 58 if (!this.__schemaDataset) {
    1058. -
    1059. 11 var ds = this.db.from(table);
    1060. -
    1061. 11 this.__createOrAlterMigrationTable().then(hitch(this, function () {
    1062. -
    1063. 11 ds.isEmpty().then(hitch(this, function (empty) {
    1064. -
    1065. 11 if (empty) {
    1066. -
    1067. 0 var item = {};
    1068. -
    1069. 0 item[c] = -1;
    1070. -
    1071. 0 this.__schemaDataset = ds;
    1072. -
    1073. 0 ds.insert(item).then(hitch(ret, "callback", ds), ret);
    1074. -
    1075. } else {
    1076. -
    1077. 11 ds.count().then(hitch(this, function (count) {
    1078. -
    1079. 11 if (count > 1) {
    1080. -
    1081. 0 ret.errback(new Error("More than one row in migrator table"));
    1082. -
    1083. } else {
    1084. -
    1085. 11 this.__schemaDataset = ds;
    1086. -
    1087. 11 ret.callback(ds);
    1088. -
    1089. }
    1090. -
    1091. }), ret);
    1092. -
    1093. }
    1094. -
    1095. }), ret);
    1096. -
    1097. }), ret);
    1098. -
    1099. } else {
    1100. -
    1101. 47 ret.callback(this.__schemaDataset);
    1102. -
    1103. }
    1104. -
    1105. 58 return ret.promise();
    1106. +
    1107. /**
    1108. +
    1109. * <p>Creates a SQL pattern match expression. left (l) is the SQL string we
    1110. +
    1111. * are matching against, and ces are the patterns we are matching.
    1112. +
    1113. * The match succeeds if any of the patterns match (SQL OR).</p>
    1114. +
    1115. *
    1116. +
    1117. * <p>If a regular expression is used as a pattern, an SQL regular expression will be
    1118. +
    1119. * used, which is currently only supported on MySQL and PostgreSQL. Be aware
    1120. +
    1121. * that MySQL and PostgreSQL regular expression syntax is similar to javascript
    1122. +
    1123. * regular expression syntax, but it not exactly the same, especially for
    1124. +
    1125. * advanced regular expression features. Patio just uses the source of the
    1126. +
    1127. * regular expression verbatim as the SQL regular expression string.</p>
    1128. +
    1129. *
    1130. +
    1131. * <p>If any other object is used as a regular expression, the SQL LIKE operator will
    1132. +
    1133. * be used, and should be supported by most databases.</p>
    1134. +
    1135. *
    1136. +
    1137. * <p>The pattern match will be case insensitive if the last argument is a hash
    1138. +
    1139. * with a key of caseInsensitive that is not false or null. Also,
    1140. +
    1141. * if a case insensitive regular expression is used (//i), that particular
    1142. +
    1143. * pattern which will always be case insensitive.</p>
    1144. +
    1145. *
    1146. +
    1147. * @example
    1148. +
    1149. * StringExpression.like(sql.a, 'a%') //=> "a" LIKE 'a%'
    1150. +
    1151. * StringExpression.like(sql.a, 'a%', {caseInsensitive : true}) //=> "a" ILIKE 'a%'
    1152. +
    1153. * StringExpression.like(sql.a, 'a%', /^a/i) //=> "a" LIKE 'a%' OR "a" ~* '^a'
    1154. +
    1155. */
    1156. +
    1157. like:function (l) {
    1158. +
    1159. 437 var args = argsToArray(arguments, 1);
    1160. +
    1161. 437 var params = likeElement(l);
    1162. +
    1163. 437 var likeMap = this.likeMap;
    1164. +
    1165. 437 var lh = params[0], lre = params[1], lci = params[2];
    1166. +
    1167. 437 var last = args[args.length - 1];
    1168. +
    1169. 437 lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;
    1170. +
    1171. 437 args = args.map(function (ce) {
    1172. +
    1173. 438 var r, rre, rci;
    1174. +
    1175. 438 var ceArr = likeElement(ce);
    1176. +
    1177. 438 r = ceArr[0], rre = ceArr[1], rci = ceArr[2];
    1178. +
    1179. 438 return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r)
    1180. +
    1181. }, this);
    1182. +
    1183. 437 return args.length == 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));
    1184. },
    1185. -
    1186. __createOrAlterMigrationTable:function () {
    1187. -
    1188. 11 var c = this.column, table = this.table, db = this.db;
    1189. -
    1190. 11 var ds = this.db.from(table);
    1191. -
    1192. 11 var ret = new Promise();
    1193. -
    1194. 11 db.tableExists(table).then(hitch(this, function (exists) {
    1195. -
    1196. 11 if (!exists) {
    1197. -
    1198. 6 db.createTable(table,
    1199. -
    1200. function () {
    1201. -
    1202. 6 this.column(c, "integer", {"default":-1, allowNull:false});
    1203. -
    1204. }).then(ret);
    1205. -
    1206. } else {
    1207. -
    1208. 5 ds.columns.then(function (columns) {
    1209. -
    1210. 5 if (columns.indexOf(c) === -1) {
    1211. -
    1212. 1 db.addColumn(table, c, "integer", {"default":-1, allowNull:false})
    1213. -
    1214. .then(ret);
    1215. -
    1216. } else {
    1217. -
    1218. 4 ret.callback();
    1219. -
    1220. }
    1221. -
    1222. });
    1223. -
    1224. }
    1225. -
    1226. }), ret);
    1227. -
    1228. 11 return ret.promise();
    1229. -
    1230. }
    1231. +
    1232. /**
    1233. +
    1234. * Like map used to by {@link patio.sql.StringExpression.like} to create the
    1235. +
    1236. * LIKE expression.
    1237. +
    1238. * @type Object
    1239. +
    1240. */
    1241. +
    1242. likeMap:{"truetrue":'~*', "truefalse":"~", "falsetrue":"ILIKE", "falsefalse":"LIKE"}
    1243. -
    1244. },
    1245. -
    1246. static:{
    1247. -
    1248. DEFAULT_SCHEMA_COLUMN:"version",
    1249. -
    1250. DEFAULT_SCHEMA_TABLE:"schema_info"
    1251. }
    1252. -
    1253. }).as(exports, "IntegerMigrator");
    1254. -
    1255. +
    1256. }).as(sql, "StringExpression");
    1257. -
    1258. /**
    1259. -
    1260. * @class Migrator that uses the file format {migrationName}.{timestamp}.js, where the timestamp
    1261. -
    1262. * can be anything greater than 20000101.
    1263. -
    1264. *
    1265. -
    1266. * @name TimestampMigrator
    1267. -
    1268. * @augments patio.migrations.Migrator
    1269. -
    1270. * @memberOf patio.migrations
    1271. -
    1272. */
    1273. -
    1274. 1var TimestampMigrator = define(Migrator, {
    1275. +
    1276. 1var SubScript = define(GenericExpression, {
    1277. instance:{
    1278. +
    1279. /**@lends patio.sql.SubScript.prototype*/
    1280. -
    1281. constructor:function (db, directory, opts) {
    1282. -
    1283. 18 this._super(arguments);
    1284. -
    1285. 18 opts = opts || {};
    1286. -
    1287. 18 this.target = opts.target;
    1288. -
    1289. },
    1290. -
    1291. -
    1292. run:function () {
    1293. -
    1294. 18 var ret = new Promise(), DB = this.db, column = this.column;
    1295. -
    1296. 18 serial([this.__getMirationFiles.bind(this), this._getSchemaDataset.bind(this)]).then(function (res) {
    1297. -
    1298. 18 var migrations = res[0], ds = res[1];
    1299. -
    1300. 18 var runMigration = hitch(this, function (index) {
    1301. -
    1302. 86 if (index >= migrations.length) {
    1303. -
    1304. 16 ret.callback();
    1305. -
    1306. } else {
    1307. -
    1308. 70 var curr = migrations[index], file = curr[0], migration = curr[1], direction = curr[2];
    1309. -
    1310. 70 var now = new Date();
    1311. -
    1312. 70 DB.logInfo("Begin applying migration file %s, direction: %s", file, direction);
    1313. -
    1314. 70 DB.transaction(hitch(this, function () {
    1315. -
    1316. 70 var ret = new Promise();
    1317. -
    1318. 70 when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
    1319. -
    1320. 68 var fileLowerCase = file.toLowerCase();
    1321. -
    1322. 68 var query = {};
    1323. -
    1324. 68 query[column] = fileLowerCase;
    1325. -
    1326. 68 (direction === "up" ? ds.insert(query) : ds.filter(query).remove()).then(ret);
    1327. -
    1328. }), ret);
    1329. -
    1330. 68 return ret.promise();
    1331. -
    1332. })).then(function () {
    1333. -
    1334. 68 DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);
    1335. -
    1336. 68 runMigration(index + 1);
    1337. -
    1338. }, ret);
    1339. -
    1340. }
    1341. -
    1342. -
    1343. });
    1344. -
    1345. 18 runMigration(0);
    1346. -
    1347. }, ret);
    1348. -
    1349. 18 return ret.promise();
    1350. -
    1351. },
    1352. -
    1353. -
    1354. getFileNames:function () {
    1355. -
    1356. 37 var ret = new Promise();
    1357. -
    1358. 37 var sup = this._super(arguments);
    1359. -
    1360. 37 sup.then(hitch(this, function (files) {
    1361. -
    1362. 37 ret.callback(files.sort(hitch(this, function (f1, f2) {
    1363. -
    1364. 178 return this.getMigrationVersionFromFile(f1) - this.getMigrationVersionFromFile(f2);
    1365. -
    1366. })));
    1367. -
    1368. }), ret);
    1369. -
    1370. 37 return ret.promise();
    1371. -
    1372. },
    1373. -
    1374. -
    1375. __getAppliedMigrations:function () {
    1376. -
    1377. 18 var ret = new Promise();
    1378. -
    1379. 18 if (!this.__appliedMigrations) {
    1380. -
    1381. 18 this._getSchemaDataset().then(hitch(this, function (ds) {
    1382. -
    1383. 18 when(
    1384. -
    1385. ds.selectOrderMap(this.column),
    1386. -
    1387. this.getFileNames()
    1388. -
    1389. ).then(hitch(this, function (res) {
    1390. -
    1391. 18 var appliedMigrations = res[0], files = res[1].map(function (f) {
    1392. -
    1393. 92 return path.basename(f).toLowerCase();
    1394. -
    1395. });
    1396. -
    1397. 18 var l = appliedMigrations.length;
    1398. -
    1399. 18 if (l) {
    1400. -
    1401. 9 for (var i = 0; i < l; i++) {
    1402. -
    1403. 39 if (files.indexOf(appliedMigrations[i]) == -1) {
    1404. -
    1405. 0 return ret.errback("Applied migrations file not found in directory " + appliedMigrations[i]);
    1406. -
    1407. }
    1408. -
    1409. }
    1410. -
    1411. 9 this.__appliedMigrations = appliedMigrations;
    1412. -
    1413. 9 ret.callback(appliedMigrations);
    1414. -
    1415. } else {
    1416. -
    1417. 9 this.__appliedMigrations = [];
    1418. -
    1419. 9 ret.callback([]);
    1420. -
    1421. }
    1422. -
    1423. }), ret);
    1424. -
    1425. }), ret);
    1426. -
    1427. } else {
    1428. -
    1429. 0 ret.callback(this.__appliedMigrations);
    1430. -
    1431. }
    1432. -
    1433. 18 return ret.promise();
    1434. +
    1435. /**
    1436. +
    1437. * Represents an SQL array access, with multiple possible arguments.
    1438. +
    1439. * @constructs
    1440. +
    1441. * @augments patio.sql.GenericExpression
    1442. +
    1443. *
    1444. +
    1445. * @param arrCol the SQL array column
    1446. +
    1447. * @param sub The array of subscripts to use (should be an array of numbers)
    1448. +
    1449. */
    1450. +
    1451. constructor:function (arrCol, sub) {
    1452. +
    1453. //The SQL array column
    1454. +
    1455. 109 this.__arrCol = arrCol;
    1456. +
    1457. //The array of subscripts to use (should be an array of numbers)
    1458. +
    1459. 109 this.__sub = sub;
    1460. },
    1461. -
    1462. __getMirationFiles:function () {
    1463. -
    1464. 18 var ret = new Promise();
    1465. -
    1466. 18 var upMigrations = [], downMigrations = [], target = this.target;
    1467. -
    1468. 18 if (!this.__migrationFiles) {
    1469. -
    1470. 18 when(
    1471. -
    1472. this.getFileNames(),
    1473. -
    1474. this.__getAppliedMigrations()
    1475. -
    1476. ).then(hitch(this, function (res) {
    1477. -
    1478. 18 var files = res[0], appliedMigrations = res[1];
    1479. -
    1480. 18 var l = files.length, inserts = [];
    1481. -
    1482. 18 if (l > 0) {
    1483. -
    1484. 18 try {
    1485. -
    1486. 18 for (var i = 0; i < l; i++) {
    1487. -
    1488. 92 var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
    1489. -
    1490. 92 if (!isUndefined(target)) {
    1491. -
    1492. 48 var version = this.getMigrationVersionFromFile(f);
    1493. -
    1494. 48 if (version > target || (version === 0 && target === version)) {
    1495. -
    1496. 35 if (appliedMigrations.indexOf(fLowerCase) != -1) {
    1497. -
    1498. 26 downMigrations.push([f, require(file), "down"]);
    1499. -
    1500. }
    1501. -
    1502. 13 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
    1503. -
    1504. 9 upMigrations.push([f, require(file), "up"]);
    1505. -
    1506. }
    1507. -
    1508. 44 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
    1509. -
    1510. 35 upMigrations.push([f, require(file), "up"]);
    1511. -
    1512. }
    1513. -
    1514. }
    1515. -
    1516. } catch (e) {
    1517. -
    1518. 0 return ret.errback(e)
    1519. -
    1520. }
    1521. -
    1522. 18 this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
    1523. -
    1524. 18 ret.callback(this.__migrationFiles);
    1525. -
    1526. } else {
    1527. -
    1528. 0 return ret.callback();
    1529. -
    1530. }
    1531. -
    1532. }), ret);
    1533. -
    1534. } else {
    1535. -
    1536. 0 ret.callback(this.__migrationFiles);
    1537. -
    1538. }
    1539. -
    1540. 18 return ret.promise();
    1541. +
    1542. /**
    1543. +
    1544. * Create a new {@link patio.sql.Subscript} appending the given subscript(s)
    1545. +
    1546. * the the current array of subscripts.
    1547. +
    1548. */
    1549. +
    1550. addSub:function (sub) {
    1551. +
    1552. 0 return new SubScript(this.__arrCol, this.__sub.concat(sub));
    1553. },
    1554. -
    1555. -
    1556. // Returns the dataset for the schema_migrations table. If no such table
    1557. -
    1558. // exists, it is automatically created.
    1559. -
    1560. _getSchemaDataset:function () {
    1561. -
    1562. 36 var ret = new Promise();
    1563. -
    1564. 36 if (!this.__schemaDataset) {
    1565. -
    1566. 18 var ds = this.db.from(this.table);
    1567. -
    1568. 18 this.__createTable().then(hitch(this, function () {
    1569. -
    1570. 18 this.__schemaDataset = ds;
    1571. -
    1572. 18 ret.callback(ds);
    1573. -
    1574. }), ret);
    1575. -
    1576. } else {
    1577. -
    1578. 18 ret.callback(this.__schemaDataset);
    1579. -
    1580. }
    1581. -
    1582. 36 return ret.promise();
    1583. +
    1584. /**
    1585. +
    1586. * Converts the {@link patio.sql.SubScript} to a string.
    1587. +
    1588. *
    1589. +
    1590. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    1591. +
    1592. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    1593. +
    1594. *
    1595. +
    1596. * @return String the SQL version of the {@link patio.sql.SubScript}.
    1597. +
    1598. */
    1599. +
    1600. toString:function (ds) {
    1601. +
    1602. 109 !Dataset && (Dataset = require("./dataset"));
    1603. +
    1604. 109 ds = ds || new Dataset();
    1605. +
    1606. 109 return ds.subscriptSql(this);
    1607. },
    1608. -
    1609. __convertSchemaInfo:function () {
    1610. -
    1611. 1 var ret = new Promise(), c = this.column;
    1612. -
    1613. 1 var ds = this.db.from(this.table);
    1614. -
    1615. 1 this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
    1616. -
    1617. 1 this.getFileNames().then(hitch(this, function (files) {
    1618. -
    1619. 1 var l = files.length, inserts = [];
    1620. -
    1621. 1 if (l > 0) {
    1622. -
    1623. 1 for (var i = 0; i < l; i++) {
    1624. -
    1625. 7 var f = path.basename(files[i]);
    1626. -
    1627. 7 if (this.getMigrationVersionFromFile(f) <= version) {
    1628. -
    1629. 5 var insert = {};
    1630. -
    1631. 5 insert[c] = f;
    1632. -
    1633. 5 inserts.push(ds.insert(insert));
    1634. -
    1635. }
    1636. -
    1637. }
    1638. -
    1639. }
    1640. -
    1641. 1 if (inserts.length) {
    1642. -
    1643. 1 when.apply(comb, inserts).then(ret);
    1644. -
    1645. } else {
    1646. -
    1647. 0 ret.callback();
    1648. -
    1649. }
    1650. -
    1651. }), ret);
    1652. -
    1653. }), ret);
    1654. -
    1655. 1 return ret.promise();
    1656. -
    1657. -
    1658. },
    1659. +
    1660. /**@ignore*/
    1661. +
    1662. getters:{
    1663. +
    1664. f:function () {
    1665. +
    1666. 110 return this.__arrCol;
    1667. +
    1668. },
    1669. -
    1670. __createTable:function () {
    1671. -
    1672. 18 var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
    1673. -
    1674. 18 var ds = this.db.from(table);
    1675. -
    1676. 18 var ret = new Promise();
    1677. -
    1678. 18 when(
    1679. -
    1680. db.tableExists(table),
    1681. -
    1682. db.tableExists(intMigrationTable)
    1683. -
    1684. ).then(hitch(this, function (res) {
    1685. -
    1686. 18 var exists = res[0], intMigratorExists = res[1];
    1687. -
    1688. 18 if (!exists) {
    1689. -
    1690. 10 db.createTable(table,
    1691. -
    1692. function () {
    1693. -
    1694. 10 this.column(c, String, {primaryKey:true});
    1695. -
    1696. }).addErrback(ret);
    1697. -
    1698. 10 if (intMigratorExists) {
    1699. -
    1700. 1 db.from(intMigrationTable).all().then(hitch(this, function (versions) {
    1701. -
    1702. 1 var version;
    1703. -
    1704. 1 if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
    1705. -
    1706. 1 this.__convertSchemaInfo().then(ret);
    1707. -
    1708. } else {
    1709. -
    1710. 0 ret.callback();
    1711. -
    1712. }
    1713. -
    1714. }));
    1715. -
    1716. } else {
    1717. -
    1718. 9 ret.callback();
    1719. -
    1720. }
    1721. -
    1722. } else {
    1723. -
    1724. 8 ds.columns.then(hitch(this, function (columns) {
    1725. -
    1726. 8 if (columns.indexOf(c) === -1) {
    1727. -
    1728. 0 ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
    1729. -
    1730. } else {
    1731. -
    1732. 8 ret.callback();
    1733. -
    1734. }
    1735. -
    1736. }), ret);
    1737. -
    1738. }
    1739. -
    1740. }), ret);
    1741. -
    1742. 18 return ret.promise();
    1743. +
    1744. sub:function () {
    1745. +
    1746. 110 return this.__sub;
    1747. +
    1748. }
    1749. }
    1750. -
    1751. },
    1752. +
    1753. }
    1754. +
    1755. }).as(sql, "SubScript");
    1756. -
    1757. static:{
    1758. -
    1759. DEFAULT_SCHEMA_COLUMN:"filename",
    1760. -
    1761. DEFAULT_SCHEMA_TABLE:"schema_migrations"
    1762. +
    1763. 1var STRING_METHODS = ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "quote",
    1764. +
    1765. "replace", "search", "slice", "split", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase",
    1766. +
    1767. "toSource", "toString", "toUpperCase", "trim", "trimLeft", "trimRight", "valueOf"];
    1768. +
    1769. +
    1770. +
    1771. 1var addStringMethod = function (op) {
    1772. +
    1773. 24 return function () {
    1774. +
    1775. 4294 return this.__str[op].apply(this.__str, arguments);
    1776. }
    1777. -
    1778. }).as(exports, "TimestampMigrator");
    1779. +
    1780. };
    1781. -
    1782. 1exports.run = function () {
    1783. -
    1784. 31 return Migrator.run.apply(Migrator, arguments);
    1785. -
    1786. };
    +
  • 1var LiteralString = define([OrderedMethods, ComplexExpressionMethods, BooleanMethods, NumericMethods, StringMethods, InequalityMethods, AliasMethods], {
  • +
  • instance:{
  • +
  • /**@lends patio.sql.LiteralString*/
  • +
  • +
  • /**
  • +
  • * Represents a string that should be placed into a SQL query literally.
  • +
  • * <b>This class has all methods that a normal javascript String has.</b>
  • +
  • * @constructs
  • +
  • * @augments patio.sql.OrderedMethods
  • +
  • * @augments patio.sql.ComplexExpressionMethods
  • +
  • * @augments patio.sql.BooleanMethods
  • +
  • * @augments patio.sql.NumericMethods
  • +
  • * @augments patio.sql.StringMethods
  • +
  • * @augments patio.sql.InequalityMethods
  • +
  • * @augments patio.sql.AliasMethods
  • +
  • *
  • +
  • * @param {String} str the literal string.
  • +
  • */
  • +
  • constructor:function (str) {
  • +
  • 3095 this.__str = str;
  • +
  • }
  • +
  • }
  • +
  • }).as(sql, "LiteralString");
  • +
  • +
  • 1STRING_METHODS.forEach(function (op) {
  • +
  • 24 LiteralString.prototype[op] = addStringMethod(op);
  • +
  • }, this);
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • -
    +
    -
    dataset/index.js
    +
    migration.js
    - Coverage92.54 - SLOC439 - LOC67 - Missed5 + Coverage91.97 + SLOC607 + LOC274 + Missed22
    -
    1. 1var comb = require("comb"),
    2. +
      1. 1var comb = require("comb"),
      2. hitch = comb.hitch,
      3. -
      4. logging = comb.logging,
      5. -
      6. Logger = logging.Logger,
      7. -
      8. errors = require("../errors"),
      9. -
      10. QueryError = errors.QueryError,
      11. -
      12. DatasetError = errors.DatasetError,
      13. Promise = comb.Promise,
      14. -
      15. PromiseList = comb.PromiseList,
      16. -
      17. isUndefined = comb.isUndefined,
      18. -
      19. isUndefinedOrNull = comb.isUndefinedOrNull,
      20. -
      21. isString = comb.isString,
      22. -
      23. isInstanceOf = comb.isInstanceOf,
      24. -
      25. isString = comb.isString,
      26. -
      27. isFunction = comb.isFunction,
      28. -
      29. isNull = comb.isNull,
      30. -
      31. merge = comb.merge,
      32. +
      33. errors = require("./errors"),
      34. +
      35. MigrationError = errors.MigrationError,
      36. +
      37. NotImplemented = errors.NotImplemented(),
      38. +
      39. format = comb.string.format,
      40. define = comb.define,
      41. -
      42. graph = require("./graph"),
      43. -
      44. actions = require("./actions"),
      45. -
      46. features = require("./features"),
      47. -
      48. query = require("./query"),
      49. -
      50. sql = require("./sql"),
      51. -
      52. SQL = require("../sql").sql,
      53. -
      54. AliasedExpression = SQL.AliasedExpression,
      55. -
      56. Identifier = SQL.Identifier,
      57. -
      58. QualifiedIdentifier = SQL.QualifiedIdentifier;
      59. +
      60. isFunction = comb.isFunction,
      61. +
      62. serial = comb.serial,
      63. +
      64. isNumber = comb.isNumber,
      65. +
      66. when = comb.when,
      67. +
      68. isUndefined = comb.isUndefined,
      69. +
      70. fs = require("fs"),
      71. +
      72. path = require("path");
      73. -
      74. 1var LOGGER = comb.logger("patio.Dataset");
      75. -
      76. 1define([actions, graph, features, query, sql], {
      77. +
      78. 1var Migrator = define(null, {
      79. instance:{
      80. -
      81. -
      82. /**@lends patio.Dataset.prototype*/
      83. -
      84. -
      85. /**
      86. -
      87. * Class that is used for querying/retirving datasets from a database.
      88. -
      89. *
      90. -
      91. * <p> Dynamically genertated methods include
      92. -
      93. * <ul>
      94. -
      95. * <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
      96. -
      97. * {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
      98. -
      99. * to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
      100. -
      101. * {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
      102. -
      103. * <ul>
      104. -
      105. * <li>Conditioned join types that accept conditions.
      106. -
      107. * <ul>
      108. -
      109. * <li>inner - INNER JOIN</li>
      110. -
      111. * <li>fullOuter - FULL OUTER</li>
      112. -
      113. * <li>rightOuter - RIGHT OUTER JOIN</li>
      114. -
      115. * <li>leftOuter - LEFT OUTER JOIN</li>
      116. -
      117. * <li>full - FULL JOIN</li>
      118. -
      119. * <li>right - RIGHT JOIN</li>
      120. -
      121. * <li>left - LEFT JOIN</li>
      122. -
      123. * </ul>
      124. -
      125. * </li>
      126. -
      127. * <li>Unconditioned join types that do not accept join conditions
      128. -
      129. * <ul>
      130. -
      131. * <li>natural - NATURAL JOIN</li>
      132. -
      133. * <li>naturalLeft - NATURAL LEFT JOIN</li>
      134. -
      135. * <li>naturalRight - NATURAL RIGHT JOIN</li>
      136. -
      137. * <li>naturalFull - NATURA FULLL JOIN</li>
      138. -
      139. * <li>cross - CROSS JOIN</li>
      140. -
      141. * </ul>
      142. -
      143. * </li>
      144. -
      145. * </ul>
      146. -
      147. * </li>
      148. -
      149. * </li>
      150. -
      151. * </ul>
      152. -
      153. *
      154. -
      155. * <p>
      156. -
      157. * <h4>Features:</h4>
      158. -
      159. * <p>
      160. -
      161. * Features that a particular {@link patio.Dataset} supports are shown in the example below.
      162. -
      163. * If you wish to implement an adapter please override these values depending on the database that
      164. -
      165. * you are developing the adapter for.
      166. -
      167. * </p>
      168. -
      169. * <pre class="code">
      170. -
      171. * var ds = DB.from("test");
      172. -
      173. *
      174. -
      175. * //The default values returned
      176. -
      177. *
      178. -
      179. * //Whether this dataset quotes identifiers.
      180. -
      181. * //Whether this dataset quotes identifiers.
      182. -
      183. * ds.quoteIdentifiers //=>true
      184. -
      185. *
      186. -
      187. * //Whether this dataset will provide accurate number of rows matched for
      188. -
      189. * //delete and update statements. Accurate in this case is the number of
      190. -
      191. * //rows matched by the dataset's filter.
      192. -
      193. * ds.providesAccurateRowsMatched; //=>true
      194. -
      195. *
      196. -
      197. * //Times Whether the dataset requires SQL standard datetimes (false by default,
      198. -
      199. * // as most allow strings with ISO 8601 format).
      200. -
      201. * ds.requiresSqlStandardDate; //=>false
      202. -
      203. *
      204. -
      205. * //Whether the dataset supports common table expressions (the WITH clause).
      206. -
      207. * ds.supportsCte; //=>true
      208. -
      209. *
      210. -
      211. * //Whether the dataset supports the DISTINCT ON clause, false by default.
      212. -
      213. * ds.supportsDistinctOn; //=>false
      214. -
      215. *
      216. -
      217. * //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      218. -
      219. * ds.supportsIntersectExcept; //=>true
      220. -
      221. *
      222. -
      223. * //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
      224. -
      225. * ds.supportsIntersectExceptAll; //=>true
      226. -
      227. *
      228. -
      229. * //Whether the dataset supports the IS TRUE syntax.
      230. -
      231. * ds.supportsIsTrue; //=>true
      232. -
      233. *
      234. -
      235. * //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      236. -
      237. * ds.supportsJoinUsing; //=>true
      238. -
      239. *
      240. -
      241. * //Whether modifying joined datasets is supported.
      242. -
      243. * ds.supportsModifyingJoin; //=>false
      244. -
      245. *
      246. -
      247. * //Whether the IN/NOT IN operators support multiple columns when an
      248. -
      249. * ds.supportsMultipleColumnIn; //=>true
      250. -
      251. *
      252. -
      253. * //Whether the dataset supports timezones in literal timestamps
      254. -
      255. * ds.supportsTimestampTimezone; //=>false
      256. -
      257. *
      258. -
      259. * //Whether the dataset supports fractional seconds in literal timestamps
      260. -
      261. * ds.supportsTimestampUsecs; //=>true
      262. -
      263. *
      264. -
      265. * //Whether the dataset supports window functions.
      266. -
      267. * ds.supportsWindowFunctions; //=>false
      268. -
      269. * </pre>
      270. -
      271. * <p>
      272. -
      273. * <p>
      274. -
      275. * <h4>Actions</h4>
      276. -
      277. * <p>
      278. -
      279. * Each dataset does not actually send any query to the database until an action method has
      280. -
      281. * been called upon it(with the exception of {@link patio.Dataset#graph} because columns
      282. -
      283. * from the other table might need retrived in order to set up the graph). Each action
      284. -
      285. * returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
      286. -
      287. * that you account for errors otherwise it can be difficult to track down issues.
      288. -
      289. * The list of action methods is:
      290. -
      291. * <ul>
      292. -
      293. * <li>{@link patio.Dataset#all}</li>
      294. -
      295. * <li>{@link patio.Dataset#one}</li>
      296. -
      297. * <li>{@link patio.Dataset#avg}</li>
      298. -
      299. * <li>{@link patio.Dataset#count}</li>
      300. -
      301. * <li>{@link patio.Dataset#columns}</li>
      302. -
      303. * <li>{@link patio.Dataset#remove}</li>
      304. -
      305. * <li>{@link patio.Dataset#forEach}</li>
      306. -
      307. * <li>{@link patio.Dataset#empty}</li>
      308. -
      309. * <li>{@link patio.Dataset#first}</li>
      310. -
      311. * <li>{@link patio.Dataset#get}</li>
      312. -
      313. * <li>{@link patio.Dataset#import}</li>
      314. -
      315. * <li>{@link patio.Dataset#insert}</li>
      316. -
      317. * <li>{@link patio.Dataset#save}</li>
      318. -
      319. * <li>{@link patio.Dataset#insertMultiple}</li>
      320. -
      321. * <li>{@link patio.Dataset#saveMultiple}</li>
      322. -
      323. * <li>{@link patio.Dataset#interval}</li>
      324. -
      325. * <li>{@link patio.Dataset#last}</li>
      326. -
      327. * <li>{@link patio.Dataset#map}</li>
      328. -
      329. * <li>{@link patio.Dataset#max}</li>
      330. -
      331. * <li>{@link patio.Dataset#min}</li>
      332. -
      333. * <li>{@link patio.Dataset#multiInsert}</li>
      334. -
      335. * <li>{@link patio.Dataset#range}</li>
      336. -
      337. * <li>{@link patio.Dataset#selectHash}</li>
      338. -
      339. * <li>{@link patio.Dataset#selectMap}</li>
      340. -
      341. * <li>{@link patio.Dataset#selectOrderMap}</li>
      342. -
      343. * <li>{@link patio.Dataset#set}</li>
      344. -
      345. * <li>{@link patio.Dataset#singleRecord}</li>
      346. -
      347. * <li>{@link patio.Dataset#singleValue}</li>
      348. -
      349. * <li>{@link patio.Dataset#sum}</li>
      350. -
      351. * <li>{@link patio.Dataset#toCsv}</li>
      352. -
      353. * <li>{@link patio.Dataset#toHash}</li>
      354. -
      355. * <li>{@link patio.Dataset#truncate}</li>
      356. -
      357. * <li>{@link patio.Dataset#update}</li>
      358. -
      359. * </ul>
      360. -
      361. *
      362. -
      363. * </p>
      364. -
      365. * </p>
      366. +
      367. /**@lends patio.migrations.Migrator.prototype*/
      368. +
      369. column:null,
      370. +
      371. db:null,
      372. +
      373. directory:null,
      374. +
      375. ds:null,
      376. +
      377. files:null,
      378. +
      379. table:null,
      380. +
      381. target:null,
      382. +
      383. +
      384. /**
      385. +
      386. * Abstract Migrator class. This class should be be instantiated directly.
      387. *
      388. * @constructs
      389. +
      390. * @param {patio.Database} db the database to migrate
      391. +
      392. * @param {String} directory directory that the migration files reside in
      393. +
      394. * @param {Object} [opts={}] optional parameters.
      395. +
      396. * @param {String} [opts.column] the column in the table that version information should be stored.
      397. +
      398. * @param {String} [opts.table] the table that version information should be stored.
      399. +
      400. * @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
      401. +
      402. * @param {String} [opts.current] the version that the database is currently at if the current version
      403. +
      404. */
      405. +
      406. constructor:function (db, directory, opts) {
      407. +
      408. 31 this.db = db;
      409. +
      410. 31 this.directory = directory;
      411. +
      412. 31 opts = opts || {};
      413. +
      414. 31 this.table = opts.table || this._static.DEFAULT_SCHEMA_TABLE;
      415. +
      416. 31 this.column = opts.column || this._static.DEFAULT_SCHEMA_COLUMN;
      417. +
      418. 31 this._opts = opts;
      419. +
      420. },
      421. +
      422. +
      423. /**
      424. +
      425. * Runs the migration and returns a promise.
      426. +
      427. */
      428. +
      429. run:function () {
      430. +
      431. 0 throw new NotImplemented("patio.migrations.Migrator#run");
      432. +
      433. },
      434. +
      435. +
      436. getFileNames:function () {
      437. +
      438. 50 if (!this.__files) {
      439. +
      440. 49 return this._static.getFileNames(this.directory).addCallback(hitch(this, function (files) {
      441. +
      442. 49 this.__files = files;
      443. +
      444. }));
      445. +
      446. } else {
      447. +
      448. 1 return new Promise().callback(this.__files).promise();
      449. +
      450. }
      451. +
      452. },
      453. +
      454. +
      455. getMigrationVersionFromFile:function (filename) {
      456. +
      457. 476 return parseInt(path.basename(filename).split(this._static.MIGRATION_SPLITTER)[0], 10);
      458. +
      459. }
      460. +
      461. },
      462. +
      463. +
      464. "static":{
      465. +
      466. /**@lends patio.migrations.Migrator*/
      467. +
      468. +
      469. MIGRATION_FILE_PATTERN:/^\d+\..+\.js$/i,
      470. +
      471. MIGRATION_SPLITTER:'.',
      472. +
      473. MINIMUM_TIMESTAMP:20000101,
      474. +
      475. +
      476. getFileNames:function (directory) {
      477. +
      478. 80 var ret = new Promise();
      479. +
      480. 80 fs.readdir(directory, hitch(this, function (err, files) {
      481. +
      482. 80 if (err) {
      483. +
      484. 0 ret.errback(err);
      485. +
      486. } else {
      487. +
      488. 80 files = files.filter(function (file) {
      489. +
      490. 394 return file.match(this.MIGRATION_FILE_PATTERN) !== null;
      491. +
      492. }, this);
      493. +
      494. 80 ret.callback(files.map(function (file) {
      495. +
      496. 394 return path.resolve(directory, file);
      497. +
      498. }));
      499. +
      500. }
      501. +
      502. }));
      503. +
      504. 80 return ret.promise();
      505. +
      506. },
      507. +
      508. +
      509. /**
      510. +
      511. * Migrates the database using migration files found in the supplied directory.
      512. +
      513. * See {@link patio#migrate}
      514. *
      515. +
      516. * @example
      517. +
      518. * var DB = patio.connect("my://connection/string");
      519. +
      520. * patio. migrate(DB, __dirname + "/timestamp_migration").then(function(){
      521. +
      522. * console.log("done migrating!");
      523. +
      524. * });
      525. *
      526. -
      527. * @param {patio.Database} db the database this dataset should use when querying for data.
      528. -
      529. * @param {Object} opts options to set on this dataset instance
      530. -
      531. *
      532. -
      533. * @property {Function} rowCb callback to be invoked for each row returned from the database.
      534. -
      535. * the return value will be used as the result of query. The rowCb can also return a promise,
      536. -
      537. * The resolved value of the promise will be used as result.
      538. -
      539. *
      540. -
      541. * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
      542. -
      543. * This value will be defaulted to whatever the identifierInputMethod
      544. -
      545. * is on the database used in initialization.
      546. -
      547. *
      548. -
      549. * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
      550. -
      551. * This value will be defaulted to whatever the identifierOutputMethod
      552. -
      553. * is on the database used in initialization.
      554. -
      555. * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
      556. -
      557. * throws a {patio.DatasetError} tf the dataset doesn't have a table.
      558. -
      559. * <pre class="code">
      560. -
      561. * DB.from("table").firstSourceAlias;
      562. -
      563. * //=> "table"
      564. -
      565. *
      566. -
      567. * DB.from("table___t").firstSourceAlias;
      568. -
      569. * //=> "t"
      570. -
      571. * </pre>
      572. +
      573. * patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).then(function(){
      574. +
      575. * console.log("done migrating down!");
      576. +
      577. * });
      578. *
      579. -
      580. * @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
      581. -
      582. * have a table, raises a {@link patio.erros.DatasetError}.
      583. -
      584. *<pre class="code">
      585. *
      586. -
      587. * DB.from("table").firstSourceTable;
      588. -
      589. * //=> "table"
      590. +
      591. * @param {patio.Database} db the database to migrate
      592. +
      593. * @param {String} directory directory that the migration files reside in
      594. +
      595. * @param {Object} [opts={}] optional parameters.
      596. +
      597. * @param {String} [opts.column] the column in the table that version information should be stored.
      598. +
      599. * @param {String} [opts.table] the table that version information should be stored.
      600. +
      601. * @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
      602. +
      603. * @param {String} [opts.current] the version that the database is currently at if the current version
      604. +
      605. * is not provided it is retrieved from the database.
      606. *
      607. -
      608. * DB.from("table___t").firstSourceTable;
      609. -
      610. * //=> "t"
      611. -
      612. * </pre>
      613. -
      614. * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
      615. -
      616. * <pre class="code">
      617. -
      618. * DB.from("items").isSimpleSelectAll; //=> true
      619. -
      620. * DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
      621. -
      622. * </pre>
      623. -
      624. * @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
      625. -
      626. * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
      627. -
      628. * delete and update statements. Accurate in this case is the number of
      629. -
      630. * rows matched by the dataset's filter.
      631. -
      632. * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
      633. -
      634. * as most allow strings with ISO 8601 format).
      635. -
      636. * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
      637. -
      638. * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
      639. -
      640. * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      641. -
      642. * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
      643. -
      644. * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
      645. -
      646. * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      647. -
      648. * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
      649. -
      650. * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
      651. -
      652. * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
      653. -
      654. * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
      655. -
      656. * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
      657. +
      658. * @return {Promise} a promise that is resolved once the migration is complete.
      659. */
      660. -
      661. constructor:function (db, opts) {
      662. -
      663. 29216 this._super(arguments);
      664. -
      665. 29216 this.db = db;
      666. -
      667. 29216 this.__opts = {};
      668. -
      669. 29216 this.__rowCb = null;
      670. -
      671. 29216 if (db) {
      672. -
      673. 15039 this.__quoteIdentifiers = db.quoteIdentifiers;
      674. -
      675. 15039 this.__identifierInputMethod = db.identifierInputMethod;
      676. -
      677. 15039 this.__identifierOutputMethod = db.identifierOutputMethod;
      678. +
      679. run:function (db, directory, opts, cb) {
      680. +
      681. 31 if (isFunction(opts)) {
      682. +
      683. 0 cb = opts;
      684. +
      685. 0 opts = {};
      686. +
      687. } else {
      688. +
      689. 31 opts = opts || {};
      690. +
      691. }
      692. +
      693. 31 opts = opts || {};
      694. +
      695. 31 var ret = new Promise();
      696. +
      697. 31 this.__getMigrator(directory).then(function (migrator) {
      698. +
      699. 31 new migrator(db, directory, opts).run().then(ret);
      700. +
      701. }, ret);
      702. +
      703. 31 ret.classic(cb);
      704. +
      705. 31 return ret.promise();
      706. +
      707. },
      708. +
      709. +
      710. // Choose the Migrator subclass to use. Uses the TimestampMigrator
      711. +
      712. // // if the version number appears to be a unix time integer for a year
      713. +
      714. // after 2005, otherwise uses the IntegerMigrator.
      715. +
      716. __getMigrator:function (directory) {
      717. +
      718. 31 var ret = new Promise();
      719. +
      720. 31 var retClass = IntegerMigrator;
      721. +
      722. 31 this.getFileNames(directory).then(hitch(this, function (files) {
      723. +
      724. 31 var l = files.length;
      725. +
      726. 31 if (l) {
      727. +
      728. 31 for (var i = 0; i < l; i++) {
      729. +
      730. 81 var file = files[i];
      731. +
      732. 81 if (parseInt(path.basename(file).split(this.MIGRATION_SPLITTER)[0], 10) > this.MINIMUM_TIMESTAMP) {
      733. +
      734. 18 retClass = TimestampMigrator;
      735. +
      736. 18 break;
      737. +
      738. }
      739. +
      740. }
      741. +
      742. }
      743. +
      744. 31 ret.callback(retClass);
      745. +
      746. +
      747. }), ret);
      748. +
      749. 31 return ret.promise();
      750. +
      751. }
      752. +
      753. }
      754. +
      755. });
      756. +
      757. +
      758. +
      759. /**
      760. +
      761. * @class Migrator that uses the file format {migrationName}.{version}.js, where version starts at 0.
      762. +
      763. * <b>Missing migrations are not allowed</b>
      764. +
      765. *
      766. +
      767. * @augments patio.migrations.Migrator
      768. +
      769. * @name IntegerMigrator
      770. +
      771. * @memberOf patio.migrations
      772. +
      773. */
      774. +
      775. 1var IntegerMigrator = define(Migrator, {
      776. +
      777. instance:{
      778. +
      779. /**@lends patio.migrations.IntegerMigrator.prototype*/
      780. +
      781. current:null,
      782. +
      783. direction:null,
      784. +
      785. migrations:null,
      786. +
      787. +
      788. _migrationFiles:null,
      789. +
      790. +
      791. run:function () {
      792. +
      793. 13 var ret = new Promise(), DB = this.db;
      794. +
      795. 13 serial([this._getLatestMigrationVersion.bind(this), this._getCurrentMigrationVersion.bind(this)]).then(hitch(this, function (res) {
      796. +
      797. 11 var target = res[0], current = res[1];
      798. +
      799. 11 if (current !== target) {
      800. +
      801. 11 var direction = this.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;
      802. +
      803. 11 this._getMigrations(current, target, direction).then(hitch(this, function (migrations) {
      804. +
      805. 11 var runMigration = hitch(this, function (index) {
      806. +
      807. 60 if (index >= migrations.length) {
      808. +
      809. 11 ret.callback(version);
      810. +
      811. } else {
      812. +
      813. 49 var curr = migrations[index], migration = curr[0];
      814. +
      815. 49 version = curr[1];
      816. +
      817. 49 var now = new Date();
      818. +
      819. 49 var lv = isUp ? version : version - 1;
      820. +
      821. 49 DB.logInfo("Begin applying migration version %d, direction: %s", lv, direction);
      822. +
      823. 49 DB.transaction(hitch(this, function () {
      824. +
      825. 49 var ret = new Promise();
      826. +
      827. 49 if (!isFunction(migration[direction])) {
      828. +
      829. 0 this._setMigrationVersion(lv).then(ret);
      830. +
      831. } else {
      832. +
      833. 49 when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
      834. +
      835. 49 this._setMigrationVersion(lv).then(ret);
      836. +
      837. }), ret);
      838. +
      839. }
      840. +
      841. 49 return ret.promise();
      842. +
      843. })).then(function () {
      844. +
      845. 49 DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);
      846. +
      847. 49 runMigration(index + 1);
      848. +
      849. }, ret);
      850. +
      851. }
      852. +
      853. +
      854. });
      855. +
      856. 11 runMigration(0);
      857. +
      858. }), ret);
      859. +
      860. } else {
      861. +
      862. 0 ret.callback(target);
      863. +
      864. }
      865. +
      866. +
      867. }), ret);
      868. +
      869. 13 return ret.promise();
      870. +
      871. },
      872. +
      873. +
      874. _getMigrations:function (current, target, direction) {
      875. +
      876. 11 var ret = new Promise(), isUp = direction === "up", migrations = [];
      877. +
      878. 11 when(this._getMigrationFiles()).then(function (files) {
      879. +
      880. 11 try {
      881. +
      882. 11 if ((isUp ? target : current - 1) < files.length) {
      883. +
      884. 11 if (isUp) {
      885. +
      886. 8 current++;
      887. +
      888. }
      889. +
      890. 11 for (; isUp ? current <= target : current > target; isUp ? current++ : current--) {
      891. +
      892. 49 migrations.push([require(files[current]), current]);
      893. +
      894. }
      895. +
      896. } else {
      897. +
      898. 0 return ret.errback(new MigrationError("Invalid target " + target));
      899. +
      900. }
      901. +
      902. } catch (e) {
      903. +
      904. 0 return ret.errback(e);
      905. +
      906. }
      907. +
      908. 11 ret.callback(migrations);
      909. +
      910. }, ret);
      911. +
      912. 11 return ret.promise();
      913. +
      914. },
      915. +
      916. +
      917. +
      918. _getMigrationFiles:function () {
      919. +
      920. 19 var ret = new Promise();
      921. +
      922. 19 if (!this._migrationFiles) {
      923. +
      924. 13 var retFiles = [];
      925. +
      926. 13 var directory = this.directory;
      927. +
      928. 13 this.getFileNames().then(hitch(this, function (files) {
      929. +
      930. 13 var l = files.length;
      931. +
      932. 13 if (l) {
      933. +
      934. 13 for (var i = 0; i < l; i++) {
      935. +
      936. 59 var file = files[i];
      937. +
      938. 59 var version = this.getMigrationVersionFromFile(file);
      939. +
      940. 59 if (isUndefined(retFiles[version])) {
      941. +
      942. 58 retFiles[version] = file;
      943. +
      944. } else {
      945. +
      946. 1 return ret.errback(new MigrationError("Duplicate migration number " + version));
      947. +
      948. }
      949. +
      950. }
      951. +
      952. 12 if (isUndefined(retFiles[0])) {
      953. +
      954. 0 retFiles.shift();
      955. +
      956. }
      957. +
      958. 12 for (var j = 0; j < l; j++) {
      959. +
      960. 57 if (isUndefined(retFiles[j])) {
      961. +
      962. 1 return ret.errback(new MigrationError("Missing migration for " + j));
      963. +
      964. }
      965. +
      966. }
      967. +
      968. }
      969. +
      970. 11 this._migrationFiles = retFiles;
      971. +
      972. 11 ret.callback(retFiles);
      973. +
      974. }), ret);
      975. +
      976. } else {
      977. +
      978. 6 ret.callback(this._migrationFiles);
      979. }
      980. +
      981. 19 return ret.promise();
      982. },
      983. +
      984. _getLatestMigrationVersion:function () {
      985. +
      986. 13 var ret = new Promise();
      987. +
      988. 13 if (!isUndefined(this._opts.target)) {
      989. +
      990. 5 ret.callback(this._opts.target);
      991. +
      992. } else {
      993. +
      994. 8 this._getMigrationFiles().then(hitch(this, function (files) {
      995. +
      996. 6 var l = files[files.length - 1];
      997. +
      998. 6 ret.callback(l ? this.getMigrationVersionFromFile(path.basename(l)) : null);
      999. +
      1000. }), ret);
      1001. +
      1002. }
      1003. +
      1004. 13 return ret.promise();
      1005. +
      1006. },
      1007. -
      1008. /**
      1009. -
      1010. * Returns a new clone of the dataset with with the given options merged into the current datasets options.
      1011. -
      1012. * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
      1013. -
      1014. * columns are deleted. This method should generally not be called
      1015. -
      1016. * directly by user code.
      1017. -
      1018. *
      1019. -
      1020. * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
      1021. -
      1022. * @return [patio.Dataset] a cloned dataset with the merged options
      1023. -
      1024. **/
      1025. -
      1026. mergeOptions:function (opts) {
      1027. -
      1028. 14948 opts = isUndefined(opts) ? {} : opts;
      1029. -
      1030. 14948 var ds = new this._static(this.db, {});
      1031. -
      1032. 14948 ds.rowCb = this.rowCb;
      1033. -
      1034. 14948 this._static.FEATURES.forEach(function (f) {
      1035. -
      1036. 209272 ds[f] = this[f];
      1037. -
      1038. }, this);
      1039. -
      1040. 14948 ds.__opts = merge({}, this.__opts, opts);
      1041. -
      1042. 14948 ds.identifierInputMethod = this.identifierInputMethod;
      1043. -
      1044. 14948 ds.identifierOutputMethod = this.identifierOutputMethod;
      1045. -
      1046. 14948 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
      1047. -
      1048. 14948 if (Object.keys(opts).some(function (o) {
      1049. -
      1050. 13536 return columnChangeOpts.indexOf(o) != -1;
      1051. -
      1052. })) {
      1053. -
      1054. 2456 ds.__opts.columns = null;
      1055. +
      1056. _getCurrentMigrationVersion:function () {
      1057. +
      1058. 11 var ret = new Promise();
      1059. +
      1060. 11 if (!isUndefined(this._opts.current)) {
      1061. +
      1062. 2 ret.callback(this._opts.current);
      1063. +
      1064. } else {
      1065. +
      1066. 9 when(this._getSchemaDataset()).then(hitch(this, function (ds) {
      1067. +
      1068. 9 ds.get(this.column).then(ret);
      1069. +
      1070. }), ret);
      1071. }
      1072. -
      1073. 14948 return ds;
      1074. +
      1075. 11 return ret.promise();
      1076. },
      1077. +
      1078. _setMigrationVersion:function (version) {
      1079. +
      1080. 49 var ret = new Promise(), c = this.column;
      1081. +
      1082. 49 this._getSchemaDataset().then(function (ds) {
      1083. +
      1084. 49 var item = {};
      1085. +
      1086. 49 item[c] = version;
      1087. +
      1088. 49 ds.update(item).both(ret);
      1089. +
      1090. }, ret);
      1091. -
      1092. /**
      1093. -
      1094. * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      1095. -
      1096. * or {@link patio.sql.AliasedExpression}, depending on the format:
      1097. -
      1098. *
      1099. -
      1100. * <ul>
      1101. -
      1102. * <li>For columns : table__column___alias.</li>
      1103. -
      1104. * <li>For tables : schema__table___alias.</li>
      1105. -
      1106. * </ul>
      1107. -
      1108. * each portion of the identifier is optional. See example below
      1109. -
      1110. *
      1111. -
      1112. * @example
      1113. -
      1114. *
      1115. -
      1116. * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
      1117. -
      1118. * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
      1119. -
      1120. * ds.stringToIdentifier("table__column___alias");
      1121. -
      1122. * //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
      1123. -
      1124. *
      1125. -
      1126. * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      1127. -
      1128. * or {@link patio.sql.AliasedExpression}.
      1129. -
      1130. *
      1131. -
      1132. * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
      1133. -
      1134. */
      1135. -
      1136. stringToIdentifier:function (name) {
      1137. -
      1138. 15093 if (isString(name)) {
      1139. -
      1140. 10138 var parts = this._splitString(name);
      1141. -
      1142. 10138 var schema = parts[0], table = parts[1], alias = parts[2];
      1143. -
      1144. 10138 return (schema && table && alias
      1145. -
      1146. ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
      1147. -
      1148. : (schema && table
      1149. -
      1150. ? new QualifiedIdentifier(schema, table)
      1151. -
      1152. : (table && alias
      1153. -
      1154. ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
      1155. +
      1156. 49 return ret.promise();
      1157. +
      1158. },
      1159. +
      1160. +
      1161. _getSchemaDataset:function () {
      1162. +
      1163. 58 var c = this.column, table = this.table;
      1164. +
      1165. 58 var ret = new Promise();
      1166. +
      1167. 58 if (!this.__schemaDataset) {
      1168. +
      1169. 11 var ds = this.db.from(table);
      1170. +
      1171. 11 this.__createOrAlterMigrationTable().then(hitch(this, function () {
      1172. +
      1173. 11 ds.isEmpty().then(hitch(this, function (empty) {
      1174. +
      1175. 11 if (empty) {
      1176. +
      1177. 0 var item = {};
      1178. +
      1179. 0 item[c] = -1;
      1180. +
      1181. 0 this.__schemaDataset = ds;
      1182. +
      1183. 0 ds.insert(item).then(hitch(ret, "callback", ds), ret);
      1184. +
      1185. } else {
      1186. +
      1187. 11 ds.count().then(hitch(this, function (count) {
      1188. +
      1189. 11 if (count > 1) {
      1190. +
      1191. 0 ret.errback(new Error("More than one row in migrator table"));
      1192. +
      1193. } else {
      1194. +
      1195. 11 this.__schemaDataset = ds;
      1196. +
      1197. 11 ret.callback(ds);
      1198. +
      1199. }
      1200. +
      1201. }), ret);
      1202. +
      1203. }
      1204. +
      1205. }), ret);
      1206. +
      1207. }), ret);
      1208. +
      1209. } else {
      1210. +
      1211. 47 ret.callback(this.__schemaDataset);
      1212. +
      1213. }
      1214. +
      1215. 58 return ret.promise();
      1216. +
      1217. },
      1218. +
      1219. +
      1220. __createOrAlterMigrationTable:function () {
      1221. +
      1222. 11 var c = this.column, table = this.table, db = this.db;
      1223. +
      1224. 11 var ds = this.db.from(table);
      1225. +
      1226. 11 var ret = new Promise();
      1227. +
      1228. 11 db.tableExists(table).then(hitch(this, function (exists) {
      1229. +
      1230. 11 if (!exists) {
      1231. +
      1232. 6 db.createTable(table,
      1233. +
      1234. function () {
      1235. +
      1236. 6 this.column(c, "integer", {"default":-1, allowNull:false});
      1237. +
      1238. }).then(ret);
      1239. +
      1240. } else {
      1241. +
      1242. 5 ds.columns.then(function (columns) {
      1243. +
      1244. 5 if (columns.indexOf(c) === -1) {
      1245. +
      1246. 1 db.addColumn(table, c, "integer", {"default":-1, allowNull:false})
      1247. +
      1248. .then(ret);
      1249. +
      1250. } else {
      1251. +
      1252. 4 ret.callback();
      1253. +
      1254. }
      1255. +
      1256. });
      1257. +
      1258. }
      1259. +
      1260. }), ret);
      1261. +
      1262. 11 return ret.promise();
      1263. +
      1264. }
      1265. +
      1266. +
      1267. },
      1268. +
      1269. +
      1270. static:{
      1271. +
      1272. DEFAULT_SCHEMA_COLUMN:"version",
      1273. +
      1274. DEFAULT_SCHEMA_TABLE:"schema_info"
      1275. +
      1276. }
      1277. +
      1278. }).as(exports, "IntegerMigrator");
      1279. +
      1280. +
      1281. +
      1282. /**
      1283. +
      1284. * @class Migrator that uses the file format {migrationName}.{timestamp}.js, where the timestamp
      1285. +
      1286. * can be anything greater than 20000101.
      1287. +
      1288. *
      1289. +
      1290. * @name TimestampMigrator
      1291. +
      1292. * @augments patio.migrations.Migrator
      1293. +
      1294. * @memberOf patio.migrations
      1295. +
      1296. */
      1297. +
      1298. 1var TimestampMigrator = define(Migrator, {
      1299. +
      1300. instance:{
      1301. +
      1302. +
      1303. constructor:function (db, directory, opts) {
      1304. +
      1305. 18 this._super(arguments);
      1306. +
      1307. 18 opts = opts || {};
      1308. +
      1309. 18 this.target = opts.target;
      1310. +
      1311. },
      1312. +
      1313. +
      1314. run:function () {
      1315. +
      1316. 18 var ret = new Promise(), DB = this.db, column = this.column;
      1317. +
      1318. 18 serial([this.__getMirationFiles.bind(this), this._getSchemaDataset.bind(this)]).then(function (res) {
      1319. +
      1320. 18 var migrations = res[0], ds = res[1];
      1321. +
      1322. 18 var runMigration = hitch(this, function (index) {
      1323. +
      1324. 86 if (index >= migrations.length) {
      1325. +
      1326. 16 ret.callback();
      1327. +
      1328. } else {
      1329. +
      1330. 70 var curr = migrations[index], file = curr[0], migration = curr[1], direction = curr[2];
      1331. +
      1332. 70 var now = new Date();
      1333. +
      1334. 70 DB.logInfo("Begin applying migration file %s, direction: %s", file, direction);
      1335. +
      1336. 70 DB.transaction(hitch(this, function () {
      1337. +
      1338. 70 var ret = new Promise();
      1339. +
      1340. 70 when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
      1341. +
      1342. 68 var fileLowerCase = file.toLowerCase();
      1343. +
      1344. 68 var query = {};
      1345. +
      1346. 68 query[column] = fileLowerCase;
      1347. +
      1348. 68 (direction === "up" ? ds.insert(query) : ds.filter(query).remove()).then(ret);
      1349. +
      1350. }), ret);
      1351. +
      1352. 68 return ret.promise();
      1353. +
      1354. })).then(function () {
      1355. +
      1356. 68 DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);
      1357. +
      1358. 68 runMigration(index + 1);
      1359. +
      1360. }, ret);
      1361. +
      1362. }
      1363. +
      1364. +
      1365. });
      1366. +
      1367. 18 runMigration(0);
      1368. +
      1369. }, ret);
      1370. +
      1371. 18 return ret.promise();
      1372. +
      1373. },
      1374. +
      1375. +
      1376. getFileNames:function () {
      1377. +
      1378. 37 var ret = new Promise();
      1379. +
      1380. 37 var sup = this._super(arguments);
      1381. +
      1382. 37 sup.then(hitch(this, function (files) {
      1383. +
      1384. 37 ret.callback(files.sort(hitch(this, function (f1, f2) {
      1385. +
      1386. 178 return this.getMigrationVersionFromFile(f1) - this.getMigrationVersionFromFile(f2);
      1387. +
      1388. })));
      1389. +
      1390. }), ret);
      1391. +
      1392. 37 return ret.promise();
      1393. +
      1394. },
      1395. +
      1396. +
      1397. __getAppliedMigrations:function () {
      1398. +
      1399. 18 var ret = new Promise();
      1400. +
      1401. 18 if (!this.__appliedMigrations) {
      1402. +
      1403. 18 this._getSchemaDataset().then(hitch(this, function (ds) {
      1404. +
      1405. 18 when(
      1406. +
      1407. ds.selectOrderMap(this.column),
      1408. +
      1409. this.getFileNames()
      1410. +
      1411. ).then(hitch(this, function (res) {
      1412. +
      1413. 18 var appliedMigrations = res[0], files = res[1].map(function (f) {
      1414. +
      1415. 92 return path.basename(f).toLowerCase();
      1416. +
      1417. });
      1418. +
      1419. 18 var l = appliedMigrations.length;
      1420. +
      1421. 18 if (l) {
      1422. +
      1423. 9 for (var i = 0; i < l; i++) {
      1424. +
      1425. 39 if (files.indexOf(appliedMigrations[i]) == -1) {
      1426. +
      1427. 0 return ret.errback("Applied migrations file not found in directory " + appliedMigrations[i]);
      1428. +
      1429. }
      1430. +
      1431. }
      1432. +
      1433. 9 this.__appliedMigrations = appliedMigrations;
      1434. +
      1435. 9 ret.callback(appliedMigrations);
      1436. +
      1437. } else {
      1438. +
      1439. 9 this.__appliedMigrations = [];
      1440. +
      1441. 9 ret.callback([]);
      1442. +
      1443. }
      1444. +
      1445. }), ret);
      1446. +
      1447. }), ret);
      1448. } else {
      1449. -
      1450. 4955 return name;
      1451. +
      1452. 0 ret.callback(this.__appliedMigrations);
      1453. }
      1454. +
      1455. 18 return ret.promise();
      1456. },
      1457. -
      1458. /**
      1459. -
      1460. * Can either be a string or null.
      1461. -
      1462. *
      1463. -
      1464. *
      1465. -
      1466. * @example
      1467. -
      1468. * //columns
      1469. -
      1470. * table__column___alias //=> table.column as alias
      1471. -
      1472. * table__column //=> table.column
      1473. -
      1474. * //tables
      1475. -
      1476. * schema__table___alias //=> schema.table as alias
      1477. -
      1478. * schema__table //=> schema.table
      1479. -
      1480. *
      1481. -
      1482. * //name and alias
      1483. -
      1484. * columnOrTable___alias //=> columnOrTable as alias
      1485. -
      1486. *
      1487. -
      1488. *
      1489. -
      1490. *
      1491. -
      1492. * @return {String[]} an array with the elements being:
      1493. -
      1494. * <ul>
      1495. -
      1496. * <li>For columns :[table, column, alias].</li>
      1497. -
      1498. * <li>For tables : [schema, table, alias].</li>
      1499. -
      1500. * </ul>
      1501. -
      1502. */
      1503. -
      1504. _splitString:function (s) {
      1505. -
      1506. 19662 var ret, m;
      1507. -
      1508. 19662 if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
      1509. -
      1510. 189 ret = m.slice(1);
      1511. -
      1512. }
      1513. -
      1514. 19473 else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
      1515. -
      1516. 28 ret = [null, m[1], m[2]];
      1517. -
      1518. }
      1519. -
      1520. 19445 else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
      1521. -
      1522. 2079 ret = [m[1], m[2], null];
      1523. -
      1524. }
      1525. -
      1526. else {
      1527. -
      1528. 17366 ret = [null, s, null];
      1529. +
      1530. __getMirationFiles:function () {
      1531. +
      1532. 18 var ret = new Promise();
      1533. +
      1534. 18 var upMigrations = [], downMigrations = [], target = this.target;
      1535. +
      1536. 18 if (!this.__migrationFiles) {
      1537. +
      1538. 18 when(
      1539. +
      1540. this.getFileNames(),
      1541. +
      1542. this.__getAppliedMigrations()
      1543. +
      1544. ).then(hitch(this, function (res) {
      1545. +
      1546. 18 var files = res[0], appliedMigrations = res[1];
      1547. +
      1548. 18 var l = files.length, inserts = [];
      1549. +
      1550. 18 if (l > 0) {
      1551. +
      1552. 18 try {
      1553. +
      1554. 18 for (var i = 0; i < l; i++) {
      1555. +
      1556. 92 var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
      1557. +
      1558. 92 if (!isUndefined(target)) {
      1559. +
      1560. 48 var version = this.getMigrationVersionFromFile(f);
      1561. +
      1562. 48 if (version > target || (version === 0 && target === version)) {
      1563. +
      1564. 35 if (appliedMigrations.indexOf(fLowerCase) != -1) {
      1565. +
      1566. 26 downMigrations.push([f, require(file), "down"]);
      1567. +
      1568. }
      1569. +
      1570. 13 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
      1571. +
      1572. 9 upMigrations.push([f, require(file), "up"]);
      1573. +
      1574. }
      1575. +
      1576. 44 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
      1577. +
      1578. 35 upMigrations.push([f, require(file), "up"]);
      1579. +
      1580. }
      1581. +
      1582. }
      1583. +
      1584. } catch (e) {
      1585. +
      1586. 0 return ret.errback(e)
      1587. +
      1588. }
      1589. +
      1590. 18 this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
      1591. +
      1592. 18 ret.callback(this.__migrationFiles);
      1593. +
      1594. } else {
      1595. +
      1596. 0 return ret.callback();
      1597. +
      1598. }
      1599. +
      1600. }), ret);
      1601. +
      1602. } else {
      1603. +
      1604. 0 ret.callback(this.__migrationFiles);
      1605. }
      1606. -
      1607. 19662 return ret;
      1608. +
      1609. 18 return ret.promise();
      1610. },
      1611. -
      1612. /**
      1613. -
      1614. * @ignore
      1615. -
      1616. **/
      1617. -
      1618. getters:{
      1619. -
      1620. -
      1621. rowCb:function () {
      1622. -
      1623. 23052 return this.__rowCb;
      1624. -
      1625. },
      1626. -
      1627. -
      1628. identifierInputMethod:function () {
      1629. -
      1630. 14948 return this.__identifierInputMethod;
      1631. -
      1632. },
      1633. -
      1634. -
      1635. identifierOutputMethod:function () {
      1636. -
      1637. 14948 return this.__identifierOutputMethod;
      1638. -
      1639. },
      1640. -
      1641. -
      1642. firstSourceAlias:function () {
      1643. -
      1644. 579 var source = this.__opts.from;
      1645. -
      1646. 579 if (isUndefinedOrNull(source) || !source.length) {
      1647. -
      1648. 2 throw new DatasetError("No source specified for the query");
      1649. -
      1650. }
      1651. -
      1652. 577 source = source[0];
      1653. -
      1654. 577 if (isInstanceOf(source, AliasedExpression)) {
      1655. -
      1656. 20 return source.alias;
      1657. -
      1658. 557 } else if (isString(source)) {
      1659. -
      1660. 0 var parts = this._splitString(source);
      1661. -
      1662. 0 var alias = parts[2];
      1663. -
      1664. 0 return alias ? alias : source;
      1665. -
      1666. } else {
      1667. -
      1668. 557 return source;
      1669. -
      1670. }
      1671. -
      1672. },
      1673. -
      1674. firstSourceTable:function () {
      1675. -
      1676. 15 var source = this.__opts.from;
      1677. -
      1678. 15 if (isUndefinedOrNull(source) || !source.length) {
      1679. -
      1680. 1 throw new QueryError("No source specified for the query");
      1681. -
      1682. }
      1683. -
      1684. 14 var source = source[0];
      1685. -
      1686. 14 if (isInstanceOf(source, AliasedExpression)) {
      1687. -
      1688. 3 return source.expression;
      1689. -
      1690. 11 } else if (isString(source)) {
      1691. -
      1692. 0 var parts = this._splitString(source);
      1693. -
      1694. 0 return source;
      1695. -
      1696. } else {
      1697. -
      1698. 11 return source;
      1699. -
      1700. }
      1701. +
      1702. // Returns the dataset for the schema_migrations table. If no such table
      1703. +
      1704. // exists, it is automatically created.
      1705. +
      1706. _getSchemaDataset:function () {
      1707. +
      1708. 36 var ret = new Promise();
      1709. +
      1710. 36 if (!this.__schemaDataset) {
      1711. +
      1712. 18 var ds = this.db.from(this.table);
      1713. +
      1714. 18 this.__createTable().then(hitch(this, function () {
      1715. +
      1716. 18 this.__schemaDataset = ds;
      1717. +
      1718. 18 ret.callback(ds);
      1719. +
      1720. }), ret);
      1721. +
      1722. } else {
      1723. +
      1724. 18 ret.callback(this.__schemaDataset);
      1725. }
      1726. +
      1727. 36 return ret.promise();
      1728. },
      1729. -
      1730. /**
      1731. -
      1732. * @ignore
      1733. -
      1734. **/
      1735. -
      1736. setters:{
      1737. -
      1738. /**@lends patio.Dataset.prototype*/
      1739. -
      1740. -
      1741. identifierInputMethod:function (meth) {
      1742. -
      1743. 15038 this.__identifierInputMethod = meth;
      1744. -
      1745. },
      1746. +
      1747. __convertSchemaInfo:function () {
      1748. +
      1749. 1 var ret = new Promise(), c = this.column;
      1750. +
      1751. 1 var ds = this.db.from(this.table);
      1752. +
      1753. 1 this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
      1754. +
      1755. 1 this.getFileNames().then(hitch(this, function (files) {
      1756. +
      1757. 1 var l = files.length, inserts = [];
      1758. +
      1759. 1 if (l > 0) {
      1760. +
      1761. 1 for (var i = 0; i < l; i++) {
      1762. +
      1763. 7 var f = path.basename(files[i]);
      1764. +
      1765. 7 if (this.getMigrationVersionFromFile(f) <= version) {
      1766. +
      1767. 5 var insert = {};
      1768. +
      1769. 5 insert[c] = f;
      1770. +
      1771. 5 inserts.push(ds.insert(insert));
      1772. +
      1773. }
      1774. +
      1775. }
      1776. +
      1777. }
      1778. +
      1779. 1 if (inserts.length) {
      1780. +
      1781. 1 when.apply(comb, inserts).then(ret);
      1782. +
      1783. } else {
      1784. +
      1785. 0 ret.callback();
      1786. +
      1787. }
      1788. +
      1789. }), ret);
      1790. +
      1791. }), ret);
      1792. +
      1793. 1 return ret.promise();
      1794. -
      1795. identifierOutputMethod:function (meth) {
      1796. -
      1797. 15038 this.__identifierOutputMethod = meth;
      1798. -
      1799. },
      1800. +
      1801. },
      1802. -
      1803. rowCb:function (cb) {
      1804. -
      1805. 18564 if (isFunction(cb) || isNull(cb)) {
      1806. -
      1807. 18559 this.__rowCb = cb;
      1808. +
      1809. __createTable:function () {
      1810. +
      1811. 18 var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
      1812. +
      1813. 18 var ds = this.db.from(table);
      1814. +
      1815. 18 var ret = new Promise();
      1816. +
      1817. 18 when(
      1818. +
      1819. db.tableExists(table),
      1820. +
      1821. db.tableExists(intMigrationTable)
      1822. +
      1823. ).then(hitch(this, function (res) {
      1824. +
      1825. 18 var exists = res[0], intMigratorExists = res[1];
      1826. +
      1827. 18 if (!exists) {
      1828. +
      1829. 10 db.createTable(table,
      1830. +
      1831. function () {
      1832. +
      1833. 10 this.column(c, String, {primaryKey:true});
      1834. +
      1835. }).addErrback(ret);
      1836. +
      1837. 10 if (intMigratorExists) {
      1838. +
      1839. 1 db.from(intMigrationTable).all().then(hitch(this, function (versions) {
      1840. +
      1841. 1 var version;
      1842. +
      1843. 1 if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
      1844. +
      1845. 1 this.__convertSchemaInfo().then(ret);
      1846. +
      1847. } else {
      1848. +
      1849. 0 ret.callback();
      1850. +
      1851. }
      1852. +
      1853. }));
      1854. +
      1855. } else {
      1856. +
      1857. 9 ret.callback();
      1858. +
      1859. }
      1860. } else {
      1861. -
      1862. 5 throw new DatasetError("rowCb mus be a function");
      1863. +
      1864. 8 ds.columns.then(hitch(this, function (columns) {
      1865. +
      1866. 8 if (columns.indexOf(c) === -1) {
      1867. +
      1868. 0 ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
      1869. +
      1870. } else {
      1871. +
      1872. 8 ret.callback();
      1873. +
      1874. }
      1875. +
      1876. }), ret);
      1877. }
      1878. -
      1879. }
      1880. +
      1881. }), ret);
      1882. +
      1883. 18 return ret.promise();
      1884. }
      1885. },
      1886. static:{
      1887. -
      1888. COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
      1889. -
      1890. COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
      1891. -
      1892. COLUMN_REF_RE3:/^(\w+)__(\w+)$/
      1893. +
      1894. DEFAULT_SCHEMA_COLUMN:"filename",
      1895. +
      1896. DEFAULT_SCHEMA_TABLE:"schema_migrations"
      1897. }
      1898. -
      1899. }).as(module);
      1900. +
      1901. }).as(exports, "TimestampMigrator");
      1902. -
      +
    3. 1exports.run = function () {
    4. +
    5. 31 return Migrator.run.apply(Migrator, arguments);
    6. +
    7. };
    @@ -13867,7 +13892,7 @@
    Coverage92.86 - SLOC1683 + SLOC1684 LOC504 Missed36
    @@ -13875,44 +13900,45 @@
    1. 1var comb = require("comb"),
    2. -
    3. define = comb.define,
    4. -
    5. array = comb.array,
    6. -
    7. intersect = array.intersect,
    8. -
    9. compact = array.compact,
    10. -
    11. string = comb.string,
    12. -
    13. format = string.format,
    14. -
    15. argsToArray = comb.argsToArray,
    16. -
    17. isInstanceOf = comb.isInstanceOf,
    18. -
    19. isArray = comb.isArray,
    20. -
    21. isNumber = comb.isNumber,
    22. -
    23. isDate = comb.isDate,
    24. -
    25. isNull = comb.isNull,
    26. -
    27. isBoolean = comb.isBoolean,
    28. -
    29. isFunction = comb.isFunction,
    30. -
    31. isUndefined = comb.isUndefined,
    32. -
    33. isObject = comb.isObject,
    34. -
    35. isHash = comb.isHash,
    36. -
    37. merge = comb.merge,
    38. -
    39. isUndefinedOrNull = comb.isUndefinedOrNull,
    40. -
    41. isString = comb.isString,
    42. -
    43. sql = require("../sql").sql,
    44. -
    45. Expression = sql.Expression,
    46. -
    47. ComplexExpression = sql.ComplexExpression,
    48. -
    49. AliasedExpression = sql.AliasedExpression,
    50. -
    51. Identifier = sql.Identifier,
    52. -
    53. QualifiedIdentifier = sql.QualifiedIdentifier,
    54. -
    55. OrderedExpression = sql.OrderedExpression,
    56. -
    57. CaseExpression = sql.CaseExpression,
    58. -
    59. SubScript = sql.SubScript,
    60. -
    61. NumericExpression = sql.NumericExpression,
    62. -
    63. ColumnAll = sql.ColumnAll,
    64. -
    65. Cast = sql.Cast,
    66. -
    67. StringExpression = sql.StringExpression,
    68. -
    69. BooleanExpression = sql.BooleanExpression,
    70. -
    71. SQLFunction = sql.SQLFunction,
    72. -
    73. LiteralString = sql.LiteralString,
    74. -
    75. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    76. -
    77. QueryError = require("../errors").QueryError, patio;
    78. +
    79. define = comb.define,
    80. +
    81. array = comb.array,
    82. +
    83. intersect = array.intersect,
    84. +
    85. compact = array.compact,
    86. +
    87. string = comb.string,
    88. +
    89. format = string.format,
    90. +
    91. argsToArray = comb.argsToArray,
    92. +
    93. isInstanceOf = comb.isInstanceOf,
    94. +
    95. isArray = comb.isArray,
    96. +
    97. isNumber = comb.isNumber,
    98. +
    99. isDate = comb.isDate,
    100. +
    101. isNull = comb.isNull,
    102. +
    103. isBoolean = comb.isBoolean,
    104. +
    105. isFunction = comb.isFunction,
    106. +
    107. isUndefined = comb.isUndefined,
    108. +
    109. isObject = comb.isObject,
    110. +
    111. isHash = comb.isHash,
    112. +
    113. isEmpty = comb.isEmpty,
    114. +
    115. merge = comb.merge,
    116. +
    117. isUndefinedOrNull = comb.isUndefinedOrNull,
    118. +
    119. isString = comb.isString,
    120. +
    121. sql = require("../sql").sql,
    122. +
    123. Expression = sql.Expression,
    124. +
    125. ComplexExpression = sql.ComplexExpression,
    126. +
    127. AliasedExpression = sql.AliasedExpression,
    128. +
    129. Identifier = sql.Identifier,
    130. +
    131. QualifiedIdentifier = sql.QualifiedIdentifier,
    132. +
    133. OrderedExpression = sql.OrderedExpression,
    134. +
    135. CaseExpression = sql.CaseExpression,
    136. +
    137. SubScript = sql.SubScript,
    138. +
    139. NumericExpression = sql.NumericExpression,
    140. +
    141. ColumnAll = sql.ColumnAll,
    142. +
    143. Cast = sql.Cast,
    144. +
    145. StringExpression = sql.StringExpression,
    146. +
    147. BooleanExpression = sql.BooleanExpression,
    148. +
    149. SQLFunction = sql.SQLFunction,
    150. +
    151. LiteralString = sql.LiteralString,
    152. +
    153. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    154. +
    155. QueryError = require("../errors").QueryError, patio;
    156. 1var Dataset;
    157. @@ -14323,7 +14349,7 @@
    158. 59 return new QualifiedIdentifier(table, e);
    159. 217 } else if (isInstanceOf(e, OrderedExpression)) {
    160. 2 return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending,
    161. -
    162. {nulls:e.nulls});
    163. +
    164. {nulls:e.nulls});
    165. 215 } else if (isInstanceOf(e, AliasedExpression)) {
    166. 72 return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias);
    167. 143 } else if (isInstanceOf(e, CaseExpression)) {
    168. @@ -14491,9 +14517,9 @@
    169. 1440 var columns = this.__opts.columns, ret = "";
    170. 1440 if (columns && columns.length) {
    171. 1387 ret = " (" + columns.map(
    172. -
    173. function (c) {
    174. -
    175. 6691 return c.toString(this);
    176. -
    177. }, this).join(this._static.COMMA_SEPARATOR) + ")";
    178. +
    179. function (c) {
    180. +
    181. 6691 return c.toString(this);
    182. +
    183. }, this).join(this._static.COMMA_SEPARATOR) + ")";
    184. }
    185. 1440 return ret;
    186. },
    187. @@ -14605,8 +14631,8 @@
    188. 0 return this.complexExpressionSql("EQ", args);
    189. } else {
    190. 0 return this.complexExpressionSql("OR",
    191. -
    192. [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
    193. -
    194. null)]);
    195. +
    196. [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
    197. +
    198. null)]);
    199. }
    200. 6454 } else if (["IN", "NOTIN"].indexOf(op) != -1) {
    201. @@ -14637,8 +14663,8 @@
    202. //literal so that if values is an array of two element arrays, it
    203. //will be treated as a value list instead of a condition specifier.
    204. 4 return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
    205. -
    206. ComplexExpression.IN_OPERATORS[op],
    207. -
    208. valArray ? this._arraySql(vals) : this.literal(vals));
    209. +
    210. ComplexExpression.IN_OPERATORS[op],
    211. +
    212. valArray ? this._arraySql(vals) : this.literal(vals));
    213. }
    214. }
    215. else {
    216. @@ -14652,13 +14678,13 @@
    217. }
    218. } else {
    219. 10 return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
    220. -
    221. ComplexExpression.IN_OPERATORS[op], this.literal(vals));
    222. +
    223. ComplexExpression.IN_OPERATORS[op], this.literal(vals));
    224. }
    225. }
    226. 6437 } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
    227. 5427 var l = args[0];
    228. 5427 return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
    229. -
    230. this.literal(args[1]));
    231. +
    232. this.literal(args[1]));
    233. 1010 } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
    234. 976 return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
    235. 34 } else if (op == "NOT") {
    236. @@ -14701,7 +14727,7 @@
    237. }
    238. 925 var tref = this.__tableRef(table);
    239. 925 return string.format(" %s %s", this._joinTypeSql(jc.joinType),
    240. -
    241. tableAlias ? this.__asSql(tref, tableAlias) : tref);
    242. +
    243. tableAlias ? this.__asSql(tref, tableAlias) : tref);
    244. },
    245. /**
    246. @@ -14774,11 +14800,11 @@
    247. */
    248. qualifiedIdentifierSql:function (qcr) {
    249. 4532 return [qcr.table, qcr.column].map(
    250. -
    251. function (x) {
    252. -
    253. 9064 return [QualifiedIdentifier, Identifier, String].some(function (c) {
    254. -
    255. 25671 return x instanceof c
    256. -
    257. }) ? this.literal(x) : this.quoteIdentifier(x)
    258. -
    259. }, this).join('.');
    260. +
    261. function (x) {
    262. +
    263. 9064 return [QualifiedIdentifier, Identifier, String].some(function (c) {
    264. +
    265. 25671 return x instanceof c
    266. +
    267. }) ? this.literal(x) : this.quoteIdentifier(x)
    268. +
    269. }, this).join('.');
    270. },
    271. /**
    272. @@ -14813,12 +14839,12 @@
    273. 33693 var i = this.__identifierInputMethod;
    274. 33693 v = v.toString(this);
    275. 33693 return !isUndefinedOrNull(i) ?
    276. -
    277. isFunction(v[i]) ?
    278. -
    279. v[i]() :
    280. -
    281. isFunction(comb[i]) ?
    282. -
    283. comb[i](v)
    284. -
    285. : v
    286. -
    287. : v;
    288. +
    289. isFunction(v[i]) ?
    290. +
    291. v[i]() :
    292. +
    293. isFunction(comb[i]) ?
    294. +
    295. comb[i](v)
    296. +
    297. : v
    298. +
    299. : v;
    300. },
    301. /**
    302. @@ -14831,12 +14857,12 @@
    303. 19885 (v == '' && (v = 'untitled'));
    304. 19885 var i = this.__identifierOutputMethod;
    305. 19885 return !isUndefinedOrNull(i) ?
    306. -
    307. isFunction(v[i]) ?
    308. -
    309. v[i]() :
    310. -
    311. isFunction(comb[i]) ?
    312. -
    313. comb[i](v)
    314. -
    315. : v
    316. -
    317. : v;
    318. +
    319. isFunction(v[i]) ?
    320. +
    321. v[i]() :
    322. +
    323. isFunction(comb[i]) ?
    324. +
    325. comb[i](v)
    326. +
    327. : v
    328. +
    329. : v;
    330. },
    331. /**
    332. @@ -14932,9 +14958,9 @@
    333. */
    334. _joinTypeSql:function (joinType) {
    335. 922 return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g,
    336. -
    337. function (m) {
    338. -
    339. 1127 return m.toUpperCase() + " ";
    340. -
    341. }
    342. +
    343. function (m) {
    344. +
    345. 1127 return m.toUpperCase() + " ";
    346. +
    347. }
    348. ).trimRight() + " JOIN";
    349. },
    350. @@ -15060,10 +15086,10 @@
    351. 6059 var table = parts[0], column = parts[1], alias = parts[2];
    352. 6059 if (!alias) {
    353. 6059 return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'"
    354. -
    355. + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
    356. +
    357. + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
    358. } else {
    359. 0 return this.literal(new AliasedExpression(column
    360. -
    361. && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
    362. +
    363. && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
    364. }
    365. },
    366. @@ -15356,9 +15382,9 @@
    367. 0 throw new QueryError("No source specified for the query");
    368. }
    369. 6657 return " " + source.map(
    370. -
    371. function (s) {
    372. -
    373. 6971 return this.__tableRef(s);
    374. -
    375. }, this).join(this._static.COMMA_SEPARATOR);
    376. +
    377. function (s) {
    378. +
    379. 6971 return this.__tableRef(s);
    380. +
    381. }, this).join(this._static.COMMA_SEPARATOR);
    382. },
    383. /**
    384. @@ -15904,7 +15930,7 @@
    385. 2734 var a = [];
    386. 2734 var ret = new Promise().classic(cb);
    387. 2734 this.forEach(hitch(this, function (r) {
    388. -
    389. 2936 a.push(r);
    390. +
    391. 2947 a.push(r);
    392. })).then(hitch(this, function () {
    393. 2731 this.postLoad(a);
    394. 2731 if (block) {
    395. @@ -17150,10 +17176,10 @@
    396. *
    397. */
    398. constructor:function () {
    399. -
    400. 3218 if (comb.isUndefinedOrNull(this.__associations)) {
    401. -
    402. 3055 this.__associations = {};
    403. +
    404. 3107 if (comb.isUndefinedOrNull(this.__associations)) {
    405. +
    406. 3000 this.__associations = {};
    407. }
    408. -
    409. 3218 this._super(arguments);
    410. +
    411. 3107 this._super(arguments);
    412. },
    413. reload:function () {
    414. @@ -18080,240 +18106,29 @@
    415. * });
    416. * }
    417. *
    418. -
    419. *
    420. -
    421. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    422. -
    423. *
    424. -
    425. * @throws {patio.ModelError} if name is not a function or string.
    426. -
    427. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    428. -
    429. */
    430. -
    431. validate:function (name) {
    432. -
    433. 41 this.__initValidation();
    434. -
    435. 41 var ret;
    436. -
    437. 41 if (isFunction(name)) {
    438. -
    439. 1 name.apply(this, [this.__getValidator.bind(this)]);
    440. -
    441. 1 ret = this;
    442. -
    443. 40 } else if (isString(name)) {
    444. -
    445. 40 ret = this.__getValidator(name);
    446. -
    447. } else {
    448. -
    449. 0 throw new ModelError("name is must be a string or function when validating");
    450. -
    451. }
    452. -
    453. 41 return ret;
    454. -
    455. }
    456. -
    457. }
    458. -
    459. -
    460. }).as(module);
    461. -
    -
    - -
    - - - - - -
    -
    database/logging.js
    -
    -
    - Coverage97.96 - SLOC193 - LOC49 - Missed1 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. define = comb.define,
    4. -
    5. Promise = comb.Promise,
    6. -
    7. isFunction = comb.isFunction,
    8. -
    9. logging = comb.logging,
    10. -
    11. Logger = logging.Logger,
    12. -
    13. hitch = comb.hitch,
    14. -
    15. format = comb.string.format,
    16. -
    17. QueryError = require("../errors").QueryError;
    18. -
    19. -
    20. -
    21. 1var LOGGER = Logger.getLogger("patio.Database");
    22. -
    23. -
    24. 1define(null, {
    25. -
    26. instance:{
    27. -
    28. /**@lends patio.Database.prototype*/
    29. -
    30. -
    31. /**
    32. -
    33. * Logs an INFO level message to the "patio.Database" logger.
    34. -
    35. */
    36. -
    37. logInfo:function () {
    38. -
    39. 8337 if (LOGGER.isInfo) {
    40. -
    41. 8337 LOGGER.info.apply(LOGGER, arguments);
    42. -
    43. }
    44. -
    45. },
    46. -
    47. -
    48. /**
    49. -
    50. * Logs a DEBUG level message to the "patio.Database" logger.
    51. -
    52. */
    53. -
    54. logDebug:function () {
    55. -
    56. 8027 if (LOGGER.isDebug) {
    57. -
    58. 8027 LOGGER.debug.apply(LOGGER, arguments);
    59. -
    60. }
    61. -
    62. },
    63. -
    64. -
    65. /**
    66. -
    67. * Logs an ERROR level message to the "patio.Database" logger.
    68. -
    69. */
    70. -
    71. logError:function (error) {
    72. -
    73. 75 if (LOGGER.isError) {
    74. -
    75. 75 LOGGER.error.apply(LOGGER, arguments);
    76. -
    77. }
    78. -
    79. },
    80. -
    81. -
    82. /**
    83. -
    84. * Logs a WARN level message to the "patio.Database" logger.
    85. -
    86. */
    87. -
    88. logWarn:function () {
    89. -
    90. 1 if (LOGGER.isWarn) {
    91. -
    92. 1 LOGGER.warn.apply(LOGGER, arguments);
    93. -
    94. }
    95. -
    96. },
    97. -
    98. -
    99. /**
    100. -
    101. * Logs a TRACE level message to the "patio.Database" logger.
    102. -
    103. */
    104. -
    105. logTrace:function () {
    106. -
    107. 1 if (LOGGER.isTrace) {
    108. -
    109. 1 LOGGER.trace.apply(LOGGER, arguments);
    110. -
    111. }
    112. -
    113. },
    114. -
    115. -
    116. /**
    117. -
    118. * Logs a FATAL level message to the "patio.Database" logger.
    119. -
    120. */
    121. -
    122. logFatal:function () {
    123. -
    124. 1 if (LOGGER.isFatal) {
    125. -
    126. 1 LOGGER.fatal.apply(LOGGER, arguments);
    127. -
    128. }
    129. -
    130. },
    131. -
    132. -
    133. /* Yield to the block, logging any errors at error level to all loggers,
    134. -
    135. * and all other queries with the duration at warn or info level.
    136. -
    137. * */
    138. -
    139. __logAndExecute:function (sql, args, cb) {
    140. -
    141. 8101 if (isFunction(args)) {
    142. -
    143. 8100 cb = args;
    144. -
    145. 8100 args = null;
    146. -
    147. }
    148. -
    149. -
    150. 8101 if (args) {
    151. -
    152. 0 sql = format("%s; %j", sql, args);
    153. -
    154. }
    155. -
    156. 8101 sql = sql.trim();
    157. -
    158. 8101 var start = new Date();
    159. -
    160. 8101 var ret = new Promise();
    161. -
    162. 8101 if (isFunction(cb)) {
    163. -
    164. 8100 this.logInfo("Executing; %s", sql);
    165. -
    166. 8100 cb().then(hitch(this, function () {
    167. -
    168. 8026 this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
    169. -
    170. 8026 ret.callback.apply(ret, arguments);
    171. -
    172. }), hitch(this, function (err) {
    173. -
    174. 74 err = new QueryError(format("%s: %s", err.message, sql));
    175. -
    176. 74 this.logError(err);
    177. -
    178. 74 ret.errback.apply(ret, [err]);
    179. -
    180. }));
    181. -
    182. } else {
    183. -
    184. 1 throw new QueryError("CB is required");
    185. -
    186. }
    187. -
    188. 8100 return ret.promise();
    189. -
    190. },
    191. -
    192. -
    193. /*Log the given SQL and then execute it on the connection, used by
    194. -
    195. *the transaction code.
    196. -
    197. * */
    198. -
    199. __logConnectionExecute:function (conn, sql) {
    200. -
    201. 2079 return this.__logAndExecute(sql, hitch(this, function () {
    202. -
    203. 2079 return conn[this.connectionExecuteMethod](sql);
    204. -
    205. }));
    206. -
    207. },
    208. -
    209. -
    210. -
    211. getters:{
    212. -
    213. /**@lends patio.Database.prototype*/
    214. -
    215. /**
    216. -
    217. * The "patio.Database" logger.
    218. -
    219. * @field
    220. -
    221. */
    222. -
    223. logger:function () {
    224. -
    225. 2 return LOGGER;
    226. -
    227. }
    228. -
    229. }
    230. -
    231. },
    232. -
    233. -
    234. "static":{
    235. -
    236. /**@lends patio.Database*/
    237. -
    238. /**
    239. -
    240. * Logs an INFO level message to the "patio.Database" logger.
    241. -
    242. */
    243. -
    244. logInfo:function () {
    245. -
    246. 1 if (LOGGER.isInfo) {
    247. -
    248. 1 LOGGER.info.apply(LOGGER, arguments);
    249. -
    250. }
    251. -
    252. },
    253. -
    254. -
    255. /**
    256. -
    257. * Logs a DEBUG level message to the "patio.Database" logger.
    258. -
    259. */
    260. -
    261. logDebug:function () {
    262. -
    263. 1 if (LOGGER.isDebug) {
    264. -
    265. 1 LOGGER.debug.apply(LOGGER, arguments);
    266. -
    267. }
    268. -
    269. },
    270. -
    271. -
    272. /**
    273. -
    274. * Logs a ERROR level message to the "patio.Database" logger.
    275. -
    276. */
    277. -
    278. logError:function () {
    279. -
    280. 1 if (LOGGER.isError) {
    281. -
    282. 1 LOGGER.error.apply(LOGGER, arguments);
    283. -
    284. }
    285. -
    286. },
    287. -
    288. -
    289. /**
    290. -
    291. * Logs a WARN level message to the "patio.Database" logger.
    292. -
    293. */
    294. -
    295. logWarn:function () {
    296. -
    297. 1 if (LOGGER.isWarn) {
    298. -
    299. 1 LOGGER.warn.apply(LOGGER, arguments);
    300. -
    301. }
    302. -
    303. },
    304. -
    305. -
    306. /**
    307. -
    308. * Logs a TRACE level message to the "patio.Database" logger.
    309. -
    310. */
    311. -
    312. logTrace:function () {
    313. -
    314. 1 if (LOGGER.isTrace) {
    315. -
    316. 1 LOGGER.trace.apply(LOGGER, arguments);
    317. -
    318. }
    319. -
    320. },
    321. -
    322. -
    323. /**
    324. -
    325. * Logs a FATAL level message to the "patio.Database" logger.
    326. +
    327. *
    328. +
    329. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    330. +
    331. *
    332. +
    333. * @throws {patio.ModelError} if name is not a function or string.
    334. +
    335. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    336. */
    337. -
    338. logFatal:function () {
    339. -
    340. 1 if (LOGGER.isFatal) {
    341. -
    342. 1 LOGGER.fatal.apply(LOGGER, arguments);
    343. -
    344. }
    345. -
    346. },
    347. -
    348. -
    349. getters:{
    350. -
    351. /**@lends patio.Database*/
    352. -
    353. /**
    354. -
    355. * The "patio.Database" logger.
    356. -
    357. * @field
    358. -
    359. */
    360. -
    361. logger:function () {
    362. -
    363. 1 return LOGGER;
    364. +
    365. validate:function (name) {
    366. +
    367. 41 this.__initValidation();
    368. +
    369. 41 var ret;
    370. +
    371. 41 if (isFunction(name)) {
    372. +
    373. 1 name.apply(this, [this.__getValidator.bind(this)]);
    374. +
    375. 1 ret = this;
    376. +
    377. 40 } else if (isString(name)) {
    378. +
    379. 40 ret = this.__getValidator(name);
    380. +
    381. } else {
    382. +
    383. 0 throw new ModelError("name is must be a string or function when validating");
    384. }
    385. +
    386. 41 return ret;
    387. }
    388. }
    389. -
    390. }).as(module);
    +
  • }).as(module);
  • +
  • @@ -18324,10 +18139,10 @@
    - Coverage98.84 - SLOC2136 - LOC431 - Missed5 + Coverage97.71 + SLOC2160 + LOC436 + Missed10
    @@ -19850,6 +19665,30 @@
  • },
  • /**
  • +
  • * Selects the columns if only if there is not already select sources.
  • +
  • *
  • +
  • * @example
  • +
  • *
  • +
  • * var ds = DB.from("items"); //SELECT * FROM items
  • +
  • *
  • +
  • * ds.select("a"); //SELECT a FROM items;
  • +
  • * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
  • +
  • * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
  • +
  • *
  • +
  • * @param cols columns to select if there is not already select sources.
  • +
  • * @return {patio.Dataset} a cloned dataset with the appropriate select sources.
  • +
  • */
  • +
  • selectIfNoSource:function (cols) {
  • +
  • 0 var ret;
  • +
  • 0 if (!this.hasSelectSource) {
  • +
  • 0 ret = this.select.apply(this, arguments);
  • +
  • } else {
  • +
  • 0 ret = this.mergeOptions();
  • +
  • }
  • +
  • 0 return ret;
  • +
  • },
  • +
  • +
  • /**
  • * Returns a copy of the dataset with the given columns added
  • * to the existing selected columns. If no columns are currently selected,
  • * it will select the columns given in addition to *.
  • @@ -20470,6 +20309,217 @@
  • as(module);
  • +
    + + + + + +
    +
    database/logging.js
    +
    +
    + Coverage97.96 + SLOC193 + LOC49 + Missed1 +
    +
    +
    1. 1var comb = require("comb"),
    2. +
    3. define = comb.define,
    4. +
    5. Promise = comb.Promise,
    6. +
    7. isFunction = comb.isFunction,
    8. +
    9. logging = comb.logging,
    10. +
    11. Logger = logging.Logger,
    12. +
    13. hitch = comb.hitch,
    14. +
    15. format = comb.string.format,
    16. +
    17. QueryError = require("../errors").QueryError;
    18. +
    19. +
    20. +
    21. 1var LOGGER = Logger.getLogger("patio.Database");
    22. +
    23. +
    24. 1define(null, {
    25. +
    26. instance:{
    27. +
    28. /**@lends patio.Database.prototype*/
    29. +
    30. +
    31. /**
    32. +
    33. * Logs an INFO level message to the "patio.Database" logger.
    34. +
    35. */
    36. +
    37. logInfo:function () {
    38. +
    39. 8337 if (LOGGER.isInfo) {
    40. +
    41. 8337 LOGGER.info.apply(LOGGER, arguments);
    42. +
    43. }
    44. +
    45. },
    46. +
    47. +
    48. /**
    49. +
    50. * Logs a DEBUG level message to the "patio.Database" logger.
    51. +
    52. */
    53. +
    54. logDebug:function () {
    55. +
    56. 8027 if (LOGGER.isDebug) {
    57. +
    58. 8027 LOGGER.debug.apply(LOGGER, arguments);
    59. +
    60. }
    61. +
    62. },
    63. +
    64. +
    65. /**
    66. +
    67. * Logs an ERROR level message to the "patio.Database" logger.
    68. +
    69. */
    70. +
    71. logError:function (error) {
    72. +
    73. 75 if (LOGGER.isError) {
    74. +
    75. 75 LOGGER.error.apply(LOGGER, arguments);
    76. +
    77. }
    78. +
    79. },
    80. +
    81. +
    82. /**
    83. +
    84. * Logs a WARN level message to the "patio.Database" logger.
    85. +
    86. */
    87. +
    88. logWarn:function () {
    89. +
    90. 1 if (LOGGER.isWarn) {
    91. +
    92. 1 LOGGER.warn.apply(LOGGER, arguments);
    93. +
    94. }
    95. +
    96. },
    97. +
    98. +
    99. /**
    100. +
    101. * Logs a TRACE level message to the "patio.Database" logger.
    102. +
    103. */
    104. +
    105. logTrace:function () {
    106. +
    107. 1 if (LOGGER.isTrace) {
    108. +
    109. 1 LOGGER.trace.apply(LOGGER, arguments);
    110. +
    111. }
    112. +
    113. },
    114. +
    115. +
    116. /**
    117. +
    118. * Logs a FATAL level message to the "patio.Database" logger.
    119. +
    120. */
    121. +
    122. logFatal:function () {
    123. +
    124. 1 if (LOGGER.isFatal) {
    125. +
    126. 1 LOGGER.fatal.apply(LOGGER, arguments);
    127. +
    128. }
    129. +
    130. },
    131. +
    132. +
    133. /* Yield to the block, logging any errors at error level to all loggers,
    134. +
    135. * and all other queries with the duration at warn or info level.
    136. +
    137. * */
    138. +
    139. __logAndExecute:function (sql, args, cb) {
    140. +
    141. 8101 if (isFunction(args)) {
    142. +
    143. 8100 cb = args;
    144. +
    145. 8100 args = null;
    146. +
    147. }
    148. +
    149. +
    150. 8101 if (args) {
    151. +
    152. 0 sql = format("%s; %j", sql, args);
    153. +
    154. }
    155. +
    156. 8101 sql = sql.trim();
    157. +
    158. 8101 var start = new Date();
    159. +
    160. 8101 var ret = new Promise();
    161. +
    162. 8101 if (isFunction(cb)) {
    163. +
    164. 8100 this.logInfo("Executing; %s", sql);
    165. +
    166. 8100 cb().then(hitch(this, function () {
    167. +
    168. 8026 this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
    169. +
    170. 8026 ret.callback.apply(ret, arguments);
    171. +
    172. }), hitch(this, function (err) {
    173. +
    174. 74 err = new QueryError(format("%s: %s", err.message, sql));
    175. +
    176. 74 this.logError(err);
    177. +
    178. 74 ret.errback.apply(ret, [err]);
    179. +
    180. }));
    181. +
    182. } else {
    183. +
    184. 1 throw new QueryError("CB is required");
    185. +
    186. }
    187. +
    188. 8100 return ret.promise();
    189. +
    190. },
    191. +
    192. +
    193. /*Log the given SQL and then execute it on the connection, used by
    194. +
    195. *the transaction code.
    196. +
    197. * */
    198. +
    199. __logConnectionExecute:function (conn, sql) {
    200. +
    201. 2079 return this.__logAndExecute(sql, hitch(this, function () {
    202. +
    203. 2079 return conn[this.connectionExecuteMethod](sql);
    204. +
    205. }));
    206. +
    207. },
    208. +
    209. +
    210. +
    211. getters:{
    212. +
    213. /**@lends patio.Database.prototype*/
    214. +
    215. /**
    216. +
    217. * The "patio.Database" logger.
    218. +
    219. * @field
    220. +
    221. */
    222. +
    223. logger:function () {
    224. +
    225. 2 return LOGGER;
    226. +
    227. }
    228. +
    229. }
    230. +
    231. },
    232. +
    233. +
    234. "static":{
    235. +
    236. /**@lends patio.Database*/
    237. +
    238. /**
    239. +
    240. * Logs an INFO level message to the "patio.Database" logger.
    241. +
    242. */
    243. +
    244. logInfo:function () {
    245. +
    246. 1 if (LOGGER.isInfo) {
    247. +
    248. 1 LOGGER.info.apply(LOGGER, arguments);
    249. +
    250. }
    251. +
    252. },
    253. +
    254. +
    255. /**
    256. +
    257. * Logs a DEBUG level message to the "patio.Database" logger.
    258. +
    259. */
    260. +
    261. logDebug:function () {
    262. +
    263. 1 if (LOGGER.isDebug) {
    264. +
    265. 1 LOGGER.debug.apply(LOGGER, arguments);
    266. +
    267. }
    268. +
    269. },
    270. +
    271. +
    272. /**
    273. +
    274. * Logs a ERROR level message to the "patio.Database" logger.
    275. +
    276. */
    277. +
    278. logError:function () {
    279. +
    280. 1 if (LOGGER.isError) {
    281. +
    282. 1 LOGGER.error.apply(LOGGER, arguments);
    283. +
    284. }
    285. +
    286. },
    287. +
    288. +
    289. /**
    290. +
    291. * Logs a WARN level message to the "patio.Database" logger.
    292. +
    293. */
    294. +
    295. logWarn:function () {
    296. +
    297. 1 if (LOGGER.isWarn) {
    298. +
    299. 1 LOGGER.warn.apply(LOGGER, arguments);
    300. +
    301. }
    302. +
    303. },
    304. +
    305. +
    306. /**
    307. +
    308. * Logs a TRACE level message to the "patio.Database" logger.
    309. +
    310. */
    311. +
    312. logTrace:function () {
    313. +
    314. 1 if (LOGGER.isTrace) {
    315. +
    316. 1 LOGGER.trace.apply(LOGGER, arguments);
    317. +
    318. }
    319. +
    320. },
    321. +
    322. +
    323. /**
    324. +
    325. * Logs a FATAL level message to the "patio.Database" logger.
    326. +
    327. */
    328. +
    329. logFatal:function () {
    330. +
    331. 1 if (LOGGER.isFatal) {
    332. +
    333. 1 LOGGER.fatal.apply(LOGGER, arguments);
    334. +
    335. }
    336. +
    337. },
    338. +
    339. +
    340. getters:{
    341. +
    342. /**@lends patio.Database*/
    343. +
    344. /**
    345. +
    346. * The "patio.Database" logger.
    347. +
    348. * @field
    349. +
    350. */
    351. +
    352. logger:function () {
    353. +
    354. 1 return LOGGER;
    355. +
    356. }
    357. +
    358. }
    359. +
    360. }
    361. +
    362. +
    363. }).as(module);
    +
    +
    diff --git a/docs-md/querying.md b/docs-md/querying.md index 9e988d34..72baa7d3 100644 --- a/docs-md/querying.md +++ b/docs-md/querying.md @@ -265,7 +265,7 @@ If there are multiple arguments in the hash, the filters are `AND`ed together: ``` //SELECT * FROM user WHERE id IN (1, 2, 3) AND name ~ 'Bo$' User.filter({id : [1,2,3], name : /Bo$/}); -`` +``` This works the same as if you used two separate filter calls: diff --git a/docs/coverage.html b/docs/coverage.html index c6344a66..21fb1ef8 100644 --- a/docs/coverage.html +++ b/docs/coverage.html @@ -256,10 +256,10 @@ @@ -943,10 +943,10 @@ @@ -1682,8 +1682,10 @@
  • * @param opts
  • */
  • configureLogging:function (opts) {
  • -
  • 0 comb.logger.configure();
  • -
  • 0 LOGGER.level = "info";
  • +
  • 0 comb.logger.configure(opts);
  • +
  • 0 if (!opts) {
  • +
  • 0 LOGGER.level = "info";
  • +
  • }
  • },
  • /**
  • @@ -3743,13 +3745,13 @@
  • * @return the typecasted value.
  • * */
  • typecastValue:function (columnType, value) {
  • -
  • 32725 if (isNull(value) || isUndefined(value)) {
  • +
  • 32726 if (isNull(value) || isUndefined(value)) {
  • 5793 return null;
  • }
  • -
  • 26932 var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
  • -
  • 26932 try {
  • -
  • 26932 if (isFunction(this[meth])) {
  • -
  • 26932 return this[meth](value);
  • +
  • 26933 var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
  • +
  • 26933 try {
  • +
  • 26933 if (isFunction(this[meth])) {
  • +
  • 26933 return this[meth](value);
  • } else {
  • 0 return value;
  • }
  • @@ -3915,7 +3917,7 @@
  • // Typecast the value to a String
  • __typecastValueString:function (value) {
  • -
  • 19607 return "" + value;
  • +
  • 19608 return "" + value;
  • }
  • },
  • @@ -5733,253 +5735,728 @@
  • -
    +
    - Coverage89.22 - SLOC21912 - LOC5351 - Missed577 + Coverage89.04 + SLOC21962 + LOC5365 + Missed588
    - Coverage72.53 - SLOC1076 - LOC91 - Missed25 + Coverage71.74 + SLOC1078 + LOC92 + Missed26
    -
    associations/_Association.js
    +
    dataset/index.js
    - Coverage87.18 - SLOC515 - LOC156 - Missed20 + Coverage86.11 + SLOC457 + LOC72 + Missed10
    -
    1. 1var comb = require("comb-proxy"),
    2. -
    3. define = comb.define,
    4. +
      1. 1var comb = require("comb"),
      2. +
      3. hitch = comb.hitch,
      4. +
      5. logging = comb.logging,
      6. +
      7. Logger = logging.Logger,
      8. +
      9. errors = require("../errors"),
      10. +
      11. QueryError = errors.QueryError,
      12. +
      13. DatasetError = errors.DatasetError,
      14. +
      15. Promise = comb.Promise,
      16. +
      17. PromiseList = comb.PromiseList,
      18. isUndefined = comb.isUndefined,
      19. isUndefinedOrNull = comb.isUndefinedOrNull,
      20. -
      21. isBoolean = comb.isBoolean,
      22. isString = comb.isString,
      23. -
      24. isHash = comb.isHash,
      25. -
      26. isFunction = comb.isFunction,
      27. isInstanceOf = comb.isInstanceOf,
      28. -
      29. Promise = comb.Promise,
      30. -
      31. PromiseList = comb.PromiseList,
      32. -
      33. hitch = comb.hitch,
      34. -
      35. array = comb.array,
      36. -
      37. isArray = comb.isArray,
      38. -
      39. Middleware = comb.plugins.Middleware,
      40. -
      41. PatioError = require("../errors").PatioError;
      42. -
      43. -
      44. 1var fetch = {
      45. -
      46. LAZY:"lazy",
      47. -
      48. EAGER:"eager"
      49. -
      50. };
      51. +
      52. isString = comb.isString,
      53. +
      54. isFunction = comb.isFunction,
      55. +
      56. isNull = comb.isNull,
      57. +
      58. merge = comb.merge,
      59. +
      60. define = comb.define,
      61. +
      62. graph = require("./graph"),
      63. +
      64. actions = require("./actions"),
      65. +
      66. features = require("./features"),
      67. +
      68. query = require("./query"),
      69. +
      70. sql = require("./sql"),
      71. +
      72. SQL = require("../sql").sql,
      73. +
      74. AliasedExpression = SQL.AliasedExpression,
      75. +
      76. Identifier = SQL.Identifier,
      77. +
      78. QualifiedIdentifier = SQL.QualifiedIdentifier;
      79. -
      80. /**
      81. -
      82. * @class
      83. -
      84. * Base class for all associations.
      85. -
      86. *
      87. -
      88. * </br>
      89. -
      90. * <b>NOT to be instantiated directly</b>
      91. -
      92. * Its just documented for reference.
      93. -
      94. *
      95. -
      96. * @constructs
      97. -
      98. * @param {Object} options
      99. -
      100. * @param {String} options.model a string to look up the model that we are associated with
      101. -
      102. * @param {Function} options.filter a callback to find association if a filter is defined then
      103. -
      104. * the association is read only
      105. -
      106. * @param {Object} options.key object with left key and right key
      107. -
      108. * @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
      109. -
      110. * @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
      111. -
      112. * the associations are automatically filled, if fetch.Lazy is supplied
      113. -
      114. * then a promise is returned and is called back with the loaded models
      115. -
      116. * @property {Model} model the model associatied with this association.
      117. -
      118. * @name Association
      119. -
      120. * @memberOf patio.associations
      121. -
      122. * */
      123. -
      124. 1define(Middleware, {
      125. +
      126. 1var LOGGER = comb.logger("patio.Dataset");
      127. +
      128. 1define([actions, graph, features, query, sql], {
      129. instance:{
      130. -
      131. /**@lends patio.associations.Association.prototype*/
      132. -
      133. -
      134. type:"",
      135. -
      136. -
      137. //Our associated model
      138. -
      139. _model:null,
      140. -
      141. -
      142. /**
      143. -
      144. * Fetch type
      145. -
      146. */
      147. -
      148. fetchType:fetch.LAZY,
      149. -
      150. -
      151. /**how to order our association*/
      152. -
      153. orderBy:null,
      154. -
      155. -
      156. /**Our filter method*/
      157. -
      158. filter:null,
      159. -
      160. -
      161. __hooks:null,
      162. -
      163. -
      164. isOwner:true,
      165. -
      166. -
      167. createSetter:true,
      168. -
      169. -
      170. isCascading:false,
      171. -
      172. -
      173. supportsStringKey:true,
      174. -
      175. -
      176. supportsHashKey:true,
      177. -
      178. -
      179. supportsCompositeKey:true,
      180. -
      181. supportsLeftAndRightKey:true,
      182. +
      183. /**@lends patio.Dataset.prototype*/
      184. /**
      185. +
      186. * Class that is used for querying/retirving datasets from a database.
      187. *
      188. -
      189. *Method to call to look up association,
      190. -
      191. *called after the model has been filtered
      192. -
      193. **/
      194. -
      195. _fetchMethod:"all",
      196. -
      197. -
      198. -
      199. constructor:function (options, patio, filter) {
      200. -
      201. 55 options = options || {};
      202. -
      203. 55 if (!options.model) {
      204. -
      205. 0 throw new Error("Model is required for " + this.type + " association");
      206. -
      207. }
      208. -
      209. 55 this._model = options.model;
      210. -
      211. 55 this.patio = patio;
      212. -
      213. 55 this.__opts = options;
      214. -
      215. 55 !isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
      216. -
      217. 55 this.filter = filter;
      218. -
      219. 55 this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
      220. -
      221. 55 this.__hooks =
      222. -
      223. {before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
      224. -
      225. 55 var hooks = ["Add", "Remove", "Set", "Load"];
      226. -
      227. 55 ["before", "after"].forEach(function (h) {
      228. -
      229. 110 hooks.forEach(function (a) {
      230. -
      231. 440 var hookName = h + a, hook;
      232. -
      233. 440 if (isFunction((hook = options[hookName]))) {
      234. -
      235. 0 this.__hooks[h][a.toLowerCase()] = hook;
      236. -
      237. }
      238. -
      239. }, this);
      240. -
      241. }, this);
      242. -
      243. 55 this.fetchType = options.fetchType || fetch.LAZY;
      244. -
      245. },
      246. -
      247. -
      248. _callHook:function (hook, action, args) {
      249. -
      250. 0 var func = this.__hooks[hook][action], ret;
      251. -
      252. 0 if (isFunction(func)) {
      253. -
      254. 0 ret = func.apply(this, args);
      255. -
      256. }
      257. -
      258. 0 return ret;
      259. -
      260. },
      261. -
      262. -
      263. _clearAssociations:function (model) {
      264. -
      265. 125 if (!this.readOnly) {
      266. -
      267. 125 delete model.__associations[this.name];
      268. -
      269. }
      270. -
      271. },
      272. -
      273. -
      274. _forceReloadAssociations:function (model) {
      275. -
      276. 243 if (!this.readOnly) {
      277. -
      278. 243 delete model.__associations[this.name];
      279. -
      280. 243 return model[this.name];
      281. -
      282. }
      283. -
      284. },
      285. -
      286. -
      287. /**
      288. -
      289. * @return {Boolean} true if the association is eager.
      290. -
      291. */
      292. -
      293. isEager:function () {
      294. -
      295. 2114 return this.fetchType == fetch.EAGER;
      296. -
      297. },
      298. -
      299. -
      300. _checkAssociationKey:function (parent) {
      301. -
      302. 785 var q = {};
      303. -
      304. 785 this._setAssociationKeys(parent, q);
      305. -
      306. 785 return Object.keys(q).every(function (k) {
      307. -
      308. 785 return q[k] != null
      309. -
      310. });
      311. -
      312. },
      313. -
      314. -
      315. _getAssociationKey:function () {
      316. -
      317. 5205 var options = this.__opts, key, ret = [], lk, rk;
      318. -
      319. 5205 if (!isUndefinedOrNull((key = options.key))) {
      320. -
      321. 722 if (this.supportsStringKey && isString(key)) {
      322. -
      323. //normalize the key first!
      324. -
      325. 356 ret = [
      326. -
      327. [this.isOwner ? this.defaultLeftKey : key],
      328. -
      329. [this.isOwner ? key : this.defaultRightKey]
      330. -
      331. ];
      332. -
      333. 366 } else if (this.supportsHashKey && isHash(key)) {
      334. -
      335. 366 var leftKey = Object.keys(key)[0];
      336. -
      337. 366 var rightKey = key[leftKey];
      338. -
      339. 366 ret = [
      340. -
      341. [leftKey],
      342. -
      343. [rightKey]
      344. -
      345. ];
      346. -
      347. 0 } else if (this.supportsCompositeKey && isArray(key)) {
      348. -
      349. 0 ret = [
      350. -
      351. [key],
      352. -
      353. null
      354. -
      355. ];
      356. -
      357. }
      358. -
      359. 4483 } else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
      360. -
      361. && !isUndefinedOrNull((rk = options.rightKey)))) {
      362. -
      363. 140 ret = [
      364. -
      365. array.toArray(lk), array.toArray(rk)
      366. -
      367. ];
      368. -
      369. } else {
      370. -
      371. //todo handle composite primary keys
      372. -
      373. 4343 ret = [
      374. -
      375. [this.defaultLeftKey],
      376. -
      377. [this.defaultRightKey]
      378. -
      379. ];
      380. -
      381. }
      382. -
      383. 5205 return ret;
      384. -
      385. },
      386. -
      387. -
      388. -
      389. _setAssociationKeys:function (parent, model, val) {
      390. -
      391. 1573 var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
      392. -
      393. 1573 if (leftKey && rightKey) {
      394. -
      395. 1573 for (var i = 0; i < leftKey.length; i++) {
      396. -
      397. 1573 model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
      398. -
      399. }
      400. -
      401. } else {
      402. -
      403. 0 leftKey.forEach(function (k) {
      404. -
      405. 0 model[k] = !isUndefined(val) ? val : parent[k];
      406. -
      407. });
      408. -
      409. }
      410. -
      411. },
      412. -
      413. -
      414. _setDatasetOptions:function (ds) {
      415. -
      416. 974 var options = this.__opts || {};
      417. -
      418. 974 var order, limit, distinct, select, query;
      419. -
      420. //allow for multi key ordering
      421. -
      422. 974 if (!isUndefined((select = this.select))) {
      423. -
      424. 0 ds = ds.select.apply(ds, array.toArray(select));
      425. -
      426. }
      427. -
      428. 974 if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
      429. -
      430. 0 ds = ds.filter(query);
      431. -
      432. }
      433. -
      434. 974 if (isFunction(this.filter)) {
      435. -
      436. 334 var ret = this.filter.apply(this, [ds]);
      437. -
      438. 334 if (isInstanceOf(ret, ds._static)) {
      439. -
      440. 334 ds = ret;
      441. -
      442. }
      443. -
      444. }
      445. -
      446. 974 if (!isUndefined((distinct = options.distinct))) {
      447. -
      448. 0 ds = ds.limit.apply(ds, array.toArray(distinct));
      449. -
      450. }
      451. -
      452. 974 if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
      453. -
      454. 0 ds = ds.order.apply(ds, array.toArray(order));
      455. -
      456. }
      457. -
      458. 974 if (!isUndefined((limit = options.limit))) {
      459. -
      460. 0 ds = ds.limit.apply(ds, array.toArray(limit));
      461. -
      462. }
      463. -
      464. 974 return ds;
      465. -
      466. -
      467. },
      468. -
      469. -
      470. /**
      471. +
      472. * <p> Dynamically genertated methods include
      473. +
      474. * <ul>
      475. +
      476. * <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
      477. +
      478. * {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
      479. +
      480. * to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
      481. +
      482. * {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
      483. +
      484. * <ul>
      485. +
      486. * <li>Conditioned join types that accept conditions.
      487. +
      488. * <ul>
      489. +
      490. * <li>inner - INNER JOIN</li>
      491. +
      492. * <li>fullOuter - FULL OUTER</li>
      493. +
      494. * <li>rightOuter - RIGHT OUTER JOIN</li>
      495. +
      496. * <li>leftOuter - LEFT OUTER JOIN</li>
      497. +
      498. * <li>full - FULL JOIN</li>
      499. +
      500. * <li>right - RIGHT JOIN</li>
      501. +
      502. * <li>left - LEFT JOIN</li>
      503. +
      504. * </ul>
      505. +
      506. * </li>
      507. +
      508. * <li>Unconditioned join types that do not accept join conditions
      509. +
      510. * <ul>
      511. +
      512. * <li>natural - NATURAL JOIN</li>
      513. +
      514. * <li>naturalLeft - NATURAL LEFT JOIN</li>
      515. +
      516. * <li>naturalRight - NATURAL RIGHT JOIN</li>
      517. +
      518. * <li>naturalFull - NATURA FULLL JOIN</li>
      519. +
      520. * <li>cross - CROSS JOIN</li>
      521. +
      522. * </ul>
      523. +
      524. * </li>
      525. +
      526. * </ul>
      527. +
      528. * </li>
      529. +
      530. * </li>
      531. +
      532. * </ul>
      533. +
      534. *
      535. +
      536. * <p>
      537. +
      538. * <h4>Features:</h4>
      539. +
      540. * <p>
      541. +
      542. * Features that a particular {@link patio.Dataset} supports are shown in the example below.
      543. +
      544. * If you wish to implement an adapter please override these values depending on the database that
      545. +
      546. * you are developing the adapter for.
      547. +
      548. * </p>
      549. +
      550. * <pre class="code">
      551. +
      552. * var ds = DB.from("test");
      553. +
      554. *
      555. +
      556. * //The default values returned
      557. +
      558. *
      559. +
      560. * //Whether this dataset quotes identifiers.
      561. +
      562. * //Whether this dataset quotes identifiers.
      563. +
      564. * ds.quoteIdentifiers //=>true
      565. +
      566. *
      567. +
      568. * //Whether this dataset will provide accurate number of rows matched for
      569. +
      570. * //delete and update statements. Accurate in this case is the number of
      571. +
      572. * //rows matched by the dataset's filter.
      573. +
      574. * ds.providesAccurateRowsMatched; //=>true
      575. +
      576. *
      577. +
      578. * //Times Whether the dataset requires SQL standard datetimes (false by default,
      579. +
      580. * // as most allow strings with ISO 8601 format).
      581. +
      582. * ds.requiresSqlStandardDate; //=>false
      583. +
      584. *
      585. +
      586. * //Whether the dataset supports common table expressions (the WITH clause).
      587. +
      588. * ds.supportsCte; //=>true
      589. +
      590. *
      591. +
      592. * //Whether the dataset supports the DISTINCT ON clause, false by default.
      593. +
      594. * ds.supportsDistinctOn; //=>false
      595. +
      596. *
      597. +
      598. * //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      599. +
      600. * ds.supportsIntersectExcept; //=>true
      601. +
      602. *
      603. +
      604. * //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
      605. +
      606. * ds.supportsIntersectExceptAll; //=>true
      607. +
      608. *
      609. +
      610. * //Whether the dataset supports the IS TRUE syntax.
      611. +
      612. * ds.supportsIsTrue; //=>true
      613. +
      614. *
      615. +
      616. * //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      617. +
      618. * ds.supportsJoinUsing; //=>true
      619. +
      620. *
      621. +
      622. * //Whether modifying joined datasets is supported.
      623. +
      624. * ds.supportsModifyingJoin; //=>false
      625. +
      626. *
      627. +
      628. * //Whether the IN/NOT IN operators support multiple columns when an
      629. +
      630. * ds.supportsMultipleColumnIn; //=>true
      631. +
      632. *
      633. +
      634. * //Whether the dataset supports timezones in literal timestamps
      635. +
      636. * ds.supportsTimestampTimezone; //=>false
      637. +
      638. *
      639. +
      640. * //Whether the dataset supports fractional seconds in literal timestamps
      641. +
      642. * ds.supportsTimestampUsecs; //=>true
      643. +
      644. *
      645. +
      646. * //Whether the dataset supports window functions.
      647. +
      648. * ds.supportsWindowFunctions; //=>false
      649. +
      650. * </pre>
      651. +
      652. * <p>
      653. +
      654. * <p>
      655. +
      656. * <h4>Actions</h4>
      657. +
      658. * <p>
      659. +
      660. * Each dataset does not actually send any query to the database until an action method has
      661. +
      662. * been called upon it(with the exception of {@link patio.Dataset#graph} because columns
      663. +
      664. * from the other table might need retrived in order to set up the graph). Each action
      665. +
      666. * returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
      667. +
      668. * that you account for errors otherwise it can be difficult to track down issues.
      669. +
      670. * The list of action methods is:
      671. +
      672. * <ul>
      673. +
      674. * <li>{@link patio.Dataset#all}</li>
      675. +
      676. * <li>{@link patio.Dataset#one}</li>
      677. +
      678. * <li>{@link patio.Dataset#avg}</li>
      679. +
      680. * <li>{@link patio.Dataset#count}</li>
      681. +
      682. * <li>{@link patio.Dataset#columns}</li>
      683. +
      684. * <li>{@link patio.Dataset#remove}</li>
      685. +
      686. * <li>{@link patio.Dataset#forEach}</li>
      687. +
      688. * <li>{@link patio.Dataset#empty}</li>
      689. +
      690. * <li>{@link patio.Dataset#first}</li>
      691. +
      692. * <li>{@link patio.Dataset#get}</li>
      693. +
      694. * <li>{@link patio.Dataset#import}</li>
      695. +
      696. * <li>{@link patio.Dataset#insert}</li>
      697. +
      698. * <li>{@link patio.Dataset#save}</li>
      699. +
      700. * <li>{@link patio.Dataset#insertMultiple}</li>
      701. +
      702. * <li>{@link patio.Dataset#saveMultiple}</li>
      703. +
      704. * <li>{@link patio.Dataset#interval}</li>
      705. +
      706. * <li>{@link patio.Dataset#last}</li>
      707. +
      708. * <li>{@link patio.Dataset#map}</li>
      709. +
      710. * <li>{@link patio.Dataset#max}</li>
      711. +
      712. * <li>{@link patio.Dataset#min}</li>
      713. +
      714. * <li>{@link patio.Dataset#multiInsert}</li>
      715. +
      716. * <li>{@link patio.Dataset#range}</li>
      717. +
      718. * <li>{@link patio.Dataset#selectHash}</li>
      719. +
      720. * <li>{@link patio.Dataset#selectMap}</li>
      721. +
      722. * <li>{@link patio.Dataset#selectOrderMap}</li>
      723. +
      724. * <li>{@link patio.Dataset#set}</li>
      725. +
      726. * <li>{@link patio.Dataset#singleRecord}</li>
      727. +
      728. * <li>{@link patio.Dataset#singleValue}</li>
      729. +
      730. * <li>{@link patio.Dataset#sum}</li>
      731. +
      732. * <li>{@link patio.Dataset#toCsv}</li>
      733. +
      734. * <li>{@link patio.Dataset#toHash}</li>
      735. +
      736. * <li>{@link patio.Dataset#truncate}</li>
      737. +
      738. * <li>{@link patio.Dataset#update}</li>
      739. +
      740. * </ul>
      741. +
      742. *
      743. +
      744. * </p>
      745. +
      746. * </p>
      747. +
      748. *
      749. +
      750. * @constructs
      751. +
      752. *
      753. +
      754. *
      755. +
      756. * @param {patio.Database} db the database this dataset should use when querying for data.
      757. +
      758. * @param {Object} opts options to set on this dataset instance
      759. +
      760. *
      761. +
      762. * @property {Function} rowCb callback to be invoked for each row returned from the database.
      763. +
      764. * the return value will be used as the result of query. The rowCb can also return a promise,
      765. +
      766. * The resolved value of the promise will be used as result.
      767. +
      768. *
      769. +
      770. * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
      771. +
      772. * This value will be defaulted to whatever the identifierInputMethod
      773. +
      774. * is on the database used in initialization.
      775. +
      776. *
      777. +
      778. * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
      779. +
      780. * This value will be defaulted to whatever the identifierOutputMethod
      781. +
      782. * is on the database used in initialization.
      783. +
      784. * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
      785. +
      786. * throws a {patio.DatasetError} tf the dataset doesn't have a table.
      787. +
      788. * <pre class="code">
      789. +
      790. * DB.from("table").firstSourceAlias;
      791. +
      792. * //=> "table"
      793. +
      794. *
      795. +
      796. * DB.from("table___t").firstSourceAlias;
      797. +
      798. * //=> "t"
      799. +
      800. * </pre>
      801. +
      802. *
      803. +
      804. * @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
      805. +
      806. * have a table, raises a {@link patio.erros.DatasetError}.
      807. +
      808. *<pre class="code">
      809. +
      810. *
      811. +
      812. * DB.from("table").firstSourceTable;
      813. +
      814. * //=> "table"
      815. +
      816. *
      817. +
      818. * DB.from("table___t").firstSourceTable;
      819. +
      820. * //=> "t"
      821. +
      822. * </pre>
      823. +
      824. * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
      825. +
      826. * <pre class="code">
      827. +
      828. * DB.from("items").isSimpleSelectAll; //=> true
      829. +
      830. * DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
      831. +
      832. * </pre>
      833. +
      834. * @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
      835. +
      836. * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
      837. +
      838. * delete and update statements. Accurate in this case is the number of
      839. +
      840. * rows matched by the dataset's filter.
      841. +
      842. * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
      843. +
      844. * as most allow strings with ISO 8601 format).
      845. +
      846. * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
      847. +
      848. * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
      849. +
      850. * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      851. +
      852. * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
      853. +
      854. * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
      855. +
      856. * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      857. +
      858. * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
      859. +
      860. * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
      861. +
      862. * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
      863. +
      864. * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
      865. +
      866. * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
      867. +
      868. * @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset.
      869. +
      870. * @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources
      871. +
      872. * @property {Boolean} hasSelectSource true if this dataset already has a select sources.
      873. +
      874. */
      875. +
      876. constructor:function (db, opts) {
      877. +
      878. 29216 this._super(arguments);
      879. +
      880. 29216 this.db = db;
      881. +
      882. 29216 this.__opts = {};
      883. +
      884. 29216 this.__rowCb = null;
      885. +
      886. 29216 if (db) {
      887. +
      888. 15039 this.__quoteIdentifiers = db.quoteIdentifiers;
      889. +
      890. 15039 this.__identifierInputMethod = db.identifierInputMethod;
      891. +
      892. 15039 this.__identifierOutputMethod = db.identifierOutputMethod;
      893. +
      894. }
      895. +
      896. },
      897. +
      898. +
      899. +
      900. /**
      901. +
      902. * Returns a new clone of the dataset with with the given options merged into the current datasets options.
      903. +
      904. * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
      905. +
      906. * columns are deleted. This method should generally not be called
      907. +
      908. * directly by user code.
      909. +
      910. *
      911. +
      912. * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
      913. +
      914. * @return [patio.Dataset] a cloned dataset with the merged options
      915. +
      916. **/
      917. +
      918. mergeOptions:function (opts) {
      919. +
      920. 14948 opts = isUndefined(opts) ? {} : opts;
      921. +
      922. 14948 var ds = new this._static(this.db, {});
      923. +
      924. 14948 ds.rowCb = this.rowCb;
      925. +
      926. 14948 this._static.FEATURES.forEach(function (f) {
      927. +
      928. 209272 ds[f] = this[f];
      929. +
      930. }, this);
      931. +
      932. 14948 ds.__opts = merge({}, this.__opts, opts);
      933. +
      934. 14948 ds.identifierInputMethod = this.identifierInputMethod;
      935. +
      936. 14948 ds.identifierOutputMethod = this.identifierOutputMethod;
      937. +
      938. 14948 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
      939. +
      940. 14948 if (Object.keys(opts).some(function (o) {
      941. +
      942. 13536 return columnChangeOpts.indexOf(o) != -1;
      943. +
      944. })) {
      945. +
      946. 2456 ds.__opts.columns = null;
      947. +
      948. }
      949. +
      950. 14948 return ds;
      951. +
      952. },
      953. +
      954. +
      955. +
      956. /**
      957. +
      958. * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      959. +
      960. * or {@link patio.sql.AliasedExpression}, depending on the format:
      961. +
      962. *
      963. +
      964. * <ul>
      965. +
      966. * <li>For columns : table__column___alias.</li>
      967. +
      968. * <li>For tables : schema__table___alias.</li>
      969. +
      970. * </ul>
      971. +
      972. * each portion of the identifier is optional. See example below
      973. +
      974. *
      975. +
      976. * @example
      977. +
      978. *
      979. +
      980. * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
      981. +
      982. * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
      983. +
      984. * ds.stringToIdentifier("table__column___alias");
      985. +
      986. * //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
      987. +
      988. *
      989. +
      990. * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      991. +
      992. * or {@link patio.sql.AliasedExpression}.
      993. +
      994. *
      995. +
      996. * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
      997. +
      998. */
      999. +
      1000. stringToIdentifier:function (name) {
      1001. +
      1002. 15093 if (isString(name)) {
      1003. +
      1004. 10138 var parts = this._splitString(name);
      1005. +
      1006. 10138 var schema = parts[0], table = parts[1], alias = parts[2];
      1007. +
      1008. 10138 return (schema && table && alias
      1009. +
      1010. ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
      1011. +
      1012. : (schema && table
      1013. +
      1014. ? new QualifiedIdentifier(schema, table)
      1015. +
      1016. : (table && alias
      1017. +
      1018. ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
      1019. +
      1020. } else {
      1021. +
      1022. 4955 return name;
      1023. +
      1024. }
      1025. +
      1026. },
      1027. +
      1028. +
      1029. /**
      1030. +
      1031. * Can either be a string or null.
      1032. +
      1033. *
      1034. +
      1035. *
      1036. +
      1037. * @example
      1038. +
      1039. * //columns
      1040. +
      1041. * table__column___alias //=> table.column as alias
      1042. +
      1043. * table__column //=> table.column
      1044. +
      1045. * //tables
      1046. +
      1047. * schema__table___alias //=> schema.table as alias
      1048. +
      1049. * schema__table //=> schema.table
      1050. +
      1051. *
      1052. +
      1053. * //name and alias
      1054. +
      1055. * columnOrTable___alias //=> columnOrTable as alias
      1056. +
      1057. *
      1058. +
      1059. *
      1060. +
      1061. *
      1062. +
      1063. * @return {String[]} an array with the elements being:
      1064. +
      1065. * <ul>
      1066. +
      1067. * <li>For columns :[table, column, alias].</li>
      1068. +
      1069. * <li>For tables : [schema, table, alias].</li>
      1070. +
      1071. * </ul>
      1072. +
      1073. */
      1074. +
      1075. _splitString:function (s) {
      1076. +
      1077. 19662 var ret, m;
      1078. +
      1079. 19662 if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
      1080. +
      1081. 189 ret = m.slice(1);
      1082. +
      1083. }
      1084. +
      1085. 19473 else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
      1086. +
      1087. 28 ret = [null, m[1], m[2]];
      1088. +
      1089. }
      1090. +
      1091. 19445 else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
      1092. +
      1093. 2079 ret = [m[1], m[2], null];
      1094. +
      1095. }
      1096. +
      1097. else {
      1098. +
      1099. 17366 ret = [null, s, null];
      1100. +
      1101. }
      1102. +
      1103. 19662 return ret;
      1104. +
      1105. },
      1106. +
      1107. +
      1108. /**
      1109. +
      1110. * @ignore
      1111. +
      1112. **/
      1113. +
      1114. getters:{
      1115. +
      1116. +
      1117. rowCb:function () {
      1118. +
      1119. 23052 return this.__rowCb;
      1120. +
      1121. },
      1122. +
      1123. +
      1124. identifierInputMethod:function () {
      1125. +
      1126. 14948 return this.__identifierInputMethod;
      1127. +
      1128. },
      1129. +
      1130. +
      1131. identifierOutputMethod:function () {
      1132. +
      1133. 14948 return this.__identifierOutputMethod;
      1134. +
      1135. },
      1136. +
      1137. +
      1138. firstSourceAlias:function () {
      1139. +
      1140. 579 var source = this.__opts.from;
      1141. +
      1142. 579 if (isUndefinedOrNull(source) || !source.length) {
      1143. +
      1144. 2 throw new DatasetError("No source specified for the query");
      1145. +
      1146. }
      1147. +
      1148. 577 source = source[0];
      1149. +
      1150. 577 if (isInstanceOf(source, AliasedExpression)) {
      1151. +
      1152. 20 return source.alias;
      1153. +
      1154. 557 } else if (isString(source)) {
      1155. +
      1156. 0 var parts = this._splitString(source);
      1157. +
      1158. 0 var alias = parts[2];
      1159. +
      1160. 0 return alias ? alias : source;
      1161. +
      1162. } else {
      1163. +
      1164. 557 return source;
      1165. +
      1166. }
      1167. +
      1168. },
      1169. +
      1170. +
      1171. firstSourceTable:function () {
      1172. +
      1173. 15 var source = this.__opts.from;
      1174. +
      1175. 15 if (isUndefinedOrNull(source) || !source.length) {
      1176. +
      1177. 1 throw new QueryError("No source specified for the query");
      1178. +
      1179. }
      1180. +
      1181. 14 var source = source[0];
      1182. +
      1183. 14 if (isInstanceOf(source, AliasedExpression)) {
      1184. +
      1185. 3 return source.expression;
      1186. +
      1187. 11 } else if (isString(source)) {
      1188. +
      1189. 0 var parts = this._splitString(source);
      1190. +
      1191. 0 return source;
      1192. +
      1193. } else {
      1194. +
      1195. 11 return source;
      1196. +
      1197. }
      1198. +
      1199. },
      1200. +
      1201. +
      1202. sourceList:function () {
      1203. +
      1204. 0 return (this.__opts.from || []).map(this.stringToIdentifier, this);
      1205. +
      1206. },
      1207. +
      1208. +
      1209. joinSourceList:function () {
      1210. +
      1211. 0 return (this.__opts.join || []).map(function (join) {
      1212. +
      1213. 0 return this.stringToIdentifier(join.tableAlias || join.table);
      1214. +
      1215. }, this);
      1216. +
      1217. },
      1218. +
      1219. +
      1220. hasSelectSource:function () {
      1221. +
      1222. 0 var select = this.__opts.select;
      1223. +
      1224. 0 return !(isUndefinedOrNull(select) || select.length === 0);
      1225. +
      1226. }
      1227. +
      1228. },
      1229. +
      1230. +
      1231. /**
      1232. +
      1233. * @ignore
      1234. +
      1235. **/
      1236. +
      1237. setters:{
      1238. +
      1239. /**@lends patio.Dataset.prototype*/
      1240. +
      1241. +
      1242. identifierInputMethod:function (meth) {
      1243. +
      1244. 15038 this.__identifierInputMethod = meth;
      1245. +
      1246. },
      1247. +
      1248. +
      1249. identifierOutputMethod:function (meth) {
      1250. +
      1251. 15038 this.__identifierOutputMethod = meth;
      1252. +
      1253. },
      1254. +
      1255. +
      1256. rowCb:function (cb) {
      1257. +
      1258. 18564 if (isFunction(cb) || isNull(cb)) {
      1259. +
      1260. 18559 this.__rowCb = cb;
      1261. +
      1262. } else {
      1263. +
      1264. 5 throw new DatasetError("rowCb mus be a function");
      1265. +
      1266. }
      1267. +
      1268. }
      1269. +
      1270. }
      1271. +
      1272. },
      1273. +
      1274. +
      1275. static:{
      1276. +
      1277. COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
      1278. +
      1279. COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
      1280. +
      1281. COLUMN_REF_RE3:/^(\w+)__(\w+)$/
      1282. +
      1283. }
      1284. +
      1285. }).as(module);
      1286. +
      1287. +
      +
    + +
    + + + + + +
    +
    associations/_Association.js
    +
    +
    + Coverage87.18 + SLOC515 + LOC156 + Missed20 +
    +
    +
    1. 1var comb = require("comb-proxy"),
    2. +
    3. define = comb.define,
    4. +
    5. isUndefined = comb.isUndefined,
    6. +
    7. isUndefinedOrNull = comb.isUndefinedOrNull,
    8. +
    9. isBoolean = comb.isBoolean,
    10. +
    11. isString = comb.isString,
    12. +
    13. isHash = comb.isHash,
    14. +
    15. isFunction = comb.isFunction,
    16. +
    17. isInstanceOf = comb.isInstanceOf,
    18. +
    19. Promise = comb.Promise,
    20. +
    21. PromiseList = comb.PromiseList,
    22. +
    23. hitch = comb.hitch,
    24. +
    25. array = comb.array,
    26. +
    27. isArray = comb.isArray,
    28. +
    29. Middleware = comb.plugins.Middleware,
    30. +
    31. PatioError = require("../errors").PatioError;
    32. +
    33. +
    34. 1var fetch = {
    35. +
    36. LAZY:"lazy",
    37. +
    38. EAGER:"eager"
    39. +
    40. };
    41. +
    42. +
    43. +
    44. /**
    45. +
    46. * @class
    47. +
    48. * Base class for all associations.
    49. +
    50. *
    51. +
    52. * </br>
    53. +
    54. * <b>NOT to be instantiated directly</b>
    55. +
    56. * Its just documented for reference.
    57. +
    58. *
    59. +
    60. * @constructs
    61. +
    62. * @param {Object} options
    63. +
    64. * @param {String} options.model a string to look up the model that we are associated with
    65. +
    66. * @param {Function} options.filter a callback to find association if a filter is defined then
    67. +
    68. * the association is read only
    69. +
    70. * @param {Object} options.key object with left key and right key
    71. +
    72. * @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
    73. +
    74. * @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
    75. +
    76. * the associations are automatically filled, if fetch.Lazy is supplied
    77. +
    78. * then a promise is returned and is called back with the loaded models
    79. +
    80. * @property {Model} model the model associatied with this association.
    81. +
    82. * @name Association
    83. +
    84. * @memberOf patio.associations
    85. +
    86. * */
    87. +
    88. 1define(Middleware, {
    89. +
    90. instance:{
    91. +
    92. /**@lends patio.associations.Association.prototype*/
    93. +
    94. +
    95. type:"",
    96. +
    97. +
    98. //Our associated model
    99. +
    100. _model:null,
    101. +
    102. +
    103. /**
    104. +
    105. * Fetch type
    106. +
    107. */
    108. +
    109. fetchType:fetch.LAZY,
    110. +
    111. +
    112. /**how to order our association*/
    113. +
    114. orderBy:null,
    115. +
    116. +
    117. /**Our filter method*/
    118. +
    119. filter:null,
    120. +
    121. +
    122. __hooks:null,
    123. +
    124. +
    125. isOwner:true,
    126. +
    127. +
    128. createSetter:true,
    129. +
    130. +
    131. isCascading:false,
    132. +
    133. +
    134. supportsStringKey:true,
    135. +
    136. +
    137. supportsHashKey:true,
    138. +
    139. +
    140. supportsCompositeKey:true,
    141. +
    142. +
    143. supportsLeftAndRightKey:true,
    144. +
    145. +
    146. /**
    147. +
    148. *
    149. +
    150. *Method to call to look up association,
    151. +
    152. *called after the model has been filtered
    153. +
    154. **/
    155. +
    156. _fetchMethod:"all",
    157. +
    158. +
    159. +
    160. constructor:function (options, patio, filter) {
    161. +
    162. 55 options = options || {};
    163. +
    164. 55 if (!options.model) {
    165. +
    166. 0 throw new Error("Model is required for " + this.type + " association");
    167. +
    168. }
    169. +
    170. 55 this._model = options.model;
    171. +
    172. 55 this.patio = patio;
    173. +
    174. 55 this.__opts = options;
    175. +
    176. 55 !isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
    177. +
    178. 55 this.filter = filter;
    179. +
    180. 55 this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
    181. +
    182. 55 this.__hooks =
    183. +
    184. {before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
    185. +
    186. 55 var hooks = ["Add", "Remove", "Set", "Load"];
    187. +
    188. 55 ["before", "after"].forEach(function (h) {
    189. +
    190. 110 hooks.forEach(function (a) {
    191. +
    192. 440 var hookName = h + a, hook;
    193. +
    194. 440 if (isFunction((hook = options[hookName]))) {
    195. +
    196. 0 this.__hooks[h][a.toLowerCase()] = hook;
    197. +
    198. }
    199. +
    200. }, this);
    201. +
    202. }, this);
    203. +
    204. 55 this.fetchType = options.fetchType || fetch.LAZY;
    205. +
    206. },
    207. +
    208. +
    209. _callHook:function (hook, action, args) {
    210. +
    211. 0 var func = this.__hooks[hook][action], ret;
    212. +
    213. 0 if (isFunction(func)) {
    214. +
    215. 0 ret = func.apply(this, args);
    216. +
    217. }
    218. +
    219. 0 return ret;
    220. +
    221. },
    222. +
    223. +
    224. _clearAssociations:function (model) {
    225. +
    226. 125 if (!this.readOnly) {
    227. +
    228. 125 delete model.__associations[this.name];
    229. +
    230. }
    231. +
    232. },
    233. +
    234. +
    235. _forceReloadAssociations:function (model) {
    236. +
    237. 243 if (!this.readOnly) {
    238. +
    239. 243 delete model.__associations[this.name];
    240. +
    241. 243 return model[this.name];
    242. +
    243. }
    244. +
    245. },
    246. +
    247. +
    248. /**
    249. +
    250. * @return {Boolean} true if the association is eager.
    251. +
    252. */
    253. +
    254. isEager:function () {
    255. +
    256. 2114 return this.fetchType == fetch.EAGER;
    257. +
    258. },
    259. +
    260. +
    261. _checkAssociationKey:function (parent) {
    262. +
    263. 785 var q = {};
    264. +
    265. 785 this._setAssociationKeys(parent, q);
    266. +
    267. 785 return Object.keys(q).every(function (k) {
    268. +
    269. 785 return q[k] != null
    270. +
    271. });
    272. +
    273. },
    274. +
    275. +
    276. _getAssociationKey:function () {
    277. +
    278. 5205 var options = this.__opts, key, ret = [], lk, rk;
    279. +
    280. 5205 if (!isUndefinedOrNull((key = options.key))) {
    281. +
    282. 722 if (this.supportsStringKey && isString(key)) {
    283. +
    284. //normalize the key first!
    285. +
    286. 356 ret = [
    287. +
    288. [this.isOwner ? this.defaultLeftKey : key],
    289. +
    290. [this.isOwner ? key : this.defaultRightKey]
    291. +
    292. ];
    293. +
    294. 366 } else if (this.supportsHashKey && isHash(key)) {
    295. +
    296. 366 var leftKey = Object.keys(key)[0];
    297. +
    298. 366 var rightKey = key[leftKey];
    299. +
    300. 366 ret = [
    301. +
    302. [leftKey],
    303. +
    304. [rightKey]
    305. +
    306. ];
    307. +
    308. 0 } else if (this.supportsCompositeKey && isArray(key)) {
    309. +
    310. 0 ret = [
    311. +
    312. [key],
    313. +
    314. null
    315. +
    316. ];
    317. +
    318. }
    319. +
    320. 4483 } else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
    321. +
    322. && !isUndefinedOrNull((rk = options.rightKey)))) {
    323. +
    324. 140 ret = [
    325. +
    326. array.toArray(lk), array.toArray(rk)
    327. +
    328. ];
    329. +
    330. } else {
    331. +
    332. //todo handle composite primary keys
    333. +
    334. 4343 ret = [
    335. +
    336. [this.defaultLeftKey],
    337. +
    338. [this.defaultRightKey]
    339. +
    340. ];
    341. +
    342. }
    343. +
    344. 5205 return ret;
    345. +
    346. },
    347. +
    348. +
    349. +
    350. _setAssociationKeys:function (parent, model, val) {
    351. +
    352. 1573 var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
    353. +
    354. 1573 if (leftKey && rightKey) {
    355. +
    356. 1573 for (var i = 0; i < leftKey.length; i++) {
    357. +
    358. 1573 model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
    359. +
    360. }
    361. +
    362. } else {
    363. +
    364. 0 leftKey.forEach(function (k) {
    365. +
    366. 0 model[k] = !isUndefined(val) ? val : parent[k];
    367. +
    368. });
    369. +
    370. }
    371. +
    372. },
    373. +
    374. +
    375. _setDatasetOptions:function (ds) {
    376. +
    377. 974 var options = this.__opts || {};
    378. +
    379. 974 var order, limit, distinct, select, query;
    380. +
    381. //allow for multi key ordering
    382. +
    383. 974 if (!isUndefined((select = this.select))) {
    384. +
    385. 0 ds = ds.select.apply(ds, array.toArray(select));
    386. +
    387. }
    388. +
    389. 974 if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
    390. +
    391. 0 ds = ds.filter(query);
    392. +
    393. }
    394. +
    395. 974 if (isFunction(this.filter)) {
    396. +
    397. 334 var ret = this.filter.apply(this, [ds]);
    398. +
    399. 334 if (isInstanceOf(ret, ds._static)) {
    400. +
    401. 334 ds = ret;
    402. +
    403. }
    404. +
    405. }
    406. +
    407. 974 if (!isUndefined((distinct = options.distinct))) {
    408. +
    409. 0 ds = ds.limit.apply(ds, array.toArray(distinct));
    410. +
    411. }
    412. +
    413. 974 if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
    414. +
    415. 0 ds = ds.order.apply(ds, array.toArray(order));
    416. +
    417. }
    418. +
    419. 974 if (!isUndefined((limit = options.limit))) {
    420. +
    421. 0 ds = ds.limit.apply(ds, array.toArray(limit));
    422. +
    423. }
    424. +
    425. 974 return ds;
    426. +
    427. +
    428. },
    429. +
    430. +
    431. /**
    432. *Filters our associated dataset to load our association.
    433. *
    434. *@return {Dataset} the dataset with all filters applied.
    435. @@ -6274,9 +6751,9 @@
      - Coverage88.36 - SLOC1061 - LOC318 + Coverage88.47 + SLOC1066 + LOC321 Missed37
      @@ -6474,24 +6951,24 @@
    436. * @borrows patio.Dataset#leftJoin as leftJoin
    437. * */
    438. constructor:function (options, fromDb) {
    439. -
    440. 3218 if (this.synced) {
    441. -
    442. 3218 this.__emitter = new EventEmitter();
    443. -
    444. 3218 this._super(arguments);
    445. -
    446. 3218 this.patio = patio || require("./index");
    447. -
    448. 3218 fromDb = isBoolean(fromDb) ? fromDb : false;
    449. -
    450. 3218 this.__changed = {};
    451. -
    452. 3218 this.__values = {};
    453. -
    454. 3218 if (fromDb) {
    455. -
    456. 1753 this._hook("pre", "load");
    457. -
    458. 1753 this.__isNew = false;
    459. -
    460. 1753 this.__setFromDb(options, true);
    461. -
    462. 1753 if (this._static.emitOnLoad) {
    463. -
    464. 1753 this.emit("load", this);
    465. -
    466. 1753 this._static.emit("load", this);
    467. +
    468. 3107 if (this.synced) {
    469. +
    470. 3107 this.__emitter = new EventEmitter();
    471. +
    472. 3107 this._super(arguments);
    473. +
    474. 3107 this.patio = patio || require("./index");
    475. +
    476. 3107 fromDb = isBoolean(fromDb) ? fromDb : false;
    477. +
    478. 3107 this.__changed = {};
    479. +
    480. 3107 this.__values = {};
    481. +
    482. 3107 if (fromDb) {
    483. +
    484. 1754 this._hook("pre", "load");
    485. +
    486. 1754 this.__isNew = false;
    487. +
    488. 1754 this.__setFromDb(options, true);
    489. +
    490. 1754 if (this._static.emitOnLoad) {
    491. +
    492. 1754 this.emit("load", this);
    493. +
    494. 1754 this._static.emit("load", this);
    495. }
    496. } else {
    497. -
    498. 1465 this.__isNew = true;
    499. -
    500. 1465 this.__set(options);
    501. +
    502. 1353 this.__isNew = true;
    503. +
    504. 1353 this.__set(options);
    505. }
    506. } else {
    507. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
    508. @@ -6499,26 +6976,31 @@
    509. },
    510. __set:function (values, ignore) {
    511. -
    512. 1587 values = values || {};
    513. -
    514. 1587 this.__ignore = ignore === true;
    515. -
    516. 1587 Object.keys(values).forEach(function (attribute) {
    517. +
    518. 1475 values = values || {};
    519. +
    520. 1475 this.__ignore = ignore === true;
    521. +
    522. 1475 Object.keys(values).forEach(function (attribute) {
    523. 6632 var value = values[attribute];
    524. //check if the column is a constrained value and is allowed to be set
    525. 6632 !ignore && this._checkIfColumnIsConstrained(attribute);
    526. 6632 this[attribute] = value;
    527. }, this);
    528. -
    529. 1587 this.__ignore = false;
    530. +
    531. 1475 this.__ignore = false;
    532. },
    533. __setFromDb:function (values, ignore) {
    534. -
    535. 3056 values = values || {};
    536. -
    537. 3056 this.__ignore = ignore === true;
    538. -
    539. 3056 Object.keys(values).forEach(function (column) {
    540. -
    541. 25828 var value = values[column];
    542. +
    543. 3057 values = values || {};
    544. +
    545. 3057 this.__ignore = ignore === true;
    546. +
    547. 3057 var schema = this.schema;
    548. +
    549. 3057 Object.keys(values).forEach(function (column) {
    550. +
    551. 25830 var value = values[column];
    552. // Typecast value retrieved from db
    553. -
    554. 25828 this.__values[column] = this._typeCastValue(column, value, ignore);
    555. +
    556. 25830 if (schema.hasOwnProperty(column)) {
    557. +
    558. 25149 this.__values[column] = this._typeCastValue(column, value, ignore);
    559. +
    560. } else {
    561. +
    562. 681 this[column] = value;
    563. +
    564. }
    565. }, this);
    566. -
    567. 3056 this.__ignore = false;
    568. +
    569. 3057 this.__ignore = false;
    570. },
    571. @@ -6576,11 +7058,11 @@
    572. },
    573. _getColumnValue:function (name) {
    574. -
    575. 5426 var val = this.__values[name];
    576. -
    577. 5426 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
    578. -
    579. 5426 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
    580. +
    581. 5427 var val = this.__values[name];
    582. +
    583. 5427 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
    584. +
    585. 5427 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
    586. -
    587. 5426 return columnValue;
    588. +
    589. 5427 return columnValue;
    590. },
    591. _setColumnValue:function (name, val) {
    592. @@ -6600,25 +7082,25 @@
    593. //typecast_value method, so database adapters can override/augment the handling
    594. //for database specific column types.
    595. _typeCastValue:function (column, value, fromDatabase) {
    596. -
    597. 33384 var colSchema, clazz = this._static;
    598. -
    599. 33384 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
    600. -
    601. 32704 var type = colSchema.type;
    602. -
    603. 32704 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
    604. +
    605. 32705 var colSchema, clazz = this._static;
    606. +
    607. 32705 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
    608. +
    609. 32705 var type = colSchema.type;
    610. +
    611. 32705 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
    612. 3 value = null;
    613. }
    614. -
    615. 32704 var raiseOnError = clazz.raiseOnTypecastError;
    616. -
    617. 32704 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
    618. +
    619. 32705 var raiseOnError = clazz.raiseOnTypecastError;
    620. +
    621. 32705 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
    622. 0 throw new ModelError("null is not allowed for the " + column + " column.");
    623. }
    624. -
    625. 32704 try {
    626. -
    627. 32704 value = clazz.db.typecastValue(type, value);
    628. +
    629. 32705 try {
    630. +
    631. 32705 value = clazz.db.typecastValue(type, value);
    632. } catch (e) {
    633. 0 if (raiseOnError === true) {
    634. 0 throw e;
    635. }
    636. }
    637. }
    638. -
    639. 33384 return value;
    640. +
    641. 32705 return value;
    642. },
    643. /**
    644. @@ -6690,8 +7172,8 @@
    645. 0 return emitter.listeners.apply(emitter, arguments);
    646. },
    647. emit:function () {
    648. -
    649. 11119 var emitter = this.__emitter;
    650. -
    651. 11119 return emitter.emit.apply(emitter, arguments);
    652. +
    653. 11120 var emitter = this.__emitter;
    654. +
    655. 11120 return emitter.emit.apply(emitter, arguments);
    656. },
    657. @@ -6732,7 +7214,7 @@
    658. },
    659. schema:function () {
    660. -
    661. 73400 return this._static.schema;
    662. +
    663. 75099 return this._static.schema;
    664. },
    665. columns:function () {
    666. @@ -6740,7 +7222,7 @@
    667. },
    668. synced:function () {
    669. -
    670. 12891 return this._static.synced;
    671. +
    672. 12780 return this._static.synced;
    673. }
    674. }
    675. @@ -7049,7 +7531,7 @@
    676. _defineColumnGetter:function (name) {
    677. 779 this.prototype.__defineGetter__(name, function () {
    678. -
    679. 5426 return this._getColumnValue(name);
    680. +
    681. 5427 return this._getColumnValue(name);
    682. });
    683. },
    684. @@ -7105,14 +7587,14 @@
    685. * @type patio.Database
    686. */
    687. db:function () {
    688. -
    689. 46373 var db = this.__db;
    690. -
    691. 46373 if (!db) {
    692. +
    693. 46374 var db = this.__db;
    694. +
    695. 46374 if (!db) {
    696. 86 db = this.__db = patio.defaultDatabase;
    697. }
    698. -
    699. 46373 if (!db) {
    700. +
    701. 46374 if (!db) {
    702. 0 throw new ModelError("patio has not been connected to a database");
    703. }
    704. -
    705. 46373 return db;
    706. +
    707. 46374 return db;
    708. },
    709. /**
    710. @@ -7159,8 +7641,8 @@
    711. * @type Object
    712. */
    713. schema:function () {
    714. -
    715. 73404 if (this.synced) {
    716. -
    717. 73404 return this.__schema;
    718. +
    719. 75103 if (this.synced) {
    720. +
    721. 75103 return this.__schema;
    722. } else {
    723. 0 throw new ModelError("Model has not been synced yet");
    724. }
    725. @@ -9060,21 +9542,21 @@
    726. 1var virtualRow = function (name) {
    727. -
    728. 1069 var WILDCARD = new LiteralString('*');
    729. -
    730. 1069 var QUESTION_MARK = new LiteralString('?');
    731. -
    732. 1069 var COMMA_SEPARATOR = new LiteralString(', ');
    733. -
    734. 1069 var DOUBLE_UNDERSCORE = '__';
    735. -
    736. -
    737. 1069 var parts = name.split(DOUBLE_UNDERSCORE);
    738. -
    739. 1069 var table = parts[0], column = parts[1];
    740. -
    741. 1069 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
    742. -
    743. 1069 var prox = methodMissing(ident, function (m) {
    744. +
    745. 875 var WILDCARD = new LiteralString('*');
    746. +
    747. 875 var QUESTION_MARK = new LiteralString('?');
    748. +
    749. 875 var COMMA_SEPARATOR = new LiteralString(', ');
    750. +
    751. 875 var DOUBLE_UNDERSCORE = '__';
    752. +
    753. +
    754. 875 var parts = name.split(DOUBLE_UNDERSCORE);
    755. +
    756. 875 var table = parts[0], column = parts[1];
    757. +
    758. 875 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
    759. +
    760. 875 var prox = methodMissing(ident, function (m) {
    761. 4 return function () {
    762. 3 var args = argsToArray(arguments);
    763. 3 return SQLFunction.fromArgs([m, name].concat(args));
    764. }
    765. }, column ? QualifiedIdentifier : Identifier);
    766. -
    767. 1069 var ret = createFunctionWrapper(prox, function (m) {
    768. +
    769. 875 var ret = createFunctionWrapper(prox, function (m) {
    770. 548 var args = argsToArray(arguments);
    771. 548 if (args.length) {
    772. 542 return SQLFunction.fromArgs([name].concat(args));
    773. @@ -9084,8 +9566,8 @@
    774. }, function () {
    775. 0 return SQLFunction.fromArgs(arguments);
    776. });
    777. -
    778. 1069 ret.__proto__ = ident;
    779. -
    780. 1069 return ret;
    781. +
    782. 875 ret.__proto__ = ident;
    783. +
    784. 875 return ret;
    785. };
    786. 1var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
    787. @@ -9472,3395 +9954,2938 @@
    788. 442 return args.length > 1 ? PlaceHolderLiteralString.fromArgs(args) : new LiteralString(s);
    789. },
    790. -
    791. /**
    792. -
    793. * Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.
    794. -
    795. *
    796. -
    797. * @example
    798. -
    799. *
    800. -
    801. * sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)
    802. -
    803. *
    804. -
    805. */
    806. -
    807. "case":function (hash, /*args**/opts) {
    808. -
    809. 2 var args = argsToArray(arguments, 1);
    810. -
    811. 2 return CaseExpression.fromArgs([hashToArray(hash)].concat(args));
    812. -
    813. },
    814. -
    815. -
    816. /**
    817. -
    818. * Creates a {@link patio.sql.StringExpression}
    819. -
    820. *
    821. -
    822. * Return a {@link patio.sql.StringExpression} representing an SQL string made up of the
    823. -
    824. * concatenation of this array's elements. If an joiner is passed
    825. -
    826. * it is used in between each element of the array in the SQL
    827. -
    828. * concatenation.
    829. -
    830. *
    831. -
    832. * @example
    833. -
    834. * patio.sql.sqlStringJoin(["a"]); //=> a
    835. -
    836. * //you can use sql.* as a shortcut to get an identifier
    837. -
    838. * patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b
    839. -
    840. * patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'
    841. -
    842. * patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b
    843. -
    844. */
    845. -
    846. sqlStringJoin:function (arr, joiner) {
    847. -
    848. 6 joiner = joiner || null;
    849. -
    850. 6 var args;
    851. -
    852. 6 arr = arr.map(function (a) {
    853. -
    854. 12 return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)
    855. -
    856. });
    857. -
    858. 6 if (joiner) {
    859. -
    860. 4 var newJoiner = [];
    861. -
    862. 4 for (var i = 0; i < arr.length; i++) {
    863. -
    864. 9 newJoiner.push(joiner);
    865. -
    866. }
    867. -
    868. 4 args = array.flatten(array.zip(arr, newJoiner));
    869. -
    870. 4 args.pop();
    871. -
    872. } else {
    873. -
    874. 2 args = arr;
    875. -
    876. }
    877. -
    878. 6 args = args.map(function (a) {
    879. -
    880. 17 return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;
    881. -
    882. });
    883. -
    884. 6 return StringExpression.fromArgs(["||"].concat(args));
    885. -
    886. },
    887. -
    888. -
    889. Year:Year,
    890. -
    891. TimeStamp:TimeStamp,
    892. -
    893. Time:Time,
    894. -
    895. DateTime:DateTime,
    896. -
    897. Float:Float,
    898. -
    899. Decimal:Decimal
    900. -
    901. -
    902. };
    903. -
    904. -
    905. 1sql.__defineGetter__("patio", function () {
    906. -
    907. 0 !patio && (patio = require("./index"));
    908. -
    909. 0 return patio;
    910. -
    911. });
    912. -
    913. -
    914. 1exports.sql = methodMissing(sql, function (name) {
    915. -
    916. 1069 return virtualRow(name);
    917. -
    918. });
    919. -
    920. -
    921. 1var OPERTATOR_INVERSIONS = {
    922. -
    923. AND:"OR",
    924. -
    925. OR:"AND",
    926. -
    927. GT:"lte",
    928. -
    929. GTE:"lt",
    930. -
    931. LT:"gte",
    932. -
    933. LTE:"gt",
    934. -
    935. EQ:"neq",
    936. -
    937. NEQ:"eq",
    938. -
    939. LIKE:'NOT LIKE',
    940. -
    941. "NOT LIKE":"LIKE",
    942. -
    943. '!~*':'~*',
    944. -
    945. '~*':'!~*',
    946. -
    947. "~":'!~',
    948. -
    949. "IN":'NOTIN',
    950. -
    951. "NOTIN":"IN",
    952. -
    953. "IS":'IS NOT',
    954. -
    955. "ISNOT":"IS",
    956. -
    957. NOT:"NOOP",
    958. -
    959. NOOP:"NOT",
    960. -
    961. ILIKE:'NOT ILIKE',
    962. -
    963. NOTILIKE:"ILIKE"
    964. -
    965. };
    966. -
    967. -
    968. // Standard mathematical operators used in +NumericMethods+
    969. -
    970. 1var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};
    971. -
    972. -
    973. // Bitwise mathematical operators used in +NumericMethods+
    974. -
    975. 1var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};
    976. -
    977. -
    978. -
    979. 1var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};
    980. -
    981. -
    982. //Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+
    983. -
    984. 1var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};
    985. -
    986. -
    987. //Operators that use IN/NOT IN for inclusion/exclusion
    988. -
    989. 1var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};
    990. -
    991. -
    992. //Operators that use IS, used for special casing to override literal true/false values
    993. -
    994. 1var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};
    995. -
    996. -
    997. //Operator symbols that take exactly two arguments
    998. -
    999. 1var TWO_ARITY_OPERATORS = merge({
    1000. -
    1001. EQ:'=',
    1002. -
    1003. NEQ:'!=', LIKE:"LIKE",
    1004. -
    1005. "NOT LIKE":'NOT LIKE',
    1006. -
    1007. ILIKE:"ILIKE",
    1008. -
    1009. "NOT ILIKE":'NOT ILIKE',
    1010. -
    1011. "~":"~",
    1012. -
    1013. '!~':"!~",
    1014. -
    1015. '~*':"~*",
    1016. -
    1017. '!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);
    1018. -
    1019. -
    1020. //Operator symbols that take one or more arguments
    1021. -
    1022. 1var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);
    1023. -
    1024. -
    1025. //Operator symbols that take only a single argument
    1026. -
    1027. 1var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};
    1028. -
    1029. -
    1030. /**
    1031. -
    1032. * @class Mixin to provide alias methods to an expression.
    1033. -
    1034. *
    1035. -
    1036. * @name AliasMethods
    1037. -
    1038. * @memberOf patio.sql
    1039. -
    1040. */
    1041. -
    1042. 1var AliasMethods = define(null, {
    1043. -
    1044. instance:{
    1045. -
    1046. /**@lends patio.sql.AliasMethods.prototype*/
    1047. -
    1048. -
    1049. /**
    1050. -
    1051. * Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression
    1052. -
    1053. * to the given alias.
    1054. -
    1055. *
    1056. -
    1057. * @example
    1058. -
    1059. *
    1060. -
    1061. * sql.identifier("column").as("alias");
    1062. -
    1063. * //=> "column" AS "alias"
    1064. -
    1065. *
    1066. -
    1067. * @param {String} alias the alias to assign to the expression.
    1068. -
    1069. *
    1070. -
    1071. * @return {patio.sql.AliasedExpression} the aliased expression.
    1072. -
    1073. */
    1074. -
    1075. as:function (alias) {
    1076. -
    1077. 552 return new AliasedExpression(this, alias);
    1078. -
    1079. }
    1080. -
    1081. -
    1082. }
    1083. -
    1084. }).as(sql, "AliasMethods");
    1085. -
    1086. -
    1087. 1var bitWiseMethod = function (op) {
    1088. -
    1089. 5 return function (expression) {
    1090. -
    1091. 0 if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {
    1092. -
    1093. 0 throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
    1094. -
    1095. }
    1096. -
    1097. else {
    1098. -
    1099. 0 return new BooleanExpression(op, this, expression);
    1100. -
    1101. }
    1102. -
    1103. }
    1104. -
    1105. };
    1106. -
    1107. -
    1108. /**
    1109. -
    1110. * @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These
    1111. -
    1112. * methods are only on {@link patio.sql.NumericExpression}
    1113. -
    1114. *
    1115. -
    1116. * @example
    1117. -
    1118. * sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
    1119. -
    1120. * sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
    1121. -
    1122. * sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
    1123. -
    1124. * sql.a.sqlNumber.leftShift("b") // "a" << "b"
    1125. -
    1126. * sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
    1127. -
    1128. *
    1129. -
    1130. * @name BitWiseMethods
    1131. -
    1132. * @memberOf patio.sql
    1133. -
    1134. */
    1135. -
    1136. 1var BitWiseMethods = define(null, {
    1137. -
    1138. instance:{
    1139. -
    1140. /**@lends patio.sql.BitWiseMethods.prototype*/
    1141. -
    1142. -
    1143. /**
    1144. -
    1145. * Bitwise and
    1146. -
    1147. *
    1148. -
    1149. * @example
    1150. -
    1151. * sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
    1152. -
    1153. */
    1154. -
    1155. bitWiseAnd:bitWiseMethod("bitWiseAnd"),
    1156. -
    1157. -
    1158. /**
    1159. -
    1160. * Bitwise or
    1161. -
    1162. *
    1163. -
    1164. * @example
    1165. -
    1166. * sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
    1167. -
    1168. */
    1169. -
    1170. bitWiseOr:bitWiseMethod("bitWiseOr"),
    1171. -
    1172. -
    1173. /**
    1174. -
    1175. * Exclusive Or
    1176. -
    1177. *
    1178. -
    1179. * @example
    1180. -
    1181. *
    1182. -
    1183. * sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
    1184. -
    1185. */
    1186. -
    1187. exclusiveOr:bitWiseMethod("exclusiveOr"),
    1188. -
    1189. -
    1190. /**
    1191. -
    1192. * Bitwise shift left
    1193. -
    1194. *
    1195. -
    1196. * @example
    1197. -
    1198. *
    1199. -
    1200. * sql.a.sqlNumber.leftShift("b") // "a" << "b"
    1201. -
    1202. */
    1203. -
    1204. leftShift:bitWiseMethod("leftShift"),
    1205. -
    1206. -
    1207. /**
    1208. -
    1209. * Bitwise shift right
    1210. -
    1211. *
    1212. -
    1213. * @example
    1214. -
    1215. *
    1216. -
    1217. * sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
    1218. -
    1219. */
    1220. -
    1221. rightShift:bitWiseMethod("rightShift")
    1222. -
    1223. }
    1224. -
    1225. }).as(sql, "BitWiseMethods");
    1226. +
    1227. /**
    1228. +
    1229. * Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.
    1230. +
    1231. *
    1232. +
    1233. * @example
    1234. +
    1235. *
    1236. +
    1237. * sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)
    1238. +
    1239. *
    1240. +
    1241. */
    1242. +
    1243. "case":function (hash, /*args**/opts) {
    1244. +
    1245. 2 var args = argsToArray(arguments, 1);
    1246. +
    1247. 2 return CaseExpression.fromArgs([hashToArray(hash)].concat(args));
    1248. +
    1249. },
    1250. -
    1251. 1var booleanMethod = function (op) {
    1252. -
    1253. 2 return function (expression) {
    1254. -
    1255. 7 if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
    1256. -
    1257. 0 throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");
    1258. -
    1259. }
    1260. -
    1261. else {
    1262. -
    1263. 7 return new BooleanExpression(op, this, expression);
    1264. +
    1265. /**
    1266. +
    1267. * Creates a {@link patio.sql.StringExpression}
    1268. +
    1269. *
    1270. +
    1271. * Return a {@link patio.sql.StringExpression} representing an SQL string made up of the
    1272. +
    1273. * concatenation of this array's elements. If an joiner is passed
    1274. +
    1275. * it is used in between each element of the array in the SQL
    1276. +
    1277. * concatenation.
    1278. +
    1279. *
    1280. +
    1281. * @example
    1282. +
    1283. * patio.sql.sqlStringJoin(["a"]); //=> a
    1284. +
    1285. * //you can use sql.* as a shortcut to get an identifier
    1286. +
    1287. * patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b
    1288. +
    1289. * patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'
    1290. +
    1291. * patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b
    1292. +
    1293. */
    1294. +
    1295. sqlStringJoin:function (arr, joiner) {
    1296. +
    1297. 6 joiner = joiner || null;
    1298. +
    1299. 6 var args;
    1300. +
    1301. 6 arr = arr.map(function (a) {
    1302. +
    1303. 12 return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)
    1304. +
    1305. });
    1306. +
    1307. 6 if (joiner) {
    1308. +
    1309. 4 var newJoiner = [];
    1310. +
    1311. 4 for (var i = 0; i < arr.length; i++) {
    1312. +
    1313. 9 newJoiner.push(joiner);
    1314. +
    1315. }
    1316. +
    1317. 4 args = array.flatten(array.zip(arr, newJoiner));
    1318. +
    1319. 4 args.pop();
    1320. +
    1321. } else {
    1322. +
    1323. 2 args = arr;
    1324. }
    1325. -
    1326. }
    1327. -
    1328. };
    1329. -
    1330. -
    1331. /**
    1332. -
    1333. * @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators
    1334. -
    1335. * that are defined on objects that can be used in a boolean context in SQL
    1336. -
    1337. * ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).
    1338. -
    1339. *
    1340. -
    1341. * @example
    1342. -
    1343. * sql.a.and(sql.b) //=> "a" AND "b"
    1344. -
    1345. * sql.a.or(sql.b) //=> "a" OR "b"
    1346. -
    1347. * sql.a.not() //=> NOT "a"
    1348. -
    1349. *
    1350. -
    1351. * @name BooleanMethods
    1352. -
    1353. * @memberOf patio.sql
    1354. -
    1355. */
    1356. -
    1357. 1var BooleanMethods = define(null, {
    1358. -
    1359. instance:{
    1360. -
    1361. /**@lends patio.sql.BooleanMethods.prototype*/
    1362. -
    1363. -
    1364. /**
    1365. -
    1366. *
    1367. -
    1368. * @function
    1369. -
    1370. * Logical AND
    1371. -
    1372. *
    1373. -
    1374. * @example
    1375. -
    1376. *
    1377. -
    1378. * sql.a.and(sql.b) //=> "a" AND "b"
    1379. -
    1380. *
    1381. -
    1382. * @return {patio.sql.BooleanExpression} a ANDed boolean expression.
    1383. -
    1384. */
    1385. -
    1386. and:booleanMethod("and"),
    1387. +
    1388. 6 args = args.map(function (a) {
    1389. +
    1390. 17 return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;
    1391. +
    1392. });
    1393. +
    1394. 6 return StringExpression.fromArgs(["||"].concat(args));
    1395. +
    1396. },
    1397. -
    1398. /**
    1399. -
    1400. * @function
    1401. -
    1402. * Logical OR
    1403. -
    1404. *
    1405. -
    1406. * @example
    1407. -
    1408. *
    1409. -
    1410. * sql.a.or(sql.b) //=> "a" OR "b"
    1411. -
    1412. *
    1413. -
    1414. * @return {patio.sql.BooleanExpression} a ORed boolean expression
    1415. -
    1416. */
    1417. -
    1418. or:booleanMethod("or"),
    1419. +
    1420. Year:Year,
    1421. +
    1422. TimeStamp:TimeStamp,
    1423. +
    1424. Time:Time,
    1425. +
    1426. DateTime:DateTime,
    1427. +
    1428. Float:Float,
    1429. +
    1430. Decimal:Decimal
    1431. -
    1432. /**
    1433. -
    1434. * Logical NOT
    1435. -
    1436. *
    1437. -
    1438. * @example
    1439. -
    1440. *
    1441. -
    1442. * sql.a.not() //=> NOT "a"
    1443. -
    1444. *
    1445. -
    1446. * @return {patio.sql.BooleanExpression} a inverted boolean expression.
    1447. -
    1448. */
    1449. -
    1450. not:function () {
    1451. -
    1452. 5 return BooleanExpression.invert(this);
    1453. -
    1454. }
    1455. +
    1456. };
    1457. -
    1458. }
    1459. -
    1460. }).as(sql, "BooleanMethods");
    1461. +
    1462. 1sql.__defineGetter__("patio", function () {
    1463. +
    1464. 0 !patio && (patio = require("./index"));
    1465. +
    1466. 0 return patio;
    1467. +
    1468. });
    1469. -
    1470. /**
    1471. -
    1472. * @class Defines case methods
    1473. -
    1474. *
    1475. -
    1476. * @name CastMethods
    1477. -
    1478. * @memberOf patio.sql
    1479. -
    1480. */
    1481. -
    1482. 1var CastMethods = define(null, {
    1483. -
    1484. instance:{
    1485. -
    1486. /**@lends patio.sql.CastMethods.prototype*/
    1487. -
    1488. /**
    1489. -
    1490. * Cast the reciever to the given SQL type.
    1491. -
    1492. *
    1493. -
    1494. * @example
    1495. -
    1496. *
    1497. -
    1498. * sql.a.cast("integer") //=> CAST(a AS integer)
    1499. -
    1500. * sql.a.cast(String) //=> CAST(a AS varchar(255))
    1501. -
    1502. *
    1503. -
    1504. * @return {patio.sql.Cast} the casted expression
    1505. -
    1506. */
    1507. -
    1508. cast:function (type) {
    1509. -
    1510. 2 return new Cast(this, type);
    1511. -
    1512. },
    1513. +
    1514. 1exports.sql = methodMissing(sql, function (name) {
    1515. +
    1516. 875 return virtualRow(name);
    1517. +
    1518. });
    1519. -
    1520. /**
    1521. -
    1522. * Cast the reciever to the given SQL type (or the database's default Number type if none given.
    1523. -
    1524. *
    1525. -
    1526. * @example
    1527. -
    1528. *
    1529. -
    1530. * sql.a.castNumeric() //=> CAST(a AS integer)
    1531. -
    1532. * sql.a.castNumeric("double") //=> CAST(a AS double precision)
    1533. -
    1534. *
    1535. -
    1536. * @param type the numeric type to cast to
    1537. -
    1538. *
    1539. -
    1540. * @return {patio.sql.NumericExpression} a casted numberic expression
    1541. -
    1542. */
    1543. -
    1544. castNumeric:function (type) {
    1545. -
    1546. 0 return this.cast(type || "integer").sqlNumber;
    1547. -
    1548. },
    1549. +
    1550. 1var OPERTATOR_INVERSIONS = {
    1551. +
    1552. AND:"OR",
    1553. +
    1554. OR:"AND",
    1555. +
    1556. GT:"lte",
    1557. +
    1558. GTE:"lt",
    1559. +
    1560. LT:"gte",
    1561. +
    1562. LTE:"gt",
    1563. +
    1564. EQ:"neq",
    1565. +
    1566. NEQ:"eq",
    1567. +
    1568. LIKE:'NOT LIKE',
    1569. +
    1570. "NOT LIKE":"LIKE",
    1571. +
    1572. '!~*':'~*',
    1573. +
    1574. '~*':'!~*',
    1575. +
    1576. "~":'!~',
    1577. +
    1578. "IN":'NOTIN',
    1579. +
    1580. "NOTIN":"IN",
    1581. +
    1582. "IS":'IS NOT',
    1583. +
    1584. "ISNOT":"IS",
    1585. +
    1586. NOT:"NOOP",
    1587. +
    1588. NOOP:"NOT",
    1589. +
    1590. ILIKE:'NOT ILIKE',
    1591. +
    1592. NOTILIKE:"ILIKE"
    1593. +
    1594. };
    1595. -
    1596. /**
    1597. -
    1598. * Cast the reciever to the given SQL type (or the database's default String type if none given),
    1599. -
    1600. * and return the result as a {@link patio.sql.StringExpression}.
    1601. -
    1602. *
    1603. -
    1604. * @example
    1605. -
    1606. *
    1607. -
    1608. * sql.a.castString() //=> CAST(a AS varchar(255))
    1609. -
    1610. * sql.a.castString("text") //=> CAST(a AS text)
    1611. -
    1612. * @param type the string type to cast to
    1613. -
    1614. *
    1615. -
    1616. * @return {patio.sql.StringExpression} the casted string expression
    1617. -
    1618. */
    1619. -
    1620. castString:function (type) {
    1621. -
    1622. 0 return this.cast(type || String).sqlString;
    1623. -
    1624. }
    1625. -
    1626. }
    1627. -
    1628. }).as(sql, "CastMethods");
    1629. +
    1630. // Standard mathematical operators used in +NumericMethods+
    1631. +
    1632. 1var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};
    1633. +
    1634. // Bitwise mathematical operators used in +NumericMethods+
    1635. +
    1636. 1var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};
    1637. -
    1638. /**
    1639. -
    1640. * @class Provides methods to assist in assigning a SQL type to
    1641. -
    1642. * particular types, i.e. Boolean, Function, Number or String.
    1643. -
    1644. *
    1645. -
    1646. * @name ComplexExpressionMethods
    1647. -
    1648. * @memberOf patio.sql
    1649. -
    1650. * @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.
    1651. -
    1652. * @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.
    1653. -
    1654. * @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.
    1655. -
    1656. * <pre class="code">
    1657. -
    1658. * sql.a.not("a") //=> NOT "a"
    1659. -
    1660. * sql.a.sqlNumber.not() //=> ~"a"
    1661. -
    1662. * </pre>
    1663. -
    1664. * @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.
    1665. -
    1666. * <pre class="code">
    1667. -
    1668. * sql.a.plus(sql.b); //=> "a" + "b"
    1669. -
    1670. * sql.a.sqlString.concat(sql.b) //=> "a" || "b"
    1671. -
    1672. * </pre>
    1673. -
    1674. */
    1675. -
    1676. 1var ComplexExpressionMethods = define(null, {
    1677. -
    1678. instance:{
    1679. -
    1680. /**@ignore*/
    1681. -
    1682. getters:{
    1683. -
    1684. /**
    1685. -
    1686. * @ignore
    1687. -
    1688. */
    1689. -
    1690. sqlBoolean:function () {
    1691. -
    1692. 0 return new BooleanExpression("noop", this);
    1693. -
    1694. },
    1695. +
    1696. 1var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};
    1697. +
    1698. //Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+
    1699. +
    1700. 1var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};
    1701. -
    1702. /**
    1703. -
    1704. * @ignore
    1705. -
    1706. */
    1707. -
    1708. sqlFunction:function () {
    1709. -
    1710. 13 return new SQLFunction(this);
    1711. -
    1712. },
    1713. +
    1714. //Operators that use IN/NOT IN for inclusion/exclusion
    1715. +
    1716. 1var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};
    1717. +
    1718. //Operators that use IS, used for special casing to override literal true/false values
    1719. +
    1720. 1var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};
    1721. -
    1722. /**
    1723. -
    1724. * @ignore
    1725. -
    1726. */
    1727. -
    1728. sqlNumber:function () {
    1729. -
    1730. 50 return new NumericExpression("noop", this);
    1731. -
    1732. },
    1733. +
    1734. //Operator symbols that take exactly two arguments
    1735. +
    1736. 1var TWO_ARITY_OPERATORS = merge({
    1737. +
    1738. EQ:'=',
    1739. +
    1740. NEQ:'!=', LIKE:"LIKE",
    1741. +
    1742. "NOT LIKE":'NOT LIKE',
    1743. +
    1744. ILIKE:"ILIKE",
    1745. +
    1746. "NOT ILIKE":'NOT ILIKE',
    1747. +
    1748. "~":"~",
    1749. +
    1750. '!~':"!~",
    1751. +
    1752. '~*':"~*",
    1753. +
    1754. '!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);
    1755. -
    1756. /**
    1757. -
    1758. * @ignore
    1759. -
    1760. */
    1761. -
    1762. sqlString:function () {
    1763. -
    1764. 0 return new StringExpression("noop", this);
    1765. -
    1766. }
    1767. -
    1768. }
    1769. -
    1770. }
    1771. -
    1772. }).as(sql, "ComplexExpressionMethods");
    1773. +
    1774. //Operator symbols that take one or more arguments
    1775. +
    1776. 1var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);
    1777. -
    1778. 1var inequalityMethod = function (op) {
    1779. -
    1780. 6 return function (expression) {
    1781. -
    1782. 88 if (isInstanceOf(expression, BooleanExpression)
    1783. -
    1784. || isBoolean(expression)
    1785. -
    1786. || isNull(expression)
    1787. -
    1788. || (isHash(expression))
    1789. -
    1790. || isArray(expression)) {
    1791. -
    1792. 0 throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
    1793. -
    1794. } else {
    1795. -
    1796. 88 return new BooleanExpression(op, this, expression);
    1797. -
    1798. }
    1799. -
    1800. }
    1801. -
    1802. };
    1803. +
    1804. //Operator symbols that take only a single argument
    1805. +
    1806. 1var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};
    1807. /**
    1808. -
    1809. * @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
    1810. -
    1811. * used in a numeric or string context in SQL.
    1812. -
    1813. *
    1814. -
    1815. * @example
    1816. -
    1817. * sql.a.gt("b") //=> a > "b"
    1818. -
    1819. * sql.a.lt("b") //=> a > "b"
    1820. -
    1821. * sql.a.gte("b") //=> a >= "b"
    1822. -
    1823. * sql.a.lte("b") //=> a <= "b"
    1824. -
    1825. * sql.a.eq("b") //=> a = "b"
    1826. +
    1827. * @class Mixin to provide alias methods to an expression.
    1828. *
    1829. -
    1830. * @name InequalityMethods
    1831. +
    1832. * @name AliasMethods
    1833. * @memberOf patio.sql
    1834. */
    1835. -
    1836. 1var InequalityMethods = define(null, {
    1837. +
    1838. 1var AliasMethods = define(null, {
    1839. instance:{
    1840. -
    1841. /**@lends patio.sql.InequalityMethods.prototype*/
    1842. -
    1843. -
    1844. /**
    1845. -
    1846. * @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.
    1847. -
    1848. * @example
    1849. -
    1850. *
    1851. -
    1852. * sql.a.gt("b") //=> a > "b"
    1853. -
    1854. *
    1855. -
    1856. * @return {patio.sql.BooleanExpression}
    1857. -
    1858. */
    1859. -
    1860. gt:inequalityMethod("gt"),
    1861. -
    1862. /**
    1863. -
    1864. * @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.
    1865. -
    1866. *
    1867. -
    1868. * @example
    1869. -
    1870. *
    1871. -
    1872. * sql.a.gte("b") //=> a >= "b"
    1873. -
    1874. *
    1875. -
    1876. * @return {patio.sql.BooleanExpression}
    1877. -
    1878. */
    1879. -
    1880. gte:inequalityMethod("gte"),
    1881. -
    1882. /**
    1883. -
    1884. * @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.
    1885. -
    1886. *
    1887. -
    1888. * @example
    1889. -
    1890. *
    1891. -
    1892. * sql.a.lt("b") //=> a < "b"
    1893. -
    1894. *
    1895. -
    1896. * @return {patio.sql.BooleanExpression}
    1897. -
    1898. */
    1899. -
    1900. lt:inequalityMethod("lt"),
    1901. -
    1902. /**
    1903. -
    1904. * @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.
    1905. -
    1906. *
    1907. -
    1908. * @example
    1909. -
    1910. *
    1911. -
    1912. * sql.a.lte("b") //=> a <= "b"
    1913. -
    1914. *
    1915. -
    1916. * @return {patio.sql.BooleanExpression}
    1917. -
    1918. */
    1919. -
    1920. lte:inequalityMethod("lte"),
    1921. -
    1922. /**
    1923. -
    1924. * @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.
    1925. -
    1926. *
    1927. -
    1928. * @example
    1929. -
    1930. *
    1931. -
    1932. * sql.a.eq("b") //=> a = "b"
    1933. -
    1934. *
    1935. -
    1936. * @return {patio.sql.BooleanExpression}
    1937. -
    1938. */
    1939. -
    1940. eq:inequalityMethod("eq"),
    1941. -
    1942. -
    1943. neq:inequalityMethod("neq"),
    1944. +
    1945. /**@lends patio.sql.AliasMethods.prototype*/
    1946. /**
    1947. -
    1948. * @private
    1949. -
    1950. *
    1951. -
    1952. * Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
    1953. +
    1954. * Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression
    1955. +
    1956. * to the given alias.
    1957. *
    1958. * @example
    1959. *
    1960. -
    1961. * sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))
    1962. -
    1963. * sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))
    1964. +
    1965. * sql.identifier("column").as("alias");
    1966. +
    1967. * //=> "column" AS "alias"
    1968. *
    1969. -
    1970. * @param {Object} items a two element array where the first element it the item to be gte and the second item lte.
    1971. +
    1972. * @param {String} alias the alias to assign to the expression.
    1973. *
    1974. -
    1975. * @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
    1976. +
    1977. * @return {patio.sql.AliasedExpression} the aliased expression.
    1978. */
    1979. -
    1980. between:function (items) {
    1981. -
    1982. 6 return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))
    1983. +
    1984. as:function (alias) {
    1985. +
    1986. 552 return new AliasedExpression(this, alias);
    1987. }
    1988. -
    1989. }
    1990. -
    1991. }).as(sql, "InequalityMethods");
    1992. -
    1993. /**
    1994. -
    1995. * @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},
    1996. -
    1997. * so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}
    1998. -
    1999. * or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>
    2000. -
    2001. *
    2002. -
    2003. * @name NoBooleanInputMethods
    2004. -
    2005. * @memberOf patio.sql
    2006. -
    2007. */
    2008. -
    2009. 1var NoBooleanInputMethods = define(null, {
    2010. -
    2011. instance:{
    2012. -
    2013. constructor:function (op) {
    2014. -
    2015. 22 var args = argsToArray(arguments, 1);
    2016. -
    2017. 22 args.forEach(function (expression) {
    2018. -
    2019. 26 if ((isInstanceOf(expression, BooleanExpression))
    2020. -
    2021. || isBoolean(expression)
    2022. -
    2023. || isNull(expression)
    2024. -
    2025. || (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))
    2026. -
    2027. || isArray(expression)) {
    2028. -
    2029. 0 throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
    2030. -
    2031. }
    2032. -
    2033. });
    2034. -
    2035. 22 this._super(arguments);
    2036. -
    2037. }
    2038. }
    2039. -
    2040. }).as(sql, "NoBooleanInputMethods");
    2041. +
    2042. }).as(sql, "AliasMethods");
    2043. -
    2044. 1var numericMethod = function (op) {
    2045. -
    2046. 4 return function (expression) {
    2047. -
    2048. 12 if (isInstanceOf(expression, BooleanExpression, StringExpression)) {
    2049. +
    2050. 1var bitWiseMethod = function (op) {
    2051. +
    2052. 5 return function (expression) {
    2053. +
    2054. 0 if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {
    2055. 0 throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
    2056. -
    2057. } else {
    2058. -
    2059. 12 return new NumericExpression(op, this, expression);
    2060. +
    2061. }
    2062. +
    2063. else {
    2064. +
    2065. 0 return new BooleanExpression(op, this, expression);
    2066. }
    2067. }
    2068. };
    2069. -
    2070. /**
    2071. -
    2072. * @class This mixin includes the standard mathematical methods (+, -, *, and /)
    2073. -
    2074. * that are defined on objects that can be used in a numeric context in SQL.
    2075. +
    2076. * @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These
    2077. +
    2078. * methods are only on {@link patio.sql.NumericExpression}
    2079. *
    2080. * @example
    2081. -
    2082. * sql.a.plus(sql.b) //=> "a" + "b"
    2083. -
    2084. * sql.a.minus(sql.b) //=> "a" - "b"
    2085. -
    2086. * sql.a.multiply(sql.b) //=> "a" * "b"
    2087. -
    2088. * sql.a.divide(sql.b) //=> "a" / "b"
    2089. +
    2090. * sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
    2091. +
    2092. * sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
    2093. +
    2094. * sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
    2095. +
    2096. * sql.a.sqlNumber.leftShift("b") // "a" << "b"
    2097. +
    2098. * sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
    2099. *
    2100. -
    2101. * @name NumericMethods
    2102. +
    2103. * @name BitWiseMethods
    2104. * @memberOf patio.sql
    2105. */
    2106. -
    2107. 1var NumericMethods = define(null, {
    2108. +
    2109. 1var BitWiseMethods = define(null, {
    2110. instance:{
    2111. -
    2112. /**@lends patio.sql.NumericMethods.prototype*/
    2113. -
    2114. -
    2115. -
    2116. /**
    2117. -
    2118. * @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.
    2119. -
    2120. *
    2121. -
    2122. * @example
    2123. -
    2124. *
    2125. -
    2126. * sql.a.plus(sql.b) //=> "a" + "b"
    2127. -
    2128. *
    2129. -
    2130. * @return {patio.sql.NumericExpression}
    2131. -
    2132. */
    2133. -
    2134. plus:numericMethod("plus"),
    2135. +
    2136. /**@lends patio.sql.BitWiseMethods.prototype*/
    2137. /**
    2138. -
    2139. * @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.
    2140. +
    2141. * Bitwise and
    2142. *
    2143. * @example
    2144. -
    2145. *
    2146. -
    2147. * sql.a.minus(sql.b) //=> "a" - "b"
    2148. -
    2149. *
    2150. -
    2151. * @return {patio.sql.NumericExpression}
    2152. +
    2153. * sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
    2154. */
    2155. -
    2156. minus:numericMethod("minus"),
    2157. +
    2158. bitWiseAnd:bitWiseMethod("bitWiseAnd"),
    2159. /**
    2160. -
    2161. * @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
    2162. +
    2163. * Bitwise or
    2164. *
    2165. * @example
    2166. -
    2167. *
    2168. -
    2169. * sql.a.divide(sql.b) //=> "a" / "b"
    2170. -
    2171. *
    2172. -
    2173. * @return {patio.sql.NumericExpression}
    2174. +
    2175. * sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
    2176. */
    2177. -
    2178. divide:numericMethod("divide"),
    2179. +
    2180. bitWiseOr:bitWiseMethod("bitWiseOr"),
    2181. /**
    2182. -
    2183. * @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
    2184. +
    2185. * Exclusive Or
    2186. *
    2187. * @example
    2188. *
    2189. -
    2190. * sql.a.multiply(sql.b) //=> "a" * "b"
    2191. -
    2192. *
    2193. -
    2194. * @return {patio.sql.NumericExpression}
    2195. +
    2196. * sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
    2197. */
    2198. -
    2199. multiply:numericMethod("multiply")
    2200. -
    2201. }
    2202. -
    2203. }).as(sql, "NumericMethods");
    2204. -
    2205. -
    2206. -
    2207. /**
    2208. -
    2209. * @class This mixin provides ordering methods ("asc", "desc") to expression.
    2210. -
    2211. *
    2212. -
    2213. * @example
    2214. -
    2215. *
    2216. -
    2217. * sql.name.asc(); //=> name ASC
    2218. -
    2219. * sql.price.desc(); //=> price DESC
    2220. -
    2221. * sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
    2222. -
    2223. * sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
    2224. -
    2225. *
    2226. -
    2227. * @name OrderedMethods
    2228. -
    2229. * @memberOf patio.sql
    2230. -
    2231. */
    2232. -
    2233. 1var OrderedMethods = define(null, {
    2234. -
    2235. instance:{
    2236. -
    2237. /**@lends patio.sql.OrderedMethods.prototype*/
    2238. +
    2239. exclusiveOr:bitWiseMethod("exclusiveOr"),
    2240. /**
    2241. -
    2242. * Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
    2243. +
    2244. * Bitwise shift left
    2245. *
    2246. -
    2247. * @example
    2248. -
    2249. * sql.name.asc(); //=> name ASC
    2250. -
    2251. * sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
    2252. +
    2253. * @example
    2254. *
    2255. -
    2256. * @param {Object} [options] options to use when sorting
    2257. -
    2258. * @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
    2259. -
    2260. * before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
    2261. -
    2262. * @return {patio.sql.OrderedExpression}
    2263. +
    2264. * sql.a.sqlNumber.leftShift("b") // "a" << "b"
    2265. */
    2266. -
    2267. asc:function (options) {
    2268. -
    2269. 7 return new OrderedExpression(this, false, options);
    2270. -
    2271. },
    2272. +
    2273. leftShift:bitWiseMethod("leftShift"),
    2274. /**
    2275. -
    2276. * Mark the receiving SQL column as sorting in a descending fashion.
    2277. -
    2278. * @example
    2279. +
    2280. * Bitwise shift right
    2281. *
    2282. -
    2283. * sql.price.desc(); //=> price DESC
    2284. -
    2285. * sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
    2286. +
    2287. * @example
    2288. *
    2289. -
    2290. * @param {Object} [options] options to use when sorting
    2291. -
    2292. * @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
    2293. -
    2294. * before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
    2295. -
    2296. * @return {patio.sql.OrderedExpression}
    2297. +
    2298. * sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
    2299. */
    2300. -
    2301. desc:function (options) {
    2302. -
    2303. 26 return new OrderedExpression(this, true, options);
    2304. -
    2305. }
    2306. +
    2307. rightShift:bitWiseMethod("rightShift")
    2308. }
    2309. -
    2310. }).as(sql, "OrderedMethods");
    2311. +
    2312. }).as(sql, "BitWiseMethods");
    2313. +
    2314. 1var booleanMethod = function (op) {
    2315. +
    2316. 2 return function (expression) {
    2317. +
    2318. 7 if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
    2319. +
    2320. 0 throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");
    2321. +
    2322. }
    2323. +
    2324. else {
    2325. +
    2326. 7 return new BooleanExpression(op, this, expression);
    2327. +
    2328. }
    2329. +
    2330. }
    2331. +
    2332. };
    2333. /**
    2334. -
    2335. * @class This mixin provides methods related to qualifying expression.
    2336. +
    2337. * @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators
    2338. +
    2339. * that are defined on objects that can be used in a boolean context in SQL
    2340. +
    2341. * ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).
    2342. *
    2343. * @example
    2344. +
    2345. * sql.a.and(sql.b) //=> "a" AND "b"
    2346. +
    2347. * sql.a.or(sql.b) //=> "a" OR "b"
    2348. +
    2349. * sql.a.not() //=> NOT "a"
    2350. *
    2351. -
    2352. * sql.column.qualify("table") //=> "table"."column"
    2353. -
    2354. * sql.table.qualify("schema") //=> "schema"."table"
    2355. -
    2356. * sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
    2357. -
    2358. *
    2359. -
    2360. * @name QualifyingMethods
    2361. +
    2362. * @name BooleanMethods
    2363. * @memberOf patio.sql
    2364. */
    2365. -
    2366. 1var QualifyingMethods = define(null, {
    2367. +
    2368. 1var BooleanMethods = define(null, {
    2369. instance:{
    2370. -
    2371. /**@lends patio.sql.QualifyingMethods.prototype*/
    2372. +
    2373. /**@lends patio.sql.BooleanMethods.prototype*/
    2374. /**
    2375. -
    2376. * Qualify the receiver with the given qualifier (table for column/schema for table).
    2377. +
    2378. *
    2379. +
    2380. * @function
    2381. +
    2382. * Logical AND
    2383. *
    2384. * @example
    2385. -
    2386. * sql.column.qualify("table") //=> "table"."column"
    2387. -
    2388. * sql.table.qualify("schema") //=> "schema"."table"
    2389. -
    2390. * sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
    2391. *
    2392. -
    2393. * @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.
    2394. +
    2395. * sql.a.and(sql.b) //=> "a" AND "b"
    2396. *
    2397. -
    2398. * @return {patio.sql.QualifiedIdentifier}
    2399. +
    2400. * @return {patio.sql.BooleanExpression} a ANDed boolean expression.
    2401. */
    2402. -
    2403. qualify:function (qualifier) {
    2404. -
    2405. 576 return new QualifiedIdentifier(qualifier, this);
    2406. -
    2407. },
    2408. +
    2409. and:booleanMethod("and"),
    2410. /**
    2411. -
    2412. * Use to create a .* expression.
    2413. +
    2414. * @function
    2415. +
    2416. * Logical OR
    2417. *
    2418. * @example
    2419. -
    2420. * sql.table.all() //=> "table".*
    2421. -
    2422. * sql.table.qualify("schema").all() //=> "schema"."table".*
    2423. *
    2424. +
    2425. * sql.a.or(sql.b) //=> "a" OR "b"
    2426. *
    2427. -
    2428. * @return {patio.sql.ColumnAll}
    2429. +
    2430. * @return {patio.sql.BooleanExpression} a ORed boolean expression
    2431. */
    2432. -
    2433. all:function () {
    2434. -
    2435. 3 return new ColumnAll(this);
    2436. -
    2437. }
    2438. +
    2439. or:booleanMethod("or"),
    2440. +
    2441. /**
    2442. +
    2443. * Logical NOT
    2444. +
    2445. *
    2446. +
    2447. * @example
    2448. +
    2449. *
    2450. +
    2451. * sql.a.not() //=> NOT "a"
    2452. +
    2453. *
    2454. +
    2455. * @return {patio.sql.BooleanExpression} a inverted boolean expression.
    2456. +
    2457. */
    2458. +
    2459. not:function () {
    2460. +
    2461. 5 return BooleanExpression.invert(this);
    2462. +
    2463. }
    2464. }
    2465. -
    2466. }).as(sql, "QualifyingMethods");
    2467. -
    2468. +
    2469. }).as(sql, "BooleanMethods");
    2470. /**
    2471. -
    2472. * @class This mixin provides SQL string methods such as (like and iLike).
    2473. -
    2474. *
    2475. -
    2476. * @example
    2477. -
    2478. *
    2479. -
    2480. * sql.a.like("A%"); //=> "a" LIKE 'A%'
    2481. -
    2482. * sql.a.iLike("A%"); //=> "a" LIKE 'A%'
    2483. -
    2484. * sql.a.like(/^a/); //=> "a" ~* '^a'
    2485. +
    2486. * @class Defines case methods
    2487. *
    2488. -
    2489. * @name StringMethods
    2490. +
    2491. * @name CastMethods
    2492. * @memberOf patio.sql
    2493. */
    2494. -
    2495. 1var StringMethods = define(null, {
    2496. +
    2497. 1var CastMethods = define(null, {
    2498. instance:{
    2499. -
    2500. /**@lends patio.sql.StringMethods.prototype*/
    2501. -
    2502. +
    2503. /**@lends patio.sql.CastMethods.prototype*/
    2504. /**
    2505. -
    2506. * Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver
    2507. -
    2508. * with the given patterns. See {@link patio.sql.StringExpression#like}.
    2509. +
    2510. * Cast the reciever to the given SQL type.
    2511. *
    2512. * @example
    2513. -
    2514. * sql.a.iLike("A%"); //=> "a" LIKE 'A%'
    2515. *
    2516. -
    2517. * @return {patio.sql.BooleanExpression}
    2518. +
    2519. * sql.a.cast("integer") //=> CAST(a AS integer)
    2520. +
    2521. * sql.a.cast(String) //=> CAST(a AS varchar(255))
    2522. +
    2523. *
    2524. +
    2525. * @return {patio.sql.Cast} the casted expression
    2526. */
    2527. -
    2528. ilike:function (expression) {
    2529. -
    2530. 310 expression = argsToArray(arguments);
    2531. -
    2532. 310 return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([
    2533. -
    2534. {caseInsensitive:true}
    2535. -
    2536. ]));
    2537. +
    2538. cast:function (type) {
    2539. +
    2540. 2 return new Cast(this, type);
    2541. },
    2542. /**
    2543. -
    2544. * Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with
    2545. -
    2546. * the given patterns. See {@link patio.sql.StringExpression#like}.
    2547. +
    2548. * Cast the reciever to the given SQL type (or the database's default Number type if none given.
    2549. *
    2550. * @example
    2551. -
    2552. * sql.a.like(/^a/); //=> "a" ~* '^a'
    2553. -
    2554. * sql.a.like("A%"); //=> "a" LIKE 'A%'
    2555. *
    2556. -
    2557. * @param expression
    2558. +
    2559. * sql.a.castNumeric() //=> CAST(a AS integer)
    2560. +
    2561. * sql.a.castNumeric("double") //=> CAST(a AS double precision)
    2562. +
    2563. *
    2564. +
    2565. * @param type the numeric type to cast to
    2566. +
    2567. *
    2568. +
    2569. * @return {patio.sql.NumericExpression} a casted numberic expression
    2570. */
    2571. -
    2572. like:function (expression) {
    2573. -
    2574. 11 expression = argsToArray(arguments);
    2575. -
    2576. 11 return StringExpression.like.apply(StringExpression, [this].concat(expression));
    2577. -
    2578. }
    2579. -
    2580. }
    2581. -
    2582. }).as(sql, "StringMethods");
    2583. -
    2584. -
    2585. /**
    2586. -
    2587. * @class This mixin provides string concatenation methods ("concat");
    2588. -
    2589. *
    2590. -
    2591. * @example
    2592. -
    2593. *
    2594. -
    2595. * sql.x.sqlString.concat("y"); //=> "x" || "y"
    2596. -
    2597. *
    2598. -
    2599. * @name StringConcatenationMethods
    2600. -
    2601. * @memberOf patio.sql
    2602. -
    2603. */
    2604. -
    2605. 1var StringConcatenationMethods = define(null, {
    2606. -
    2607. instance:{
    2608. -
    2609. /**@lends patio.sql.StringConcatenationMethods.prototype*/
    2610. +
    2611. castNumeric:function (type) {
    2612. +
    2613. 0 return this.cast(type || "integer").sqlNumber;
    2614. +
    2615. },
    2616. /**
    2617. -
    2618. * Return a {@link patio.sql.StringExpression} representing the concatenation of this expression
    2619. -
    2620. * with the given argument.
    2621. +
    2622. * Cast the reciever to the given SQL type (or the database's default String type if none given),
    2623. +
    2624. * and return the result as a {@link patio.sql.StringExpression}.
    2625. *
    2626. * @example
    2627. *
    2628. -
    2629. * sql.x.sqlString.concat("y"); //=> "x" || "y"
    2630. +
    2631. * sql.a.castString() //=> CAST(a AS varchar(255))
    2632. +
    2633. * sql.a.castString("text") //=> CAST(a AS text)
    2634. +
    2635. * @param type the string type to cast to
    2636. *
    2637. -
    2638. * @param expression expression to concatenate this expression with.
    2639. +
    2640. * @return {patio.sql.StringExpression} the casted string expression
    2641. */
    2642. -
    2643. concat:function (expression) {
    2644. -
    2645. 0 return new StringExpression("||", this, expression);
    2646. +
    2647. castString:function (type) {
    2648. +
    2649. 0 return this.cast(type || String).sqlString;
    2650. }
    2651. }
    2652. -
    2653. }).as(sql, "StringConcatenationMethods");
    2654. +
    2655. }).as(sql, "CastMethods");
    2656. +
    2657. /**
    2658. -
    2659. * @class This mixin provides the ability to access elements within a SQL array.
    2660. -
    2661. *
    2662. -
    2663. * @example
    2664. -
    2665. * sql.array.sqlSubscript(1) //=> array[1]
    2666. -
    2667. * sql.array.sqlSubscript(1, 2) //=> array[1, 2]
    2668. -
    2669. * sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
    2670. +
    2671. * @class Provides methods to assist in assigning a SQL type to
    2672. +
    2673. * particular types, i.e. Boolean, Function, Number or String.
    2674. *
    2675. -
    2676. * @name SubscriptMethods
    2677. +
    2678. * @name ComplexExpressionMethods
    2679. * @memberOf patio.sql
    2680. +
    2681. * @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.
    2682. +
    2683. * @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.
    2684. +
    2685. * @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.
    2686. +
    2687. * <pre class="code">
    2688. +
    2689. * sql.a.not("a") //=> NOT "a"
    2690. +
    2691. * sql.a.sqlNumber.not() //=> ~"a"
    2692. +
    2693. * </pre>
    2694. +
    2695. * @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.
    2696. +
    2697. * <pre class="code">
    2698. +
    2699. * sql.a.plus(sql.b); //=> "a" + "b"
    2700. +
    2701. * sql.a.sqlString.concat(sql.b) //=> "a" || "b"
    2702. +
    2703. * </pre>
    2704. */
    2705. -
    2706. 1var SubscriptMethods = define(null, {
    2707. +
    2708. 1var ComplexExpressionMethods = define(null, {
    2709. instance:{
    2710. +
    2711. /**@ignore*/
    2712. +
    2713. getters:{
    2714. -
    2715. /**
    2716. -
    2717. * Return a {@link patio.sql.Subscript} with the given arguments, representing an
    2718. -
    2719. * SQL array access.
    2720. -
    2721. *
    2722. -
    2723. * @example
    2724. -
    2725. * sql.array.sqlSubscript(1) //=> array[1]
    2726. -
    2727. * sql.array.sqlSubscript(1, 2) //=> array[1, 2]
    2728. -
    2729. * sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
    2730. -
    2731. *
    2732. -
    2733. * @param subscript
    2734. -
    2735. */
    2736. -
    2737. sqlSubscript:function (subscript) {
    2738. -
    2739. 108 var args = argsToArray(arguments);
    2740. -
    2741. 108 return new SubScript(this, flatten(args));
    2742. +
    2743. /**
    2744. +
    2745. * @ignore
    2746. +
    2747. */
    2748. +
    2749. sqlBoolean:function () {
    2750. +
    2751. 0 return new BooleanExpression("noop", this);
    2752. +
    2753. },
    2754. +
    2755. +
    2756. +
    2757. /**
    2758. +
    2759. * @ignore
    2760. +
    2761. */
    2762. +
    2763. sqlFunction:function () {
    2764. +
    2765. 13 return new SQLFunction(this);
    2766. +
    2767. },
    2768. +
    2769. +
    2770. +
    2771. /**
    2772. +
    2773. * @ignore
    2774. +
    2775. */
    2776. +
    2777. sqlNumber:function () {
    2778. +
    2779. 50 return new NumericExpression("noop", this);
    2780. +
    2781. },
    2782. +
    2783. +
    2784. /**
    2785. +
    2786. * @ignore
    2787. +
    2788. */
    2789. +
    2790. sqlString:function () {
    2791. +
    2792. 0 return new StringExpression("noop", this);
    2793. +
    2794. }
    2795. }
    2796. }
    2797. -
    2798. }).as(sql, "SubScriptMethods");
    2799. +
    2800. }).as(sql, "ComplexExpressionMethods");
    2801. +
    2802. 1var inequalityMethod = function (op) {
    2803. +
    2804. 6 return function (expression) {
    2805. +
    2806. 88 if (isInstanceOf(expression, BooleanExpression)
    2807. +
    2808. || isBoolean(expression)
    2809. +
    2810. || isNull(expression)
    2811. +
    2812. || (isHash(expression))
    2813. +
    2814. || isArray(expression)) {
    2815. +
    2816. 0 throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
    2817. +
    2818. } else {
    2819. +
    2820. 88 return new BooleanExpression(op, this, expression);
    2821. +
    2822. }
    2823. +
    2824. }
    2825. +
    2826. };
    2827. /**
    2828. -
    2829. * @class This is the parent of all expressions.
    2830. +
    2831. * @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
    2832. +
    2833. * used in a numeric or string context in SQL.
    2834. *
    2835. -
    2836. * @name Expression
    2837. +
    2838. * @example
    2839. +
    2840. * sql.a.gt("b") //=> a > "b"
    2841. +
    2842. * sql.a.lt("b") //=> a > "b"
    2843. +
    2844. * sql.a.gte("b") //=> a >= "b"
    2845. +
    2846. * sql.a.lte("b") //=> a <= "b"
    2847. +
    2848. * sql.a.eq("b") //=> a = "b"
    2849. +
    2850. *
    2851. +
    2852. * @name InequalityMethods
    2853. * @memberOf patio.sql
    2854. */
    2855. -
    2856. 1var Expression = define(null, {
    2857. -
    2858. +
    2859. 1var InequalityMethods = define(null, {
    2860. instance:{
    2861. -
    2862. /**@lends patio.sql.Expression.prototype*/
    2863. +
    2864. /**@lends patio.sql.InequalityMethods.prototype*/
    2865. /**
    2866. -
    2867. * Returns the string representation of this expression
    2868. +
    2869. * @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.
    2870. +
    2871. * @example
    2872. *
    2873. -
    2874. * @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.
    2875. -
    2876. * @return {String} a string literal version of this expression.
    2877. +
    2878. * sql.a.gt("b") //=> a > "b"
    2879. +
    2880. *
    2881. +
    2882. * @return {patio.sql.BooleanExpression}
    2883. */
    2884. -
    2885. sqlLiteral:function (ds) {
    2886. -
    2887. 0 return this.toString(ds);
    2888. -
    2889. }
    2890. -
    2891. -
    2892. },
    2893. -
    2894. -
    2895. static:{
    2896. -
    2897. /**@lends patio.sql.Expression*/
    2898. -
    2899. +
    2900. gt:inequalityMethod("gt"),
    2901. /**
    2902. -
    2903. * This is a helper method that will take in an array of arguments and return an expression.
    2904. +
    2905. * @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.
    2906. *
    2907. * @example
    2908. *
    2909. -
    2910. * QualifiedIdentifier.fromArgs(["table", "column"]);
    2911. +
    2912. * sql.a.gte("b") //=> a >= "b"
    2913. *
    2914. -
    2915. * @param {*[]} args array of arguments to pass into the constructor of the function.
    2916. +
    2917. * @return {patio.sql.BooleanExpression}
    2918. +
    2919. */
    2920. +
    2921. gte:inequalityMethod("gte"),
    2922. +
    2923. /**
    2924. +
    2925. * @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.
    2926. *
    2927. -
    2928. * @return {patio.sql.Expression} an expression.
    2929. +
    2930. * @example
    2931. +
    2932. *
    2933. +
    2934. * sql.a.lt("b") //=> a < "b"
    2935. +
    2936. *
    2937. +
    2938. * @return {patio.sql.BooleanExpression}
    2939. */
    2940. -
    2941. fromArgs:function (args) {
    2942. -
    2943. 2410 var ret;
    2944. -
    2945. 2410 try {
    2946. -
    2947. 2410 ret = new this();
    2948. -
    2949. } catch (ignore) {
    2950. -
    2951. }
    2952. -
    2953. 2410 this.apply(ret, args);
    2954. -
    2955. 2410 return ret;
    2956. -
    2957. },
    2958. +
    2959. lt:inequalityMethod("lt"),
    2960. +
    2961. /**
    2962. +
    2963. * @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.
    2964. +
    2965. *
    2966. +
    2967. * @example
    2968. +
    2969. *
    2970. +
    2971. * sql.a.lte("b") //=> a <= "b"
    2972. +
    2973. *
    2974. +
    2975. * @return {patio.sql.BooleanExpression}
    2976. +
    2977. */
    2978. +
    2979. lte:inequalityMethod("lte"),
    2980. +
    2981. /**
    2982. +
    2983. * @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.
    2984. +
    2985. *
    2986. +
    2987. * @example
    2988. +
    2989. *
    2990. +
    2991. * sql.a.eq("b") //=> a = "b"
    2992. +
    2993. *
    2994. +
    2995. * @return {patio.sql.BooleanExpression}
    2996. +
    2997. */
    2998. +
    2999. eq:inequalityMethod("eq"),
    3000. +
    3001. +
    3002. neq:inequalityMethod("neq"),
    3003. /**
    3004. -
    3005. * Helper to determine if something is a condition specifier. Returns true if the object
    3006. -
    3007. * is a Hash or is an array of two element arrays.
    3008. +
    3009. * @private
    3010. +
    3011. *
    3012. +
    3013. * Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
    3014. *
    3015. * @example
    3016. -
    3017. * Expression.isConditionSpecifier({a : "b"}); //=> true
    3018. -
    3019. * Expression.isConditionSpecifier("a"); //=> false
    3020. -
    3021. * Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true
    3022. -
    3023. * Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false
    3024. *
    3025. -
    3026. * @param {*} obj object to test if it is a condition specifier
    3027. -
    3028. * @return {Boolean} true if the object is a Hash or is an array of two element arrays.
    3029. +
    3030. * sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))
    3031. +
    3032. * sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))
    3033. +
    3034. *
    3035. +
    3036. * @param {Object} items a two element array where the first element it the item to be gte and the second item lte.
    3037. +
    3038. *
    3039. +
    3040. * @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
    3041. */
    3042. -
    3043. isConditionSpecifier:function (obj) {
    3044. -
    3045. 22715 return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
    3046. -
    3047. 9896 return isArray(i) && i.length === 2;
    3048. -
    3049. }));
    3050. +
    3051. between:function (items) {
    3052. +
    3053. 6 return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))
    3054. }
    3055. }
    3056. -
    3057. -
    3058. }).as(sql, "Expression");
    3059. +
    3060. }).as(sql, "InequalityMethods");
    3061. /**
    3062. -
    3063. * @class Base class for all GenericExpressions
    3064. -
    3065. *
    3066. -
    3067. * @augments patio.sql.Expression
    3068. -
    3069. * @augments patio.sql.AliasMethods
    3070. -
    3071. * @augments patio.sql.BooleanMethods
    3072. -
    3073. * @augments patio.sql.CastMethods
    3074. -
    3075. * @augments patio.sql.ComplexExpressionMethods
    3076. -
    3077. * @augments patio.sql.InequalityMethods
    3078. -
    3079. * @augments patio.sql.NumericMethods
    3080. -
    3081. * @augments patio.sql.OrderedMethods
    3082. -
    3083. * @augments patio.sql.StringMethods
    3084. -
    3085. * @augments patio.sql.SubscriptMethods
    3086. +
    3087. * @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},
    3088. +
    3089. * so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}
    3090. +
    3091. * or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>
    3092. *
    3093. -
    3094. * @name GenericExpression
    3095. +
    3096. * @name NoBooleanInputMethods
    3097. * @memberOf patio.sql
    3098. */
    3099. -
    3100. 1var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");
    3101. -
    3102. -
    3103. -
    3104. 1var AliasedExpression = define(Expression, {
    3105. -
    3106. instance:{
    3107. -
    3108. /**@lends patio.sql.AliasedExpression.prototype*/
    3109. -
    3110. -
    3111. /**
    3112. -
    3113. * This class reperesents an Aliased Expression
    3114. -
    3115. *
    3116. -
    3117. * @constructs
    3118. -
    3119. * @augments patio.sql.Expression
    3120. -
    3121. *
    3122. -
    3123. * @param expression the expression to alias.
    3124. -
    3125. * @param alias the alias to alias the expression to.
    3126. -
    3127. *
    3128. -
    3129. * @property expression the expression being aliased
    3130. -
    3131. * @property alias the alias of the expression
    3132. -
    3133. *
    3134. -
    3135. */
    3136. -
    3137. constructor:function (expression, alias) {
    3138. -
    3139. 945 this.expression = expression;
    3140. -
    3141. 945 this.alias = alias;
    3142. -
    3143. },
    3144. +
    3145. 1var NoBooleanInputMethods = define(null, {
    3146. +
    3147. instance:{
    3148. +
    3149. constructor:function (op) {
    3150. +
    3151. 22 var args = argsToArray(arguments, 1);
    3152. +
    3153. 22 args.forEach(function (expression) {
    3154. +
    3155. 26 if ((isInstanceOf(expression, BooleanExpression))
    3156. +
    3157. || isBoolean(expression)
    3158. +
    3159. || isNull(expression)
    3160. +
    3161. || (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))
    3162. +
    3163. || isArray(expression)) {
    3164. +
    3165. 0 throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
    3166. +
    3167. }
    3168. +
    3169. });
    3170. +
    3171. 22 this._super(arguments);
    3172. +
    3173. }
    3174. +
    3175. }
    3176. +
    3177. }).as(sql, "NoBooleanInputMethods");
    3178. -
    3179. /**
    3180. -
    3181. * Converts the aliased expression to a string
    3182. -
    3183. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3184. -
    3185. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3186. -
    3187. *
    3188. -
    3189. * @return String the SQL alias fragment.
    3190. -
    3191. */
    3192. -
    3193. toString:function (ds) {
    3194. -
    3195. 877 !Dataset && (Dataset = require("./dataset"));
    3196. -
    3197. 877 ds = ds || new Dataset();
    3198. -
    3199. 877 return ds.aliasedExpressionSql(this);
    3200. -
    3201. }
    3202. +
    3203. 1var numericMethod = function (op) {
    3204. +
    3205. 4 return function (expression) {
    3206. +
    3207. 12 if (isInstanceOf(expression, BooleanExpression, StringExpression)) {
    3208. +
    3209. 0 throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
    3210. +
    3211. } else {
    3212. +
    3213. 12 return new NumericExpression(op, this, expression);
    3214. }
    3215. }
    3216. -
    3217. ).as(sql, "AliasedExpression");
    3218. +
    3219. };
    3220. -
    3221. 1var CaseExpression = define(GenericExpression, {
    3222. +
    3223. /**
    3224. +
    3225. * @class This mixin includes the standard mathematical methods (+, -, *, and /)
    3226. +
    3227. * that are defined on objects that can be used in a numeric context in SQL.
    3228. +
    3229. *
    3230. +
    3231. * @example
    3232. +
    3233. * sql.a.plus(sql.b) //=> "a" + "b"
    3234. +
    3235. * sql.a.minus(sql.b) //=> "a" - "b"
    3236. +
    3237. * sql.a.multiply(sql.b) //=> "a" * "b"
    3238. +
    3239. * sql.a.divide(sql.b) //=> "a" / "b"
    3240. +
    3241. *
    3242. +
    3243. * @name NumericMethods
    3244. +
    3245. * @memberOf patio.sql
    3246. +
    3247. */
    3248. +
    3249. 1var NumericMethods = define(null, {
    3250. instance:{
    3251. -
    3252. /**@lends patio.sql.CaseExpression.prototype*/
    3253. +
    3254. /**@lends patio.sql.NumericMethods.prototype*/
    3255. +
    3256. /**
    3257. -
    3258. * Create an object with the given conditions and
    3259. -
    3260. * default value. An expression can be provided to
    3261. -
    3262. * test each condition against, instead of having
    3263. -
    3264. * all conditions represent their own boolean expression.
    3265. +
    3266. * @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.
    3267. *
    3268. -
    3269. * @constructs
    3270. -
    3271. * @augments patio.sql.GenericExpression
    3272. -
    3273. * @param {Array|Object} conditions conditions to create the case expression from
    3274. -
    3275. * @param def default value
    3276. -
    3277. * @param expression expression to create the CASE expression from
    3278. +
    3279. * @example
    3280. *
    3281. -
    3282. * @property {Boolean} hasExpression returns true if this case expression has a expression
    3283. -
    3284. * @property conditions the conditions of the {@link patio.sql.CaseExpression}.
    3285. -
    3286. * @property def the default value of the {@link patio.sql.CaseExpression}.
    3287. -
    3288. * @property expression the expression of the {@link patio.sql.CaseExpression}.
    3289. -
    3290. * @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.
    3291. +
    3292. * sql.a.plus(sql.b) //=> "a" + "b"
    3293. +
    3294. *
    3295. +
    3296. * @return {patio.sql.NumericExpression}
    3297. */
    3298. -
    3299. constructor:function (conditions, def, expression) {
    3300. -
    3301. 8 if (Expression.isConditionSpecifier(conditions)) {
    3302. -
    3303. 4 this.conditions = toArray(conditions);
    3304. -
    3305. 4 this.def = def;
    3306. -
    3307. 4 this.expression = expression;
    3308. -
    3309. 4 this.noExpression = isUndefined(expression);
    3310. -
    3311. }
    3312. -
    3313. },
    3314. +
    3315. plus:numericMethod("plus"),
    3316. /**
    3317. -
    3318. * Converts the case expression to a string
    3319. -
    3320. *
    3321. -
    3322. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3323. -
    3324. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3325. +
    3326. * @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.
    3327. *
    3328. -
    3329. * @return String the SQL case expression fragment.
    3330. -
    3331. */
    3332. -
    3333. toString:function (ds) {
    3334. -
    3335. 2 !Dataset && (Dataset = require("./dataset"));
    3336. -
    3337. 2 ds = ds || new Dataset();
    3338. -
    3339. 2 return ds.caseExpressionSql(this);
    3340. -
    3341. },
    3342. -
    3343. -
    3344. /**@ignore*/
    3345. -
    3346. getters:{
    3347. -
    3348. /**@ignore*/
    3349. -
    3350. hasExpression:function () {
    3351. -
    3352. 2 return !this.noExpression;
    3353. -
    3354. }
    3355. -
    3356. }
    3357. -
    3358. }
    3359. -
    3360. }).as(sql, "CaseExpression");
    3361. -
    3362. -
    3363. -
    3364. 1var Cast = define(GenericExpression, {
    3365. -
    3366. instance:{
    3367. -
    3368. /**@lends patio.sql.Cast*/
    3369. +
    3370. * @example
    3371. +
    3372. *
    3373. +
    3374. * sql.a.minus(sql.b) //=> "a" - "b"
    3375. +
    3376. *
    3377. +
    3378. * @return {patio.sql.NumericExpression}
    3379. +
    3380. */
    3381. +
    3382. minus:numericMethod("minus"),
    3383. /**
    3384. -
    3385. * Represents a cast of an SQL expression to a specific type.
    3386. -
    3387. * @constructs
    3388. -
    3389. * @augments patio.sql.GenericExpression
    3390. +
    3391. * @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
    3392. *
    3393. -
    3394. * @param expr the expression to CAST.
    3395. -
    3396. * @param type the type to CAST the expression to.
    3397. +
    3398. * @example
    3399. *
    3400. -
    3401. * @property expr the expression to CAST.
    3402. -
    3403. * @property type the type to CAST the expression to.
    3404. +
    3405. * sql.a.divide(sql.b) //=> "a" / "b"
    3406. +
    3407. *
    3408. +
    3409. * @return {patio.sql.NumericExpression}
    3410. */
    3411. -
    3412. constructor:function (expr, type) {
    3413. -
    3414. 3 this.expr = expr;
    3415. -
    3416. 3 this.type = type;
    3417. -
    3418. },
    3419. +
    3420. divide:numericMethod("divide"),
    3421. /**
    3422. -
    3423. * Converts the cast expression to a string
    3424. +
    3425. * @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
    3426. *
    3427. -
    3428. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3429. -
    3430. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3431. +
    3432. * @example
    3433. *
    3434. -
    3435. * @return String the SQL cast expression fragment.
    3436. +
    3437. * sql.a.multiply(sql.b) //=> "a" * "b"
    3438. +
    3439. *
    3440. +
    3441. * @return {patio.sql.NumericExpression}
    3442. */
    3443. -
    3444. toString:function (ds) {
    3445. -
    3446. 2 !Dataset && (Dataset = require("./dataset"));
    3447. -
    3448. 2 ds = ds || new Dataset();
    3449. -
    3450. 2 return ds.castSql(this.expr, this.type);
    3451. -
    3452. }
    3453. +
    3454. multiply:numericMethod("multiply")
    3455. }
    3456. -
    3457. }).as(sql, "Cast");
    3458. +
    3459. }).as(sql, "NumericMethods");
    3460. -
    3461. 1var ColumnAll = define(Expression, {
    3462. +
    3463. /**
    3464. +
    3465. * @class This mixin provides ordering methods ("asc", "desc") to expression.
    3466. +
    3467. *
    3468. +
    3469. * @example
    3470. +
    3471. *
    3472. +
    3473. * sql.name.asc(); //=> name ASC
    3474. +
    3475. * sql.price.desc(); //=> price DESC
    3476. +
    3477. * sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
    3478. +
    3479. * sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
    3480. +
    3481. *
    3482. +
    3483. * @name OrderedMethods
    3484. +
    3485. * @memberOf patio.sql
    3486. +
    3487. */
    3488. +
    3489. 1var OrderedMethods = define(null, {
    3490. instance:{
    3491. -
    3492. /**@lends patio.sql.ColumnAll.prototype*/
    3493. +
    3494. /**@lends patio.sql.OrderedMethods.prototype*/
    3495. /**
    3496. -
    3497. * Represents all columns in a given table, table.* in SQL
    3498. -
    3499. * @constructs
    3500. -
    3501. *
    3502. -
    3503. * @augments patio.sql.Expression
    3504. +
    3505. * Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
    3506. *
    3507. -
    3508. * @param table the table this expression is for.
    3509. +
    3510. * @example
    3511. +
    3512. * sql.name.asc(); //=> name ASC
    3513. +
    3514. * sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
    3515. *
    3516. -
    3517. * @property table the table this all column expression represents.
    3518. +
    3519. * @param {Object} [options] options to use when sorting
    3520. +
    3521. * @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
    3522. +
    3523. * before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
    3524. +
    3525. * @return {patio.sql.OrderedExpression}
    3526. */
    3527. -
    3528. constructor:function (table) {
    3529. -
    3530. 20 this.table = table;
    3531. +
    3532. asc:function (options) {
    3533. +
    3534. 7 return new OrderedExpression(this, false, options);
    3535. },
    3536. /**
    3537. -
    3538. * Converts the ColumnAll expression to a string
    3539. +
    3540. * Mark the receiving SQL column as sorting in a descending fashion.
    3541. +
    3542. * @example
    3543. *
    3544. -
    3545. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3546. -
    3547. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3548. +
    3549. * sql.price.desc(); //=> price DESC
    3550. +
    3551. * sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
    3552. *
    3553. -
    3554. * @return String the SQL columnAll expression fragment.
    3555. +
    3556. * @param {Object} [options] options to use when sorting
    3557. +
    3558. * @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
    3559. +
    3560. * before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
    3561. +
    3562. * @return {patio.sql.OrderedExpression}
    3563. */
    3564. -
    3565. toString:function (ds) {
    3566. -
    3567. 19 !Dataset && (Dataset = require("./dataset"));
    3568. -
    3569. 19 ds = ds || new Dataset();
    3570. -
    3571. 19 return ds.columnAllSql(this);
    3572. +
    3573. desc:function (options) {
    3574. +
    3575. 26 return new OrderedExpression(this, true, options);
    3576. }
    3577. }
    3578. -
    3579. }).as(sql, "ColumnAll");
    3580. +
    3581. }).as(sql, "OrderedMethods");
    3582. -
    3583. 1var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {
    3584. +
    3585. +
    3586. /**
    3587. +
    3588. * @class This mixin provides methods related to qualifying expression.
    3589. +
    3590. *
    3591. +
    3592. * @example
    3593. +
    3594. *
    3595. +
    3596. * sql.column.qualify("table") //=> "table"."column"
    3597. +
    3598. * sql.table.qualify("schema") //=> "schema"."table"
    3599. +
    3600. * sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
    3601. +
    3602. *
    3603. +
    3604. * @name QualifyingMethods
    3605. +
    3606. * @memberOf patio.sql
    3607. +
    3608. */
    3609. +
    3610. 1var QualifyingMethods = define(null, {
    3611. instance:{
    3612. -
    3613. /**@lends patio.sql.ComplexExpression.prototype*/
    3614. +
    3615. /**@lends patio.sql.QualifyingMethods.prototype*/
    3616. /**
    3617. -
    3618. * Represents a complex SQL expression, with a given operator and one
    3619. -
    3620. * or more attributes (which may also be ComplexExpressions, forming
    3621. -
    3622. * a tree).
    3623. -
    3624. *
    3625. -
    3626. * This is an abstract class that is not that useful by itself. The
    3627. -
    3628. * subclasses @link patio.sql.BooleanExpression},
    3629. -
    3630. * {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should
    3631. -
    3632. * be used instead of this class directly.
    3633. +
    3634. * Qualify the receiver with the given qualifier (table for column/schema for table).
    3635. *
    3636. -
    3637. * @constructs
    3638. -
    3639. * @augments patio.sql.Expression
    3640. -
    3641. * @augments patio.sql.AliasMethods
    3642. -
    3643. * @augments patio.sql.CastMethods
    3644. -
    3645. * @augments patio.sql.OrderedMethods
    3646. -
    3647. * @augments patio.sql.SubscriptMethods
    3648. +
    3649. * @example
    3650. +
    3651. * sql.column.qualify("table") //=> "table"."column"
    3652. +
    3653. * sql.table.qualify("schema") //=> "schema"."table"
    3654. +
    3655. * sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
    3656. *
    3657. -
    3658. * @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.
    3659. -
    3660. * @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.
    3661. +
    3662. * @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.
    3663. *
    3664. -
    3665. * @param {...} op The operator and arguments for this object to the ones given.
    3666. -
    3667. * <p>
    3668. -
    3669. * Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,
    3670. -
    3671. * other than the second arg for an IN/NOT IN operator.</li>
    3672. -
    3673. * </p>
    3674. +
    3675. * @return {patio.sql.QualifiedIdentifier}
    3676. */
    3677. -
    3678. constructor:function (op) {
    3679. -
    3680. 7952 if (op) {
    3681. -
    3682. 7164 var args = argsToArray(arguments,1 );
    3683. -
    3684. //make a copy of the args
    3685. -
    3686. 7164 var origArgs = args.slice(0);
    3687. -
    3688. 7164 args.forEach(function (a, i) {
    3689. -
    3690. 14676 if (Expression.isConditionSpecifier(a)) {
    3691. -
    3692. 6 args[i] = BooleanExpression.fromValuePairs(a);
    3693. -
    3694. }
    3695. -
    3696. });
    3697. -
    3698. 7164 op = op.toUpperCase();
    3699. -
    3700. -
    3701. 7164 if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
    3702. -
    3703. 1164 if (args.length < 1) {
    3704. -
    3705. 0 throw new ExpressionError("The " + op + " operator requires at least 1 argument")
    3706. -
    3707. }
    3708. -
    3709. 1164 var oldArgs = args.slice(0);
    3710. -
    3711. 1164 args = [];
    3712. -
    3713. 1164 oldArgs.forEach(function (a) {
    3714. -
    3715. 2739 a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
    3716. -
    3717. });
    3718. -
    3719. -
    3720. 6000 } else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
    3721. -
    3722. 5937 if (args.length != 2) {
    3723. -
    3724. 0 throw new ExpressionError("The " + op + " operator requires precisely 2 arguments");
    3725. -
    3726. }
    3727. -
    3728. //With IN/NOT IN, even if the second argument is an array of two element arrays,
    3729. -
    3730. //don't convert it into a boolean expression, since it's definitely being used
    3731. -
    3732. //as a value list.
    3733. -
    3734. 5937 if (IN_OPERATORS[op]) {
    3735. -
    3736. 23 args[1] = origArgs[1]
    3737. -
    3738. }
    3739. -
    3740. 63 } else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
    3741. -
    3742. 63 if (args.length != 1) {
    3743. -
    3744. 0 throw new ExpressionError("The " + op + " operator requires only one argument");
    3745. -
    3746. }
    3747. -
    3748. } else {
    3749. -
    3750. 0 throw new ExpressionError("Invalid operator " + op);
    3751. -
    3752. }
    3753. -
    3754. 7164 this.op = op;
    3755. -
    3756. 7164 this.args = args;
    3757. -
    3758. }
    3759. +
    3760. qualify:function (qualifier) {
    3761. +
    3762. 576 return new QualifiedIdentifier(qualifier, this);
    3763. },
    3764. /**
    3765. -
    3766. * Converts the ComplexExpression to a string.
    3767. +
    3768. * Use to create a .* expression.
    3769. *
    3770. -
    3771. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    3772. -
    3773. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    3774. +
    3775. * @example
    3776. +
    3777. * sql.table.all() //=> "table".*
    3778. +
    3779. * sql.table.qualify("schema").all() //=> "schema"."table".*
    3780. *
    3781. -
    3782. * @return String the SQL version of the {@link patio.sql.ComplexExpression}.
    3783. +
    3784. *
    3785. +
    3786. * @return {patio.sql.ColumnAll}
    3787. */
    3788. -
    3789. toString:function (ds) {
    3790. -
    3791. 6761 !Dataset && (Dataset = require("./dataset"));
    3792. -
    3793. 6761 ds = ds || new Dataset();
    3794. -
    3795. 6761 return ds.complexExpressionSql(this.op, this.args);
    3796. +
    3797. all:function () {
    3798. +
    3799. 3 return new ColumnAll(this);
    3800. }
    3801. -
    3802. },
    3803. -
    3804. static:{
    3805. -
    3806. /**@lends patio.sql.ComplexExpression*/
    3807. -
    3808. /**
    3809. -
    3810. * Hash of operator inversions
    3811. -
    3812. * @type Object
    3813. -
    3814. * @default {
    3815. -
    3816. * AND:"OR",
    3817. -
    3818. * OR:"AND",
    3819. -
    3820. * GT:"lte",
    3821. -
    3822. * GTE:"lt",
    3823. -
    3824. * LT:"gte",
    3825. -
    3826. * LTE:"gt",
    3827. -
    3828. * EQ:"neq",
    3829. -
    3830. * NEQ:"eq",
    3831. -
    3832. * LIKE:'NOT LIKE',
    3833. -
    3834. * "NOT LIKE":"LIKE",
    3835. -
    3836. * '!~*':'~*',
    3837. -
    3838. * '~*':'!~*',
    3839. -
    3840. * "~":'!~',
    3841. -
    3842. * "IN":'NOTIN',
    3843. -
    3844. * "NOTIN":"IN",
    3845. -
    3846. * "IS":'IS NOT',
    3847. -
    3848. * "ISNOT":"IS",
    3849. -
    3850. * NOT:"NOOP",
    3851. -
    3852. * NOOP:"NOT",
    3853. -
    3854. * ILIKE:'NOT ILIKE',
    3855. -
    3856. * NOTILIKE:"ILIKE"
    3857. -
    3858. * }
    3859. -
    3860. */
    3861. -
    3862. OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,
    3863. +
    3864. }
    3865. +
    3866. }).as(sql, "QualifyingMethods");
    3867. +
    3868. +
    3869. +
    3870. /**
    3871. +
    3872. * @class This mixin provides SQL string methods such as (like and iLike).
    3873. +
    3874. *
    3875. +
    3876. * @example
    3877. +
    3878. *
    3879. +
    3880. * sql.a.like("A%"); //=> "a" LIKE 'A%'
    3881. +
    3882. * sql.a.iLike("A%"); //=> "a" LIKE 'A%'
    3883. +
    3884. * sql.a.like(/^a/); //=> "a" ~* '^a'
    3885. +
    3886. *
    3887. +
    3888. * @name StringMethods
    3889. +
    3890. * @memberOf patio.sql
    3891. +
    3892. */
    3893. +
    3894. 1var StringMethods = define(null, {
    3895. +
    3896. instance:{
    3897. +
    3898. /**@lends patio.sql.StringMethods.prototype*/
    3899. /**
    3900. -
    3901. * Default mathematical operators.
    3902. +
    3903. * Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver
    3904. +
    3905. * with the given patterns. See {@link patio.sql.StringExpression#like}.
    3906. +
    3907. *
    3908. +
    3909. * @example
    3910. +
    3911. * sql.a.iLike("A%"); //=> "a" LIKE 'A%'
    3912. *
    3913. -
    3914. * @type Object
    3915. -
    3916. * @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}
    3917. +
    3918. * @return {patio.sql.BooleanExpression}
    3919. */
    3920. -
    3921. MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,
    3922. +
    3923. ilike:function (expression) {
    3924. +
    3925. 310 expression = argsToArray(arguments);
    3926. +
    3927. 310 return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([
    3928. +
    3929. {caseInsensitive:true}
    3930. +
    3931. ]));
    3932. +
    3933. },
    3934. /**
    3935. -
    3936. * Default bitwise operators.
    3937. +
    3938. * Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with
    3939. +
    3940. * the given patterns. See {@link patio.sql.StringExpression#like}.
    3941. *
    3942. -
    3943. * @type Object
    3944. -
    3945. * @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}
    3946. -
    3947. */
    3948. -
    3949. BITWISE_OPERATORS:BITWISE_OPERATORS,
    3950. -
    3951. /**
    3952. -
    3953. * Default inequality operators.
    3954. +
    3955. * @example
    3956. +
    3957. * sql.a.like(/^a/); //=> "a" ~* '^a'
    3958. +
    3959. * sql.a.like("A%"); //=> "a" LIKE 'A%'
    3960. *
    3961. -
    3962. * @type Object
    3963. -
    3964. * @default {GT:">",GTE:">=",LT:"<",LTE:"<="}
    3965. +
    3966. * @param expression
    3967. */
    3968. -
    3969. INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,
    3970. +
    3971. like:function (expression) {
    3972. +
    3973. 11 expression = argsToArray(arguments);
    3974. +
    3975. 11 return StringExpression.like.apply(StringExpression, [this].concat(expression));
    3976. +
    3977. }
    3978. +
    3979. }
    3980. +
    3981. }).as(sql, "StringMethods");
    3982. +
    3983. +
    3984. /**
    3985. +
    3986. * @class This mixin provides string concatenation methods ("concat");
    3987. +
    3988. *
    3989. +
    3990. * @example
    3991. +
    3992. *
    3993. +
    3994. * sql.x.sqlString.concat("y"); //=> "x" || "y"
    3995. +
    3996. *
    3997. +
    3998. * @name StringConcatenationMethods
    3999. +
    4000. * @memberOf patio.sql
    4001. +
    4002. */
    4003. +
    4004. 1var StringConcatenationMethods = define(null, {
    4005. +
    4006. instance:{
    4007. +
    4008. /**@lends patio.sql.StringConcatenationMethods.prototype*/
    4009. /**
    4010. -
    4011. * Default boolean operators.
    4012. +
    4013. * Return a {@link patio.sql.StringExpression} representing the concatenation of this expression
    4014. +
    4015. * with the given argument.
    4016. *
    4017. -
    4018. * @type Object
    4019. -
    4020. * @default {AND:"AND",OR:"OR"}
    4021. +
    4022. * @example
    4023. +
    4024. *
    4025. +
    4026. * sql.x.sqlString.concat("y"); //=> "x" || "y"
    4027. +
    4028. *
    4029. +
    4030. * @param expression expression to concatenate this expression with.
    4031. */
    4032. -
    4033. BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,
    4034. +
    4035. concat:function (expression) {
    4036. +
    4037. 0 return new StringExpression("||", this, expression);
    4038. +
    4039. }
    4040. +
    4041. }
    4042. +
    4043. }).as(sql, "StringConcatenationMethods");
    4044. +
    4045. +
    4046. /**
    4047. +
    4048. * @class This mixin provides the ability to access elements within a SQL array.
    4049. +
    4050. *
    4051. +
    4052. * @example
    4053. +
    4054. * sql.array.sqlSubscript(1) //=> array[1]
    4055. +
    4056. * sql.array.sqlSubscript(1, 2) //=> array[1, 2]
    4057. +
    4058. * sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
    4059. +
    4060. *
    4061. +
    4062. * @name SubscriptMethods
    4063. +
    4064. * @memberOf patio.sql
    4065. +
    4066. */
    4067. +
    4068. 1var SubscriptMethods = define(null, {
    4069. +
    4070. instance:{
    4071. /**
    4072. -
    4073. * Default IN operators.
    4074. +
    4075. * Return a {@link patio.sql.Subscript} with the given arguments, representing an
    4076. +
    4077. * SQL array access.
    4078. *
    4079. -
    4080. * @type Object
    4081. -
    4082. * @default {IN:"IN",NOTIN:'NOT IN'}
    4083. -
    4084. */
    4085. -
    4086. IN_OPERATORS:IN_OPERATORS,
    4087. -
    4088. /**
    4089. -
    4090. * Default IS operators.
    4091. +
    4092. * @example
    4093. +
    4094. * sql.array.sqlSubscript(1) //=> array[1]
    4095. +
    4096. * sql.array.sqlSubscript(1, 2) //=> array[1, 2]
    4097. +
    4098. * sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
    4099. *
    4100. -
    4101. * @type Object
    4102. -
    4103. * @default {IS:"IS", ISNOT:'IS NOT'}
    4104. +
    4105. * @param subscript
    4106. */
    4107. -
    4108. IS_OPERATORS:IS_OPERATORS,
    4109. +
    4110. sqlSubscript:function (subscript) {
    4111. +
    4112. 108 var args = argsToArray(arguments);
    4113. +
    4114. 108 return new SubScript(this, flatten(args));
    4115. +
    4116. }
    4117. +
    4118. }
    4119. +
    4120. }).as(sql, "SubScriptMethods");
    4121. +
    4122. +
    4123. +
    4124. /**
    4125. +
    4126. * @class This is the parent of all expressions.
    4127. +
    4128. *
    4129. +
    4130. * @name Expression
    4131. +
    4132. * @memberOf patio.sql
    4133. +
    4134. */
    4135. +
    4136. 1var Expression = define(null, {
    4137. +
    4138. +
    4139. instance:{
    4140. +
    4141. /**@lends patio.sql.Expression.prototype*/
    4142. +
    4143. /**
    4144. -
    4145. * Default two arity operators.
    4146. +
    4147. * Returns the string representation of this expression
    4148. *
    4149. -
    4150. * @type Object
    4151. -
    4152. * @default {
    4153. -
    4154. * EQ:'=',
    4155. -
    4156. * NEQ:'!=', LIKE:"LIKE",
    4157. -
    4158. * "NOT LIKE":'NOT LIKE',
    4159. -
    4160. * ILIKE:"ILIKE",
    4161. -
    4162. * "NOT ILIKE":'NOT ILIKE',
    4163. -
    4164. * "~":"~",
    4165. -
    4166. * '!~':"!~",
    4167. -
    4168. * '~*':"~*",
    4169. -
    4170. * '!~*':"!~*",
    4171. -
    4172. * GT:">",
    4173. -
    4174. * GTE:">=",
    4175. -
    4176. * LT:"<",
    4177. -
    4178. * LTE:"<=",
    4179. -
    4180. * bitWiseAnd:"&",
    4181. -
    4182. * bitWiseOr:"|",
    4183. -
    4184. * exclusiveOr:"^",
    4185. -
    4186. * leftShift:"<<",
    4187. -
    4188. * rightShift:">>",
    4189. -
    4190. * IS:"IS",
    4191. -
    4192. * ISNOT:'IS NOT',
    4193. -
    4194. * IN:"IN",
    4195. -
    4196. * NOTIN:'NOT IN'
    4197. -
    4198. * }
    4199. +
    4200. * @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.
    4201. +
    4202. * @return {String} a string literal version of this expression.
    4203. */
    4204. -
    4205. TWO_ARITY_OPERATORS:TWO_ARITY_OPERATORS,
    4206. +
    4207. sqlLiteral:function (ds) {
    4208. +
    4209. 0 return this.toString(ds);
    4210. +
    4211. }
    4212. +
    4213. +
    4214. },
    4215. +
    4216. +
    4217. static:{
    4218. +
    4219. /**@lends patio.sql.Expression*/
    4220. /**
    4221. -
    4222. * Default N(multi) arity operators.
    4223. +
    4224. * This is a helper method that will take in an array of arguments and return an expression.
    4225. *
    4226. -
    4227. * @type Object
    4228. -
    4229. * @default {
    4230. -
    4231. * "||":"||",
    4232. -
    4233. * AND:"AND",
    4234. -
    4235. * OR:"OR",
    4236. -
    4237. * PLUS:"+",
    4238. -
    4239. * MINUS:"-",
    4240. -
    4241. * DIVIDE:"/", MULTIPLY:"*"
    4242. -
    4243. * }
    4244. +
    4245. * @example
    4246. +
    4247. *
    4248. +
    4249. * QualifiedIdentifier.fromArgs(["table", "column"]);
    4250. +
    4251. *
    4252. +
    4253. * @param {*[]} args array of arguments to pass into the constructor of the function.
    4254. +
    4255. *
    4256. +
    4257. * @return {patio.sql.Expression} an expression.
    4258. */
    4259. -
    4260. N_ARITY_OPERATORS:N_ARITY_OPERATORS,
    4261. +
    4262. fromArgs:function (args) {
    4263. +
    4264. 2216 var ret;
    4265. +
    4266. 2216 try {
    4267. +
    4268. 2216 ret = new this();
    4269. +
    4270. } catch (ignore) {
    4271. +
    4272. }
    4273. +
    4274. 2216 this.apply(ret, args);
    4275. +
    4276. 2216 return ret;
    4277. +
    4278. },
    4279. /**
    4280. -
    4281. * Default ONE operators.
    4282. +
    4283. * Helper to determine if something is a condition specifier. Returns true if the object
    4284. +
    4285. * is a Hash or is an array of two element arrays.
    4286. *
    4287. -
    4288. * @type Object
    4289. -
    4290. * @default {
    4291. -
    4292. * "NOT":"NOT",
    4293. -
    4294. * "NOOP":"NOOP"
    4295. -
    4296. * }
    4297. +
    4298. * @example
    4299. +
    4300. * Expression.isConditionSpecifier({a : "b"}); //=> true
    4301. +
    4302. * Expression.isConditionSpecifier("a"); //=> false
    4303. +
    4304. * Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true
    4305. +
    4306. * Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false
    4307. +
    4308. *
    4309. +
    4310. * @param {*} obj object to test if it is a condition specifier
    4311. +
    4312. * @return {Boolean} true if the object is a Hash or is an array of two element arrays.
    4313. */
    4314. -
    4315. ONE_ARITY_OPERATORS:ONE_ARITY_OPERATORS
    4316. +
    4317. isConditionSpecifier:function (obj) {
    4318. +
    4319. 22715 return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
    4320. +
    4321. 9896 return isArray(i) && i.length === 2;
    4322. +
    4323. }));
    4324. +
    4325. }
    4326. }
    4327. -
    4328. }).as(sql, "ComplexExpression");
    4329. +
    4330. +
    4331. }).as(sql, "Expression");
    4332. +
    4333. +
    4334. /**
    4335. +
    4336. * @class Base class for all GenericExpressions
    4337. +
    4338. *
    4339. +
    4340. * @augments patio.sql.Expression
    4341. +
    4342. * @augments patio.sql.AliasMethods
    4343. +
    4344. * @augments patio.sql.BooleanMethods
    4345. +
    4346. * @augments patio.sql.CastMethods
    4347. +
    4348. * @augments patio.sql.ComplexExpressionMethods
    4349. +
    4350. * @augments patio.sql.InequalityMethods
    4351. +
    4352. * @augments patio.sql.NumericMethods
    4353. +
    4354. * @augments patio.sql.OrderedMethods
    4355. +
    4356. * @augments patio.sql.StringMethods
    4357. +
    4358. * @augments patio.sql.SubscriptMethods
    4359. +
    4360. *
    4361. +
    4362. * @name GenericExpression
    4363. +
    4364. * @memberOf patio.sql
    4365. +
    4366. */
    4367. +
    4368. 1var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");
    4369. +
    4370. +
    4371. +
    4372. 1var AliasedExpression = define(Expression, {
    4373. +
    4374. instance:{
    4375. +
    4376. /**@lends patio.sql.AliasedExpression.prototype*/
    4377. +
    4378. +
    4379. /**
    4380. +
    4381. * This class reperesents an Aliased Expression
    4382. +
    4383. *
    4384. +
    4385. * @constructs
    4386. +
    4387. * @augments patio.sql.Expression
    4388. +
    4389. *
    4390. +
    4391. * @param expression the expression to alias.
    4392. +
    4393. * @param alias the alias to alias the expression to.
    4394. +
    4395. *
    4396. +
    4397. * @property expression the expression being aliased
    4398. +
    4399. * @property alias the alias of the expression
    4400. +
    4401. *
    4402. +
    4403. */
    4404. +
    4405. constructor:function (expression, alias) {
    4406. +
    4407. 945 this.expression = expression;
    4408. +
    4409. 945 this.alias = alias;
    4410. +
    4411. },
    4412. +
    4413. +
    4414. /**
    4415. +
    4416. * Converts the aliased expression to a string
    4417. +
    4418. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    4419. +
    4420. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    4421. +
    4422. *
    4423. +
    4424. * @return String the SQL alias fragment.
    4425. +
    4426. */
    4427. +
    4428. toString:function (ds) {
    4429. +
    4430. 877 !Dataset && (Dataset = require("./dataset"));
    4431. +
    4432. 877 ds = ds || new Dataset();
    4433. +
    4434. 877 return ds.aliasedExpressionSql(this);
    4435. +
    4436. }
    4437. +
    4438. }
    4439. +
    4440. }
    4441. +
    4442. ).as(sql, "AliasedExpression");
    4443. -
    4444. /**
    4445. -
    4446. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    4447. -
    4448. * in a boolean value in SQL.
    4449. -
    4450. *
    4451. -
    4452. * @augments patio.sql.ComplexExpression
    4453. -
    4454. * @augments patio.sql.BooleanMethods
    4455. -
    4456. * @name BooleanExpression
    4457. -
    4458. * @memberOf patio.sql
    4459. -
    4460. */
    4461. -
    4462. 1var BooleanExpression = define([ComplexExpression, BooleanMethods], {
    4463. -
    4464. static:{
    4465. -
    4466. /**@lends patio.sql.BooleanExpression*/
    4467. +
    4468. 1var CaseExpression = define(GenericExpression, {
    4469. +
    4470. instance:{
    4471. +
    4472. /**@lends patio.sql.CaseExpression.prototype*/
    4473. /**
    4474. -
    4475. * Invert the expression, if possible. If the expression cannot
    4476. -
    4477. * be inverted, it throws an {@link patio.error.ExpressionError}. An inverted expression should match
    4478. -
    4479. * everything that the uninverted expression did not match, and vice-versa, except for possible issues with
    4480. -
    4481. * SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
    4482. -
    4483. *
    4484. -
    4485. * @example
    4486. -
    4487. * BooleanExpression.invert(sql.a) //=> NOT "a"
    4488. +
    4489. * Create an object with the given conditions and
    4490. +
    4491. * default value. An expression can be provided to
    4492. +
    4493. * test each condition against, instead of having
    4494. +
    4495. * all conditions represent their own boolean expression.
    4496. *
    4497. -
    4498. * @param {patio.sql.BooleanExpression} expression
    4499. -
    4500. * the expression to invert.
    4501. +
    4502. * @constructs
    4503. +
    4504. * @augments patio.sql.GenericExpression
    4505. +
    4506. * @param {Array|Object} conditions conditions to create the case expression from
    4507. +
    4508. * @param def default value
    4509. +
    4510. * @param expression expression to create the CASE expression from
    4511. *
    4512. -
    4513. * @return {patio.sql.BooleanExpression} the inverted expression.
    4514. +
    4515. * @property {Boolean} hasExpression returns true if this case expression has a expression
    4516. +
    4517. * @property conditions the conditions of the {@link patio.sql.CaseExpression}.
    4518. +
    4519. * @property def the default value of the {@link patio.sql.CaseExpression}.
    4520. +
    4521. * @property expression the expression of the {@link patio.sql.CaseExpression}.
    4522. +
    4523. * @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.
    4524. */
    4525. -
    4526. invert:function (expression) {
    4527. -
    4528. 95 if (isInstanceOf(expression, BooleanExpression)) {
    4529. -
    4530. 90 var op = expression.op, newArgs;
    4531. -
    4532. 90 if (op == "AND" || op == "OR") {
    4533. -
    4534. 3 newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args.map(function (arg) {
    4535. -
    4536. 6 return BooleanExpression.invert(arg);
    4537. -
    4538. }));
    4539. -
    4540. 3 return BooleanExpression.fromArgs(newArgs);
    4541. -
    4542. } else {
    4543. -
    4544. 87 newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);
    4545. -
    4546. 87 return BooleanExpression.fromArgs(newArgs);
    4547. -
    4548. }
    4549. -
    4550. 5 } else if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
    4551. -
    4552. 0 throw new ExpressionError(format("Cannot invert %4j", [expression]));
    4553. -
    4554. } else {
    4555. -
    4556. 5 return new BooleanExpression("NOT", expression);
    4557. +
    4558. constructor:function (conditions, def, expression) {
    4559. +
    4560. 8 if (Expression.isConditionSpecifier(conditions)) {
    4561. +
    4562. 4 this.conditions = toArray(conditions);
    4563. +
    4564. 4 this.def = def;
    4565. +
    4566. 4 this.expression = expression;
    4567. +
    4568. 4 this.noExpression = isUndefined(expression);
    4569. }
    4570. },
    4571. +
    4572. /**
    4573. -
    4574. * Take pairs of values (e.g. a hash or array of two element arrays)
    4575. -
    4576. * and converts it to a {@link patio.sql.BooleanExpression}. The operator and args
    4577. -
    4578. * used depends on the case of the right (2nd) argument:
    4579. +
    4580. * Converts the case expression to a string
    4581. *
    4582. -
    4583. * <pre class='code'>
    4584. -
    4585. * BooleanExpression.fromValuePairs({a : [1,2,3]}) //=> a IN (1,2,3)
    4586. -
    4587. * BooleanExpression.fromValuePairs({a : true}); // a IS TRUE;
    4588. -
    4589. * BooleanExpression.fromValuePairs({a : /^A/i}); // a *~ '^A'
    4590. -
    4591. * </pre>
    4592. +
    4593. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    4594. +
    4595. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    4596. *
    4597. -
    4598. * If multiple arguments are given, they are joined with the op given (AND
    4599. -
    4600. * by default, OR possible). If negate is set to true,
    4601. -
    4602. * all subexpressions are inverted before used.
    4603. -
    4604. * <pre class='code'>
    4605. -
    4606. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}) //=> a IN (1,2,3) AND b IS TRUE
    4607. -
    4608. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
    4609. -
    4610. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
    4611. -
    4612. * </pre>
    4613. -
    4614. * @param {Object} a object to convert to a {@link patio.sql.BooleanExpression}
    4615. -
    4616. * @param {String} [op="AND"] Boolean operator to join each subexpression with.
    4617. -
    4618. * <pre class="code">
    4619. -
    4620. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
    4621. -
    4622. * </pre>
    4623. -
    4624. * @param {Boolean} [negate=false] set to try to invert the {@link patio.sql.BooleanExpression}.
    4625. -
    4626. * <pre class="code">
    4627. -
    4628. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
    4629. -
    4630. * </pre>
    4631. -
    4632. * @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
    4633. +
    4634. * @return String the SQL case expression fragment.
    4635. */
    4636. -
    4637. fromValuePairs:function (a, op, negate) {
    4638. -
    4639. 7610 !Dataset && (Dataset = require("./dataset"));
    4640. -
    4641. 7610 op = op || "AND", negate = negate || false;
    4642. -
    4643. 7610 var pairArr = [];
    4644. -
    4645. 7610 var isArr = isArray(a) && Expression.isConditionSpecifier(a);
    4646. -
    4647. 7610 if (isHash(a)) {
    4648. -
    4649. 3385 pairArr.push(this.__filterObject(a));
    4650. -
    4651. } else {
    4652. -
    4653. 4225 for (var k in a) {
    4654. -
    4655. 5203 var v = isArr ? a[k][1] : a[k], ret;
    4656. -
    4657. 5203 k = isArr ? a[k][0] : k;
    4658. -
    4659. 5203 if (isArray(v) || isInstanceOf(v, Dataset)) {
    4660. -
    4661. 17 k = isArray(k) ? k.map(function (i) {
    4662. -
    4663. 12 return isString(i) ? sql.stringToIdentifier(i) : i
    4664. -
    4665. }) : isString(k) ? sql.stringToIdentifier(k) : k;
    4666. -
    4667. 17 ret = new BooleanExpression("IN", k, v);
    4668. -
    4669. 5186 } else if (isInstanceOf(v, NegativeBooleanConstant)) {
    4670. -
    4671. 0 ret = new BooleanExpression("ISNOT", k, v.constant);
    4672. -
    4673. 5186 } else if (isInstanceOf(v, BooleanConstant)) {
    4674. -
    4675. 0 ret = new BooleanExpression("IS", k, v.constant);
    4676. -
    4677. 5186 } else if (isNull(v) || isBoolean(v)) {
    4678. -
    4679. 240 ret = new BooleanExpression("IS", k, v);
    4680. -
    4681. 4946 } else if (isHash(v)) {
    4682. -
    4683. 0 ret = BooleanExpression.__filterObject(v, k);
    4684. -
    4685. 4946 } else if (isRegExp(v)) {
    4686. -
    4687. 69 ret = StringExpression.like(sql.stringToIdentifier(k), v);
    4688. -
    4689. } else {
    4690. -
    4691. 4877 ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
    4692. -
    4693. }
    4694. -
    4695. 5203 negate && (ret = BooleanExpression.invert(ret));
    4696. -
    4697. 5203 pairArr.push(ret);
    4698. -
    4699. }
    4700. -
    4701. }
    4702. -
    4703. //if We just have one then return the first otherwise create a new Boolean expression
    4704. -
    4705. 7610 return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
    4706. +
    4707. toString:function (ds) {
    4708. +
    4709. 2 !Dataset && (Dataset = require("./dataset"));
    4710. +
    4711. 2 ds = ds || new Dataset();
    4712. +
    4713. 2 return ds.caseExpressionSql(this);
    4714. },
    4715. +
    4716. /**@ignore*/
    4717. +
    4718. getters:{
    4719. +
    4720. /**@ignore*/
    4721. +
    4722. hasExpression:function () {
    4723. +
    4724. 2 return !this.noExpression;
    4725. +
    4726. }
    4727. +
    4728. }
    4729. +
    4730. }
    4731. +
    4732. }).as(sql, "CaseExpression");
    4733. +
    4734. +
    4735. +
    4736. 1var Cast = define(GenericExpression, {
    4737. +
    4738. instance:{
    4739. +
    4740. /**@lends patio.sql.Cast*/
    4741. +
    4742. /**
    4743. -
    4744. * @private
    4745. -
    4746. *
    4747. -
    4748. * This builds an expression from a hash
    4749. +
    4750. * Represents a cast of an SQL expression to a specific type.
    4751. +
    4752. * @constructs
    4753. +
    4754. * @augments patio.sql.GenericExpression
    4755. *
    4756. -
    4757. * @example
    4758. +
    4759. * @param expr the expression to CAST.
    4760. +
    4761. * @param type the type to CAST the expression to.
    4762. *
    4763. -
    4764. * Dataset._filterObject({a : 1}) //=> WHERE (a = 1)
    4765. -
    4766. * Dataset._filterObject({x : {gt : 1}}) //=> WHERE (x > 1)
    4767. -
    4768. * Dataset._filterObject({x : {gt : 1}, a : 1}) //=> WHERE ((x > 1) AND (a = 1))
    4769. -
    4770. * Dataset._filterObject({x : {like : "name"}}) //=> WHERE (x LIKE 'name')
    4771. -
    4772. * Dataset._filterObject({x : {iLike : "name"}}) //=> WHERE (x LIKE 'name')
    4773. -
    4774. * Dataset._filterObject({x : {between : [1,10]}}) //=> WHERE ((x >= 1) AND (x <= 10))
    4775. -
    4776. * Dataset._filterObject({x : {notBetween : [1,10]}}) //=> WHERE ((x < 1) OR (x > 10))
    4777. -
    4778. * Dataset._filterObject({x : {neq : 1}}) //=> WHERE (x != 1)
    4779. +
    4780. * @property expr the expression to CAST.
    4781. +
    4782. * @property type the type to CAST the expression to.
    4783. +
    4784. */
    4785. +
    4786. constructor:function (expr, type) {
    4787. +
    4788. 3 this.expr = expr;
    4789. +
    4790. 3 this.type = type;
    4791. +
    4792. },
    4793. +
    4794. +
    4795. /**
    4796. +
    4797. * Converts the cast expression to a string
    4798. *
    4799. -
    4800. * @param {Object} expr the expression we need to create an expression out of
    4801. -
    4802. * @param {String} [key=null] the key that the hash corresponds to
    4803. +
    4804. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    4805. +
    4806. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    4807. *
    4808. -
    4809. * @return {patio.sql.Expression} an expression to use in the filter
    4810. +
    4811. * @return String the SQL cast expression fragment.
    4812. */
    4813. -
    4814. __filterObject:function (expr, key) {
    4815. -
    4816. 3502 var pairs = [], opts, newKey;
    4817. -
    4818. 3502 var twoArityOperators = this.TWO_ARITY_OPERATORS;
    4819. -
    4820. 3502 for (var k in expr) {
    4821. -
    4822. 3541 var v = expr[k];
    4823. -
    4824. 3541 if (isHash(v)) { //its a hash too filter it too!
    4825. -
    4826. 115 pairs.push(this.__filterObject(v, k));
    4827. -
    4828. 3426 } else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
    4829. -
    4830. //its a two arrity operator (e.g. '=', '>')
    4831. -
    4832. 118 newKey = isString(key) ? key.split(",") : [key];
    4833. -
    4834. 118 if (newKey.length > 1) {
    4835. -
    4836. //this represents a hash where the key represents two columns
    4837. -
    4838. //(e.g. {"col1,col2" : 1}) => WHERE (col1 = 1 AND col2 = 1)
    4839. -
    4840. 1 pairs = pairs.concat(newKey.map(function (k) {
    4841. -
    4842. //filter each column with the expression
    4843. -
    4844. 2 return this.__filterObject(expr, k);
    4845. -
    4846. }, this));
    4847. -
    4848. } else {
    4849. -
    4850. 117 newKey = [sql.stringToIdentifier(newKey[0])];
    4851. -
    4852. 117 if (k.match(/^like$/)) {
    4853. -
    4854. //its a like clause {col : {like : "hello"}}
    4855. -
    4856. -
    4857. 3 pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));
    4858. -
    4859. 114 } else if (k.match(/^iLike$/)) {
    4860. -
    4861. //its a like clause {col : {iLike : "hello"}}
    4862. -
    4863. 2 pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]).concat({caseInsensitive:true}))));
    4864. -
    4865. 112 } else if (k.match(/between/i)) {
    4866. -
    4867. //its a like clause {col : {between : [1,10]}}
    4868. -
    4869. 6 var between = sql.stringToIdentifier(newKey[0]).between(v);
    4870. -
    4871. 6 k == "notBetween" && (between = between.not());
    4872. -
    4873. 6 pairs.push(between);
    4874. -
    4875. } else {
    4876. -
    4877. //otherwise is just a boolean expressio
    4878. -
    4879. //it its not a valid operator then we
    4880. -
    4881. //BooleanExpression with throw an error
    4882. -
    4883. 106 pairs.push(new BooleanExpression(k, newKey[0], v));
    4884. -
    4885. }
    4886. -
    4887. }
    4888. -
    4889. } else {
    4890. -
    4891. //we're not a twoarity operator
    4892. -
    4893. //so we create a boolean expression out of it
    4894. -
    4895. 3308 newKey = k.split(",");
    4896. -
    4897. 3308 if (newKey.length == 1) {
    4898. -
    4899. 3302 newKey = sql.stringToIdentifier(newKey[0]);
    4900. -
    4901. }
    4902. -
    4903. 3308 opts = [
    4904. -
    4905. [newKey, v]
    4906. -
    4907. ];
    4908. -
    4909. 3308 pairs.push(BooleanExpression.fromValuePairs(opts));
    4910. -
    4911. }
    4912. -
    4913. }
    4914. -
    4915. //if the total of pairs is one then we just return the first element
    4916. -
    4917. //otherwise we join them all with an AND
    4918. -
    4919. 3502 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
    4920. +
    4921. toString:function (ds) {
    4922. +
    4923. 2 !Dataset && (Dataset = require("./dataset"));
    4924. +
    4925. 2 ds = ds || new Dataset();
    4926. +
    4927. 2 return ds.castSql(this.expr, this.type);
    4928. }
    4929. }
    4930. -
    4931. }).as(sql, "BooleanExpression");
    4932. +
    4933. }).as(sql, "Cast");
    4934. -
    4935. 1var Constant = define(GenericExpression, {
    4936. +
    4937. 1var ColumnAll = define(Expression, {
    4938. instance:{
    4939. -
    4940. /**@lends patio.sql.Constant.prototype*/
    4941. +
    4942. /**@lends patio.sql.ColumnAll.prototype*/
    4943. +
    4944. /**
    4945. -
    4946. * Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.
    4947. -
    4948. *
    4949. +
    4950. * Represents all columns in a given table, table.* in SQL
    4951. * @constructs
    4952. -
    4953. * @augments patio.sql.GenericExpression
    4954. -
    4955. * @property {String} constant <b>READ ONLY</b> the contant.
    4956. +
    4957. *
    4958. +
    4959. * @augments patio.sql.Expression
    4960. +
    4961. *
    4962. +
    4963. * @param table the table this expression is for.
    4964. +
    4965. *
    4966. +
    4967. * @property table the table this all column expression represents.
    4968. */
    4969. -
    4970. constructor:function (constant) {
    4971. -
    4972. 18 this.__constant = constant;
    4973. +
    4974. constructor:function (table) {
    4975. +
    4976. 20 this.table = table;
    4977. },
    4978. /**
    4979. -
    4980. * Converts the {@link patio.sql.Constant} to a string.
    4981. +
    4982. * Converts the ColumnAll expression to a string
    4983. *
    4984. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    4985. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    4986. *
    4987. -
    4988. * @return String the SQL version of the {@link patio.sql.Constant}.
    4989. +
    4990. * @return String the SQL columnAll expression fragment.
    4991. */
    4992. toString:function (ds) {
    4993. -
    4994. 6 !Dataset && (Dataset = require("./dataset"));
    4995. -
    4996. 6 ds = ds || new Dataset();
    4997. -
    4998. 6 return ds.constantSql(this.__constant);
    4999. -
    5000. },
    5001. -
    5002. -
    5003. getters:{
    5004. -
    5005. constant:function () {
    5006. -
    5007. 0 return this.__constant;
    5008. -
    5009. }
    5010. +
    5011. 19 !Dataset && (Dataset = require("./dataset"));
    5012. +
    5013. 19 ds = ds || new Dataset();
    5014. +
    5015. 19 return ds.columnAllSql(this);
    5016. }
    5017. }
    5018. -
    5019. }).as(sql, "Constant");
    5020. +
    5021. }).as(sql, "ColumnAll");
    5022. -
    5023. /**
    5024. -
    5025. * @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
    5026. -
    5027. * @auments patio.sql.Constant
    5028. -
    5029. * @name BooleanConstant
    5030. -
    5031. * @memberOf patio.sql
    5032. -
    5033. */
    5034. -
    5035. 1var BooleanConstant = define(Constant, {
    5036. +
    5037. 1var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {
    5038. instance:{
    5039. -
    5040. /**@lends patio.sql.BooleanConstant.prototype*/
    5041. +
    5042. /**@lends patio.sql.ComplexExpression.prototype*/
    5043. /**
    5044. -
    5045. * Converts the {@link patio.sql.BooleanConstant} to a string.
    5046. +
    5047. * Represents a complex SQL expression, with a given operator and one
    5048. +
    5049. * or more attributes (which may also be ComplexExpressions, forming
    5050. +
    5051. * a tree).
    5052. *
    5053. -
    5054. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5055. -
    5056. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5057. +
    5058. * This is an abstract class that is not that useful by itself. The
    5059. +
    5060. * subclasses @link patio.sql.BooleanExpression},
    5061. +
    5062. * {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should
    5063. +
    5064. * be used instead of this class directly.
    5065. *
    5066. -
    5067. * @return String the SQL version of the {@link patio.sql.BooleanConstant}.
    5068. +
    5069. * @constructs
    5070. +
    5071. * @augments patio.sql.Expression
    5072. +
    5073. * @augments patio.sql.AliasMethods
    5074. +
    5075. * @augments patio.sql.CastMethods
    5076. +
    5077. * @augments patio.sql.OrderedMethods
    5078. +
    5079. * @augments patio.sql.SubscriptMethods
    5080. +
    5081. *
    5082. +
    5083. * @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.
    5084. +
    5085. * @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.
    5086. +
    5087. *
    5088. +
    5089. * @param {...} op The operator and arguments for this object to the ones given.
    5090. +
    5091. * <p>
    5092. +
    5093. * Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,
    5094. +
    5095. * other than the second arg for an IN/NOT IN operator.</li>
    5096. +
    5097. * </p>
    5098. */
    5099. -
    5100. toString:function (ds) {
    5101. -
    5102. 10 !Dataset && (Dataset = require("./dataset"));
    5103. -
    5104. 10 ds = ds || new Dataset();
    5105. -
    5106. 10 return ds.booleanConstantSql(this.__constant);
    5107. -
    5108. }
    5109. -
    5110. }
    5111. -
    5112. }).as(sql, "BooleanConstant");
    5113. +
    5114. constructor:function (op) {
    5115. +
    5116. 7952 if (op) {
    5117. +
    5118. 7164 var args = argsToArray(arguments,1 );
    5119. +
    5120. //make a copy of the args
    5121. +
    5122. 7164 var origArgs = args.slice(0);
    5123. +
    5124. 7164 args.forEach(function (a, i) {
    5125. +
    5126. 14676 if (Expression.isConditionSpecifier(a)) {
    5127. +
    5128. 6 args[i] = BooleanExpression.fromValuePairs(a);
    5129. +
    5130. }
    5131. +
    5132. });
    5133. +
    5134. 7164 op = op.toUpperCase();
    5135. -
    5136. /**
    5137. -
    5138. * Represents inverse boolean constants (currently only NOTNULL). A
    5139. -
    5140. * special class to allow for special behavior.
    5141. -
    5142. *
    5143. -
    5144. * @augments patio.sql.BooleanConstant
    5145. -
    5146. * @name NegativeBooleanConstant
    5147. -
    5148. * @memberOf patio.sql
    5149. -
    5150. */
    5151. -
    5152. 1var NegativeBooleanConstant = define(BooleanConstant, {
    5153. -
    5154. instance:{
    5155. -
    5156. /**@lends patio.sql.NegativeBooleanConstant.prototype*/
    5157. +
    5158. 7164 if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
    5159. +
    5160. 1164 if (args.length < 1) {
    5161. +
    5162. 0 throw new ExpressionError("The " + op + " operator requires at least 1 argument")
    5163. +
    5164. }
    5165. +
    5166. 1164 var oldArgs = args.slice(0);
    5167. +
    5168. 1164 args = [];
    5169. +
    5170. 1164 oldArgs.forEach(function (a) {
    5171. +
    5172. 2739 a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
    5173. +
    5174. });
    5175. +
    5176. +
    5177. 6000 } else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
    5178. +
    5179. 5937 if (args.length != 2) {
    5180. +
    5181. 0 throw new ExpressionError("The " + op + " operator requires precisely 2 arguments");
    5182. +
    5183. }
    5184. +
    5185. //With IN/NOT IN, even if the second argument is an array of two element arrays,
    5186. +
    5187. //don't convert it into a boolean expression, since it's definitely being used
    5188. +
    5189. //as a value list.
    5190. +
    5191. 5937 if (IN_OPERATORS[op]) {
    5192. +
    5193. 23 args[1] = origArgs[1]
    5194. +
    5195. }
    5196. +
    5197. 63 } else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
    5198. +
    5199. 63 if (args.length != 1) {
    5200. +
    5201. 0 throw new ExpressionError("The " + op + " operator requires only one argument");
    5202. +
    5203. }
    5204. +
    5205. } else {
    5206. +
    5207. 0 throw new ExpressionError("Invalid operator " + op);
    5208. +
    5209. }
    5210. +
    5211. 7164 this.op = op;
    5212. +
    5213. 7164 this.args = args;
    5214. +
    5215. }
    5216. +
    5217. },
    5218. /**
    5219. -
    5220. * Converts the {@link patio.sql.NegativeBooleanConstant} to a string.
    5221. +
    5222. * Converts the ComplexExpression to a string.
    5223. *
    5224. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5225. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5226. *
    5227. -
    5228. * @return String the SQL version of the {@link patio.sql.NegativeBooleanConstant}.
    5229. +
    5230. * @return String the SQL version of the {@link patio.sql.ComplexExpression}.
    5231. */
    5232. toString:function (ds) {
    5233. -
    5234. 2 !Dataset && (Dataset = require("./dataset"));
    5235. -
    5236. 2 ds = ds || new Dataset();
    5237. -
    5238. 2 return ds.negativeBooleanConstantSql(this.__constant);
    5239. +
    5240. 6761 !Dataset && (Dataset = require("./dataset"));
    5241. +
    5242. 6761 ds = ds || new Dataset();
    5243. +
    5244. 6761 return ds.complexExpressionSql(this.op, this.args);
    5245. }
    5246. -
    5247. }
    5248. -
    5249. }).as(sql, "NegativeBooleanConstant");
    5250. -
    5251. -
    5252. /**
    5253. -
    5254. * @namespace Holds default generic constants that can be referenced. These
    5255. -
    5256. * are included in {@link patio}
    5257. -
    5258. * @name Constants
    5259. -
    5260. * @memberOf patio.sql
    5261. -
    5262. */
    5263. -
    5264. 1sql.Constants = {
    5265. -
    5266. /**@lends patio.sql.Constants*/
    5267. -
    5268. -
    5269. /**
    5270. -
    5271. * Constant for CURRENT DATE
    5272. -
    5273. * @type patio.sql.Constant
    5274. -
    5275. */
    5276. -
    5277. CURRENT_DATE:new Constant("CURRENT_DATE"),
    5278. -
    5279. -
    5280. /**
    5281. -
    5282. * Constant for CURRENT TIME
    5283. -
    5284. * @type patio.sql.Constant
    5285. -
    5286. */
    5287. -
    5288. CURRENT_TIME:new Constant("CURRENT_TIME"),
    5289. -
    5290. -
    5291. /**
    5292. -
    5293. * Constant for CURRENT TIMESTAMP
    5294. -
    5295. * @type patio.sql.Constant
    5296. -
    5297. */
    5298. -
    5299. CURRENT_TIMESTAMP:new Constant("CURRENT_TIMESTAMP"),
    5300. -
    5301. -
    5302. /**
    5303. -
    5304. * Constant for TRUE
    5305. -
    5306. * @type patio.sql.BooleanConstant
    5307. -
    5308. */
    5309. -
    5310. SQLTRUE:new BooleanConstant(1),
    5311. -
    5312. -
    5313. /**
    5314. -
    5315. * Constant for TRUE
    5316. -
    5317. * @type patio.sql.BooleanConstant
    5318. -
    5319. */
    5320. -
    5321. TRUE:new BooleanConstant(1),
    5322. -
    5323. -
    5324. /**
    5325. -
    5326. * Constant for FALSE.
    5327. -
    5328. * @type patio.sql.BooleanConstant
    5329. -
    5330. */
    5331. -
    5332. SQLFALSE:new BooleanConstant(0),
    5333. -
    5334. -
    5335. /**
    5336. -
    5337. * Constant for FALSE
    5338. -
    5339. * @type patio.sql.BooleanConstant
    5340. -
    5341. */
    5342. -
    5343. FALSE:new BooleanConstant(0),
    5344. -
    5345. /**
    5346. -
    5347. * Constant for NULL
    5348. -
    5349. * @type patio.sql.BooleanConstant
    5350. -
    5351. */
    5352. -
    5353. NULL:new BooleanConstant(null),
    5354. -
    5355. -
    5356. /**
    5357. -
    5358. * Constant for NOT NULL
    5359. -
    5360. * @type patio.sql.NegativeBooleanConstant
    5361. -
    5362. */
    5363. -
    5364. NOTNULL:new NegativeBooleanConstant(null)
    5365. -
    5366. -
    5367. };
    5368. +
    5369. },
    5370. -
    5371. 1var Constants = sql.Constants
    5372. +
    5373. static:{
    5374. +
    5375. /**@lends patio.sql.ComplexExpression*/
    5376. -
    5377. 1var Identifier = define([GenericExpression, QualifyingMethods], {
    5378. -
    5379. instance:{
    5380. -
    5381. /**@lends patio.sql.Identifier.prototype*/
    5382. +
    5383. /**
    5384. +
    5385. * Hash of operator inversions
    5386. +
    5387. * @type Object
    5388. +
    5389. * @default {
    5390. +
    5391. * AND:"OR",
    5392. +
    5393. * OR:"AND",
    5394. +
    5395. * GT:"lte",
    5396. +
    5397. * GTE:"lt",
    5398. +
    5399. * LT:"gte",
    5400. +
    5401. * LTE:"gt",
    5402. +
    5403. * EQ:"neq",
    5404. +
    5405. * NEQ:"eq",
    5406. +
    5407. * LIKE:'NOT LIKE',
    5408. +
    5409. * "NOT LIKE":"LIKE",
    5410. +
    5411. * '!~*':'~*',
    5412. +
    5413. * '~*':'!~*',
    5414. +
    5415. * "~":'!~',
    5416. +
    5417. * "IN":'NOTIN',
    5418. +
    5419. * "NOTIN":"IN",
    5420. +
    5421. * "IS":'IS NOT',
    5422. +
    5423. * "ISNOT":"IS",
    5424. +
    5425. * NOT:"NOOP",
    5426. +
    5427. * NOOP:"NOT",
    5428. +
    5429. * ILIKE:'NOT ILIKE',
    5430. +
    5431. * NOTILIKE:"ILIKE"
    5432. +
    5433. * }
    5434. +
    5435. */
    5436. +
    5437. OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,
    5438. /**
    5439. -
    5440. * Represents an identifier (column or table). Can be used
    5441. -
    5442. * to specify a String with multiple underscores that should not be
    5443. -
    5444. * split, or for creating an implicit identifier without using a String.
    5445. +
    5446. * Default mathematical operators.
    5447. *
    5448. -
    5449. * @constructs
    5450. -
    5451. * @augments patio.sql.GenericExpression
    5452. -
    5453. * @augments patio.sql.QualifyingMethods
    5454. +
    5455. * @type Object
    5456. +
    5457. * @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}
    5458. +
    5459. */
    5460. +
    5461. MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,
    5462. +
    5463. +
    5464. /**
    5465. +
    5466. * Default bitwise operators.
    5467. *
    5468. -
    5469. * @param {String}value the identifier.
    5470. +
    5471. * @type Object
    5472. +
    5473. * @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}
    5474. +
    5475. */
    5476. +
    5477. BITWISE_OPERATORS:BITWISE_OPERATORS,
    5478. +
    5479. /**
    5480. +
    5481. * Default inequality operators.
    5482. *
    5483. -
    5484. * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
    5485. +
    5486. * @type Object
    5487. +
    5488. * @default {GT:">",GTE:">=",LT:"<",LTE:"<="}
    5489. */
    5490. -
    5491. constructor:function (value) {
    5492. -
    5493. 16802 this.__value = value;
    5494. -
    5495. },
    5496. +
    5497. INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,
    5498. /**
    5499. -
    5500. * Converts the {@link patio.sql.Identifier} to a string.
    5501. -
    5502. *
    5503. -
    5504. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5505. -
    5506. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5507. +
    5508. * Default boolean operators.
    5509. *
    5510. -
    5511. * @return String the SQL version of the {@link patio.sql.Identifier}.
    5512. +
    5513. * @type Object
    5514. +
    5515. * @default {AND:"AND",OR:"OR"}
    5516. */
    5517. -
    5518. toString:function (ds) {
    5519. -
    5520. 21906 !Dataset && (Dataset = require("./dataset"));
    5521. -
    5522. 21906 ds = ds || new Dataset();
    5523. -
    5524. 21906 return ds.quoteIdentifier(this);
    5525. -
    5526. },
    5527. -
    5528. -
    5529. /**@ignore*/
    5530. -
    5531. getters:{
    5532. -
    5533. value:function () {
    5534. -
    5535. 25154 return this.__value;
    5536. -
    5537. }
    5538. -
    5539. }
    5540. -
    5541. }
    5542. -
    5543. }).as(sql, "Identifier");
    5544. -
    5545. -
    5546. 1var JoinClause = define(Expression, {
    5547. -
    5548. instance:{
    5549. -
    5550. /**@lends patio.sql.JoinClause.prototype*/
    5551. +
    5552. BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,
    5553. /**
    5554. -
    5555. * Represents an SQL JOIN clause, used for joining tables.
    5556. -
    5557. * Created by {@link patio.Dataset} join methods.
    5558. -
    5559. * @constructs
    5560. -
    5561. * @augments patio.sql.Expression
    5562. -
    5563. *
    5564. -
    5565. * @param {String} joinType the type of join this JoinClause should use
    5566. -
    5567. * @param table the table to join with
    5568. -
    5569. * @param tableAlias the alias to use for this join clause
    5570. +
    5571. * Default IN operators.
    5572. *
    5573. -
    5574. * @property {String} joinType <b>READ ONLY</b> the type of join this JoinClause should use
    5575. -
    5576. * @property table <b>READ ONLY</b> the table to join with
    5577. -
    5578. * @property joinType <b>READ ONLY</b> the alias to use for this join clause
    5579. -
    5580. * */
    5581. -
    5582. constructor:function (joinType, table, tableAlias) {
    5583. -
    5584. 791 this.__joinType = joinType;
    5585. -
    5586. 791 this.__table = table;
    5587. -
    5588. 791 this.__tableAlias = tableAlias || null;
    5589. -
    5590. },
    5591. -
    5592. +
    5593. * @type Object
    5594. +
    5595. * @default {IN:"IN",NOTIN:'NOT IN'}
    5596. +
    5597. */
    5598. +
    5599. IN_OPERATORS:IN_OPERATORS,
    5600. /**
    5601. -
    5602. * Converts the {@link patio.sql.JoinClause} to a string.
    5603. +
    5604. * Default IS operators.
    5605. *
    5606. -
    5607. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5608. -
    5609. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5610. +
    5611. * @type Object
    5612. +
    5613. * @default {IS:"IS", ISNOT:'IS NOT'}
    5614. +
    5615. */
    5616. +
    5617. IS_OPERATORS:IS_OPERATORS,
    5618. +
    5619. /**
    5620. +
    5621. * Default two arity operators.
    5622. *
    5623. -
    5624. * @return String the SQL version of the {@link patio.sql.JoinClause}.
    5625. +
    5626. * @type Object
    5627. +
    5628. * @default {
    5629. +
    5630. * EQ:'=',
    5631. +
    5632. * NEQ:'!=', LIKE:"LIKE",
    5633. +
    5634. * "NOT LIKE":'NOT LIKE',
    5635. +
    5636. * ILIKE:"ILIKE",
    5637. +
    5638. * "NOT ILIKE":'NOT ILIKE',
    5639. +
    5640. * "~":"~",
    5641. +
    5642. * '!~':"!~",
    5643. +
    5644. * '~*':"~*",
    5645. +
    5646. * '!~*':"!~*",
    5647. +
    5648. * GT:">",
    5649. +
    5650. * GTE:">=",
    5651. +
    5652. * LT:"<",
    5653. +
    5654. * LTE:"<=",
    5655. +
    5656. * bitWiseAnd:"&",
    5657. +
    5658. * bitWiseOr:"|",
    5659. +
    5660. * exclusiveOr:"^",
    5661. +
    5662. * leftShift:"<<",
    5663. +
    5664. * rightShift:">>",
    5665. +
    5666. * IS:"IS",
    5667. +
    5668. * ISNOT:'IS NOT',
    5669. +
    5670. * IN:"IN",
    5671. +
    5672. * NOTIN:'NOT IN'
    5673. +
    5674. * }
    5675. */
    5676. -
    5677. toString:function (ds) {
    5678. -
    5679. 17 !Dataset && (Dataset = require("./dataset"));
    5680. -
    5681. 17 ds = ds || new Dataset();
    5682. -
    5683. 17 return ds.joinClauseSql(this);
    5684. -
    5685. },
    5686. -
    5687. -
    5688. /**@ignore*/
    5689. -
    5690. getters:{
    5691. -
    5692. joinType:function () {
    5693. -
    5694. 927 return this.__joinType;
    5695. -
    5696. },
    5697. -
    5698. -
    5699. table:function () {
    5700. -
    5701. 928 return this.__table;
    5702. -
    5703. },
    5704. -
    5705. -
    5706. tableAlias:function () {
    5707. -
    5708. 926 return this.__tableAlias;
    5709. -
    5710. }
    5711. -
    5712. }
    5713. -
    5714. }
    5715. -
    5716. }).as(sql, "JoinClause");
    5717. -
    5718. +
    5719. TWO_ARITY_OPERATORS:TWO_ARITY_OPERATORS,
    5720. -
    5721. 1var JoinOnClause = define(JoinClause, {
    5722. -
    5723. instance:{
    5724. -
    5725. /**@lends patio.sql.JoinOnClause.prototype*/
    5726. /**
    5727. -
    5728. * Represents an SQL JOIN clause with ON conditions. Created by {@link patio.Dataset} join methods.
    5729. -
    5730. * See {@link patio.sql.JoinClause} for other argument parameters.
    5731. -
    5732. * @constructs
    5733. -
    5734. * @augments patio.sql.JoinClause
    5735. +
    5736. * Default N(multi) arity operators.
    5737. *
    5738. -
    5739. * @param on the expression to filter with. See {@link patio.Dataset#filter}
    5740. -
    5741. * @property on <b>READ ONLY</b> the filter to use with joining the datasets.
    5742. +
    5743. * @type Object
    5744. +
    5745. * @default {
    5746. +
    5747. * "||":"||",
    5748. +
    5749. * AND:"AND",
    5750. +
    5751. * OR:"OR",
    5752. +
    5753. * PLUS:"+",
    5754. +
    5755. * MINUS:"-",
    5756. +
    5757. * DIVIDE:"/", MULTIPLY:"*"
    5758. +
    5759. * }
    5760. */
    5761. -
    5762. constructor:function (on, joinType, table, tableAlias) {
    5763. -
    5764. 761 this.__on = on;
    5765. -
    5766. 761 this._super(arguments, [joinType, table, tableAlias]);
    5767. -
    5768. },
    5769. +
    5770. N_ARITY_OPERATORS:N_ARITY_OPERATORS,
    5771. /**
    5772. -
    5773. * Converts the {@link patio.sql.JoinOnClause} to a string.
    5774. -
    5775. *
    5776. -
    5777. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5778. -
    5779. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5780. +
    5781. * Default ONE operators.
    5782. *
    5783. -
    5784. * @return String the SQL version of the {@link patio.sql.JoinOnClause}.
    5785. +
    5786. * @type Object
    5787. +
    5788. * @default {
    5789. +
    5790. * "NOT":"NOT",
    5791. +
    5792. * "NOOP":"NOOP"
    5793. +
    5794. * }
    5795. */
    5796. -
    5797. toString:function (ds) {
    5798. -
    5799. 813 !Dataset && (Dataset = require("./dataset"));
    5800. -
    5801. 813 ds = ds || new Dataset();
    5802. -
    5803. 813 return ds.joinOnClauseSql(this);
    5804. -
    5805. },
    5806. -
    5807. -
    5808. /**@ignore*/
    5809. -
    5810. getters:{
    5811. -
    5812. on:function () {
    5813. -
    5814. 813 return this.__on;
    5815. -
    5816. }
    5817. -
    5818. }
    5819. +
    5820. ONE_ARITY_OPERATORS:ONE_ARITY_OPERATORS
    5821. }
    5822. -
    5823. }).as(sql, "JoinOnClause");
    5824. +
    5825. }).as(sql, "ComplexExpression");
    5826. -
    5827. 1var JoinUsingClause = define(JoinClause, {
    5828. -
    5829. instance:{
    5830. -
    5831. /**@lends patio.sql.JoinUsingClause.prototype*/
    5832. +
    5833. /**
    5834. +
    5835. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    5836. +
    5837. * in a boolean value in SQL.
    5838. +
    5839. *
    5840. +
    5841. * @augments patio.sql.ComplexExpression
    5842. +
    5843. * @augments patio.sql.BooleanMethods
    5844. +
    5845. * @name BooleanExpression
    5846. +
    5847. * @memberOf patio.sql
    5848. +
    5849. */
    5850. +
    5851. 1var BooleanExpression = define([ComplexExpression, BooleanMethods], {
    5852. +
    5853. static:{
    5854. +
    5855. /**@lends patio.sql.BooleanExpression*/
    5856. /**
    5857. -
    5858. * Represents an SQL JOIN clause with USING conditions.
    5859. -
    5860. * Created by {@link patio.Dataset} join methods.
    5861. -
    5862. * See {@link patio.sql.JoinClause} for other argument parameters.
    5863. +
    5864. * Invert the expression, if possible. If the expression cannot
    5865. +
    5866. * be inverted, it throws an {@link patio.error.ExpressionError}. An inverted expression should match
    5867. +
    5868. * everything that the uninverted expression did not match, and vice-versa, except for possible issues with
    5869. +
    5870. * SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
    5871. *
    5872. -
    5873. * @constructs
    5874. -
    5875. * @augments patio.sql.JoinClause
    5876. +
    5877. * @example
    5878. +
    5879. * BooleanExpression.invert(sql.a) //=> NOT "a"
    5880. *
    5881. -
    5882. * @param using the column/s to use when joining.
    5883. -
    5884. * @property using <b>READ ONLY</b> the column/s to use when joining.
    5885. +
    5886. * @param {patio.sql.BooleanExpression} expression
    5887. +
    5888. * the expression to invert.
    5889. +
    5890. *
    5891. +
    5892. * @return {patio.sql.BooleanExpression} the inverted expression.
    5893. */
    5894. -
    5895. constructor:function (using, joinType, table, tableAlias) {
    5896. -
    5897. 8 this.__using = using.map(function (u) {
    5898. -
    5899. 9 return isString(u) ? new Identifier(u) : u;
    5900. -
    5901. });
    5902. -
    5903. 8 this._super(arguments, [joinType, table, tableAlias]);
    5904. +
    5905. invert:function (expression) {
    5906. +
    5907. 95 if (isInstanceOf(expression, BooleanExpression)) {
    5908. +
    5909. 90 var op = expression.op, newArgs;
    5910. +
    5911. 90 if (op == "AND" || op == "OR") {
    5912. +
    5913. 3 newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args.map(function (arg) {
    5914. +
    5915. 6 return BooleanExpression.invert(arg);
    5916. +
    5917. }));
    5918. +
    5919. 3 return BooleanExpression.fromArgs(newArgs);
    5920. +
    5921. } else {
    5922. +
    5923. 87 newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);
    5924. +
    5925. 87 return BooleanExpression.fromArgs(newArgs);
    5926. +
    5927. }
    5928. +
    5929. 5 } else if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
    5930. +
    5931. 0 throw new ExpressionError(format("Cannot invert %4j", [expression]));
    5932. +
    5933. } else {
    5934. +
    5935. 5 return new BooleanExpression("NOT", expression);
    5936. +
    5937. }
    5938. },
    5939. -
    5940. /**
    5941. -
    5942. * Converts the {@link patio.sql.JoinUsingClause} to a string.
    5943. +
    5944. * Take pairs of values (e.g. a hash or array of two element arrays)
    5945. +
    5946. * and converts it to a {@link patio.sql.BooleanExpression}. The operator and args
    5947. +
    5948. * used depends on the case of the right (2nd) argument:
    5949. *
    5950. -
    5951. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    5952. -
    5953. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    5954. +
    5955. * <pre class='code'>
    5956. +
    5957. * BooleanExpression.fromValuePairs({a : [1,2,3]}) //=> a IN (1,2,3)
    5958. +
    5959. * BooleanExpression.fromValuePairs({a : true}); // a IS TRUE;
    5960. +
    5961. * BooleanExpression.fromValuePairs({a : /^A/i}); // a *~ '^A'
    5962. +
    5963. * </pre>
    5964. *
    5965. -
    5966. * @return String the SQL version of the {@link patio.sql.JoinUsingClause}.
    5967. -
    5968. */
    5969. -
    5970. toString:function (ds) {
    5971. -
    5972. 95 !Dataset && (Dataset = require("./dataset"));
    5973. -
    5974. 95 ds = ds || new Dataset();
    5975. -
    5976. 95 return ds.joinUsingClauseSql(this);
    5977. -
    5978. },
    5979. -
    5980. -
    5981. /**@ignore*/
    5982. -
    5983. getters:{
    5984. -
    5985. using:function () {
    5986. -
    5987. 95 return this.__using;
    5988. +
    5989. * If multiple arguments are given, they are joined with the op given (AND
    5990. +
    5991. * by default, OR possible). If negate is set to true,
    5992. +
    5993. * all subexpressions are inverted before used.
    5994. +
    5995. * <pre class='code'>
    5996. +
    5997. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}) //=> a IN (1,2,3) AND b IS TRUE
    5998. +
    5999. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
    6000. +
    6001. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
    6002. +
    6003. * </pre>
    6004. +
    6005. * @param {Object} a object to convert to a {@link patio.sql.BooleanExpression}
    6006. +
    6007. * @param {String} [op="AND"] Boolean operator to join each subexpression with.
    6008. +
    6009. * <pre class="code">
    6010. +
    6011. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
    6012. +
    6013. * </pre>
    6014. +
    6015. * @param {Boolean} [negate=false] set to try to invert the {@link patio.sql.BooleanExpression}.
    6016. +
    6017. * <pre class="code">
    6018. +
    6019. * BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
    6020. +
    6021. * </pre>
    6022. +
    6023. * @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
    6024. +
    6025. */
    6026. +
    6027. fromValuePairs:function (a, op, negate) {
    6028. +
    6029. 7610 !Dataset && (Dataset = require("./dataset"));
    6030. +
    6031. 7610 op = op || "AND", negate = negate || false;
    6032. +
    6033. 7610 var pairArr = [];
    6034. +
    6035. 7610 var isArr = isArray(a) && Expression.isConditionSpecifier(a);
    6036. +
    6037. 7610 if (isHash(a)) {
    6038. +
    6039. 3385 pairArr.push(this.__filterObject(a));
    6040. +
    6041. } else {
    6042. +
    6043. 4225 for (var k in a) {
    6044. +
    6045. 5203 var v = isArr ? a[k][1] : a[k], ret;
    6046. +
    6047. 5203 k = isArr ? a[k][0] : k;
    6048. +
    6049. 5203 if (isArray(v) || isInstanceOf(v, Dataset)) {
    6050. +
    6051. 17 k = isArray(k) ? k.map(function (i) {
    6052. +
    6053. 12 return isString(i) ? sql.stringToIdentifier(i) : i
    6054. +
    6055. }) : isString(k) ? sql.stringToIdentifier(k) : k;
    6056. +
    6057. 17 ret = new BooleanExpression("IN", k, v);
    6058. +
    6059. 5186 } else if (isInstanceOf(v, NegativeBooleanConstant)) {
    6060. +
    6061. 0 ret = new BooleanExpression("ISNOT", k, v.constant);
    6062. +
    6063. 5186 } else if (isInstanceOf(v, BooleanConstant)) {
    6064. +
    6065. 0 ret = new BooleanExpression("IS", k, v.constant);
    6066. +
    6067. 5186 } else if (isNull(v) || isBoolean(v)) {
    6068. +
    6069. 240 ret = new BooleanExpression("IS", k, v);
    6070. +
    6071. 4946 } else if (isHash(v)) {
    6072. +
    6073. 0 ret = BooleanExpression.__filterObject(v, k);
    6074. +
    6075. 4946 } else if (isRegExp(v)) {
    6076. +
    6077. 69 ret = StringExpression.like(sql.stringToIdentifier(k), v);
    6078. +
    6079. } else {
    6080. +
    6081. 4877 ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
    6082. +
    6083. }
    6084. +
    6085. 5203 negate && (ret = BooleanExpression.invert(ret));
    6086. +
    6087. 5203 pairArr.push(ret);
    6088. +
    6089. }
    6090. }
    6091. -
    6092. }
    6093. -
    6094. }
    6095. -
    6096. }).as(sql, "JoinUsingClause");
    6097. -
    6098. -
    6099. -
    6100. 1var PlaceHolderLiteralString = define(GenericExpression, {
    6101. -
    6102. instance:{
    6103. -
    6104. /**@lends patio.sql.PlaceHolderLiteralString.prototype*/
    6105. +
    6106. //if We just have one then return the first otherwise create a new Boolean expression
    6107. +
    6108. 7610 return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
    6109. +
    6110. },
    6111. /**
    6112. -
    6113. * Represents a literal string with placeholders and arguments.
    6114. -
    6115. * This is necessary to ensure delayed literalization of the arguments
    6116. -
    6117. * required for the prepared statement support and for database-specific
    6118. -
    6119. * literalization.
    6120. +
    6121. * @private
    6122. *
    6123. -
    6124. * @constructs
    6125. -
    6126. * @augments patio.sql.GenericExpression
    6127. +
    6128. * This builds an expression from a hash
    6129. *
    6130. -
    6131. * @param {String} str the string that contains placeholders.
    6132. -
    6133. * @param {Array} args array of arguments that will be literalized using {@link patio.Dataset#literal}, and
    6134. -
    6135. * replaced in the string.
    6136. -
    6137. * @param {Boolean} [parens=false] set to true to wrap the string in parens.
    6138. +
    6139. * @example
    6140. *
    6141. -
    6142. * @property {String} str <b>READ ONLY</b> the string that contains placeholders.
    6143. -
    6144. * @property {Array} args <b>READ ONLY</b> array of arguments that will be literalized using {@link patio.Dataset#literal}, and
    6145. -
    6146. * replaced in the string.
    6147. -
    6148. * @property {String} parens <b>READ ONLY</b> set to true to wrap the string in parens.
    6149. -
    6150. */
    6151. -
    6152. constructor:function (str, args, parens) {
    6153. -
    6154. 53 parens = parens || false;
    6155. -
    6156. 53 var v;
    6157. -
    6158. 53 this.__str = str;
    6159. -
    6160. 53 this.__args = isArray(args) && args.length == 1 && isHash((v = args[0])) ? v : args;
    6161. -
    6162. 53 this.__parens = parens;
    6163. -
    6164. },
    6165. -
    6166. -
    6167. /**
    6168. -
    6169. * Converts the {@link patio.sql.PlaceHolderLiteralString} to a string.
    6170. +
    6171. * Dataset._filterObject({a : 1}) //=> WHERE (a = 1)
    6172. +
    6173. * Dataset._filterObject({x : {gt : 1}}) //=> WHERE (x > 1)
    6174. +
    6175. * Dataset._filterObject({x : {gt : 1}, a : 1}) //=> WHERE ((x > 1) AND (a = 1))
    6176. +
    6177. * Dataset._filterObject({x : {like : "name"}}) //=> WHERE (x LIKE 'name')
    6178. +
    6179. * Dataset._filterObject({x : {iLike : "name"}}) //=> WHERE (x LIKE 'name')
    6180. +
    6181. * Dataset._filterObject({x : {between : [1,10]}}) //=> WHERE ((x >= 1) AND (x <= 10))
    6182. +
    6183. * Dataset._filterObject({x : {notBetween : [1,10]}}) //=> WHERE ((x < 1) OR (x > 10))
    6184. +
    6185. * Dataset._filterObject({x : {neq : 1}}) //=> WHERE (x != 1)
    6186. *
    6187. -
    6188. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6189. -
    6190. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6191. +
    6192. * @param {Object} expr the expression we need to create an expression out of
    6193. +
    6194. * @param {String} [key=null] the key that the hash corresponds to
    6195. *
    6196. -
    6197. * @return String the SQL version of the {@link patio.sql.PlaceHolderLiteralString}.
    6198. +
    6199. * @return {patio.sql.Expression} an expression to use in the filter
    6200. */
    6201. -
    6202. toString:function (ds) {
    6203. -
    6204. 52 !Dataset && (Dataset = require("./dataset"));
    6205. -
    6206. 52 ds = ds || new Dataset();
    6207. -
    6208. 52 return ds.placeholderLiteralStringSql(this);
    6209. -
    6210. },
    6211. -
    6212. -
    6213. /**@ignore*/
    6214. -
    6215. getters:{
    6216. -
    6217. str:function () {
    6218. -
    6219. 55 return this.__str;
    6220. -
    6221. },
    6222. -
    6223. args:function () {
    6224. -
    6225. 55 return this.__args;
    6226. -
    6227. },
    6228. +
    6229. __filterObject:function (expr, key) {
    6230. +
    6231. 3502 var pairs = [], opts, newKey;
    6232. +
    6233. 3502 var twoArityOperators = this.TWO_ARITY_OPERATORS;
    6234. +
    6235. 3502 for (var k in expr) {
    6236. +
    6237. 3541 var v = expr[k];
    6238. +
    6239. 3541 if (isHash(v)) { //its a hash too filter it too!
    6240. +
    6241. 115 pairs.push(this.__filterObject(v, k));
    6242. +
    6243. 3426 } else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
    6244. +
    6245. //its a two arrity operator (e.g. '=', '>')
    6246. +
    6247. 118 newKey = isString(key) ? key.split(",") : [key];
    6248. +
    6249. 118 if (newKey.length > 1) {
    6250. +
    6251. //this represents a hash where the key represents two columns
    6252. +
    6253. //(e.g. {"col1,col2" : 1}) => WHERE (col1 = 1 AND col2 = 1)
    6254. +
    6255. 1 pairs = pairs.concat(newKey.map(function (k) {
    6256. +
    6257. //filter each column with the expression
    6258. +
    6259. 2 return this.__filterObject(expr, k);
    6260. +
    6261. }, this));
    6262. +
    6263. } else {
    6264. +
    6265. 117 newKey = [sql.stringToIdentifier(newKey[0])];
    6266. +
    6267. 117 if (k.match(/^like$/)) {
    6268. +
    6269. //its a like clause {col : {like : "hello"}}
    6270. -
    6271. parens:function () {
    6272. -
    6273. 55 return this.__parens;
    6274. +
    6275. 3 pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));
    6276. +
    6277. 114 } else if (k.match(/^iLike$/)) {
    6278. +
    6279. //its a like clause {col : {iLike : "hello"}}
    6280. +
    6281. 2 pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]).concat({caseInsensitive:true}))));
    6282. +
    6283. 112 } else if (k.match(/between/i)) {
    6284. +
    6285. //its a like clause {col : {between : [1,10]}}
    6286. +
    6287. 6 var between = sql.stringToIdentifier(newKey[0]).between(v);
    6288. +
    6289. 6 k == "notBetween" && (between = between.not());
    6290. +
    6291. 6 pairs.push(between);
    6292. +
    6293. } else {
    6294. +
    6295. //otherwise is just a boolean expressio
    6296. +
    6297. //it its not a valid operator then we
    6298. +
    6299. //BooleanExpression with throw an error
    6300. +
    6301. 106 pairs.push(new BooleanExpression(k, newKey[0], v));
    6302. +
    6303. }
    6304. +
    6305. }
    6306. +
    6307. } else {
    6308. +
    6309. //we're not a twoarity operator
    6310. +
    6311. //so we create a boolean expression out of it
    6312. +
    6313. 3308 newKey = k.split(",");
    6314. +
    6315. 3308 if (newKey.length == 1) {
    6316. +
    6317. 3302 newKey = sql.stringToIdentifier(newKey[0]);
    6318. +
    6319. }
    6320. +
    6321. 3308 opts = [
    6322. +
    6323. [newKey, v]
    6324. +
    6325. ];
    6326. +
    6327. 3308 pairs.push(BooleanExpression.fromValuePairs(opts));
    6328. +
    6329. }
    6330. }
    6331. -
    6332. +
    6333. //if the total of pairs is one then we just return the first element
    6334. +
    6335. //otherwise we join them all with an AND
    6336. +
    6337. 3502 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
    6338. }
    6339. }
    6340. -
    6341. }).as(sql, "PlaceHolderLiteralString");
    6342. +
    6343. }).as(sql, "BooleanExpression");
    6344. -
    6345. 1var SQLFunction = define(GenericExpression, {
    6346. +
    6347. 1var Constant = define(GenericExpression, {
    6348. instance:{
    6349. -
    6350. /**@lends patio.sql.SQLFunction.prototype*/
    6351. -
    6352. +
    6353. /**@lends patio.sql.Constant.prototype*/
    6354. /**
    6355. -
    6356. * Represents an SQL function call.
    6357. +
    6358. * Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.
    6359. *
    6360. * @constructs
    6361. * @augments patio.sql.GenericExpression
    6362. -
    6363. *
    6364. -
    6365. * @param {...} f variable number of arguments where the first argument is the name
    6366. -
    6367. * of the SQL function to invoke. The rest of the arguments will be literalized through
    6368. -
    6369. * {@link patio.Dataset#literal} and placed into the SQL function call.
    6370. -
    6371. *
    6372. -
    6373. * @property {String} f <b>READ ONLY</b> the SQL function to call.
    6374. -
    6375. * @property {Array} args <b>READ ONLY</b> args arguments will be literalized through
    6376. -
    6377. * {@link patio.Dataset#literal} and placed into the SQL function call.
    6378. -
    6379. * */
    6380. -
    6381. constructor:function (f) {
    6382. -
    6383. 1109 var args = argsToArray(arguments).slice(1);
    6384. -
    6385. 1109 this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {
    6386. -
    6387. 773 return isString(a) ? sql.stringToIdentifier(a) : a;
    6388. -
    6389. });
    6390. +
    6391. * @property {String} constant <b>READ ONLY</b> the contant.
    6392. +
    6393. */
    6394. +
    6395. constructor:function (constant) {
    6396. +
    6397. 18 this.__constant = constant;
    6398. },
    6399. /**
    6400. -
    6401. * Converts the {@link patio.sql.SQLFunction} to a string.
    6402. +
    6403. * Converts the {@link patio.sql.Constant} to a string.
    6404. *
    6405. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6406. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6407. *
    6408. -
    6409. * @return String the SQL version of the {@link patio.sql.SQLFunction}.
    6410. +
    6411. * @return String the SQL version of the {@link patio.sql.Constant}.
    6412. */
    6413. toString:function (ds) {
    6414. -
    6415. 565 !Dataset && (Dataset = require("./dataset"));
    6416. -
    6417. 565 ds = ds || new Dataset();
    6418. -
    6419. 565 return ds.functionSql(this);
    6420. +
    6421. 6 !Dataset && (Dataset = require("./dataset"));
    6422. +
    6423. 6 ds = ds || new Dataset();
    6424. +
    6425. 6 return ds.constantSql(this.__constant);
    6426. },
    6427. -
    6428. /**@ignore*/
    6429. getters:{
    6430. -
    6431. f:function () {
    6432. -
    6433. 567 return this.__f;
    6434. -
    6435. },
    6436. -
    6437. -
    6438. args:function () {
    6439. -
    6440. 567 return this.__args;
    6441. +
    6442. constant:function () {
    6443. +
    6444. 0 return this.__constant;
    6445. }
    6446. }
    6447. }
    6448. -
    6449. }).as(sql, "SQLFunction");
    6450. +
    6451. }).as(sql, "Constant");
    6452. /**
    6453. -
    6454. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    6455. -
    6456. * in a numeric value in SQL.
    6457. -
    6458. *
    6459. -
    6460. * @name NumericExpression
    6461. +
    6462. * @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
    6463. +
    6464. * @auments patio.sql.Constant
    6465. +
    6466. * @name BooleanConstant
    6467. * @memberOf patio.sql
    6468. -
    6469. * @augments patio.sql.ComplexExpression
    6470. -
    6471. * @augments patio.sql.BitWiseMethods
    6472. -
    6473. * @augments patio.sql.NumericMethods
    6474. -
    6475. * @augments patio.sql.InequalityMethods
    6476. */
    6477. -
    6478. 1var NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");
    6479. -
    6480. -
    6481. -
    6482. 1var OrderedExpression = define(Expression, {
    6483. +
    6484. 1var BooleanConstant = define(Constant, {
    6485. instance:{
    6486. -
    6487. /**@lends patio.sql.OrderedExpression.prototype*/
    6488. -
    6489. -
    6490. /**
    6491. -
    6492. * Represents a column/expression to order the result set by.
    6493. -
    6494. * @constructs
    6495. -
    6496. * @augments patio.sql.Expression
    6497. -
    6498. *
    6499. -
    6500. * @param expression the expression to order
    6501. -
    6502. * @param {Boolean}[descending=true] set to false to order ASC
    6503. -
    6504. * @param {String|Object} [opts=null] additional options
    6505. -
    6506. * <ul>
    6507. -
    6508. * <li>String: if value is "first" the null values will be first, if "last" then null values
    6509. -
    6510. * will be last</li>
    6511. -
    6512. * <li>Object: will pull the nulls property off of the object use use the same rules as if it
    6513. -
    6514. * were a string</li>
    6515. -
    6516. * </ul>
    6517. -
    6518. * @property expression <b>READ ONLY</b> the expression to order.
    6519. -
    6520. * @property {Boolean} [descending=true] <b>READ ONLY</b> true if decending, false otherwise.
    6521. -
    6522. * @property {String} [nulls=null] if value is "first" the null values will be first, if "last" then null values
    6523. -
    6524. * will be last
    6525. -
    6526. */
    6527. -
    6528. constructor:function (expression, descending, opts) {
    6529. -
    6530. 92 descending = isBoolean(descending) ? descending : true;
    6531. -
    6532. 92 opts = opts || {};
    6533. -
    6534. 92 this.__expression = expression;
    6535. -
    6536. 92 this.__descending = descending;
    6537. -
    6538. 92 var nulls = isString(opts) ? opts : opts.nulls;
    6539. -
    6540. 92 this.__nulls = isString(nulls) ? nulls.toLowerCase() : null;
    6541. -
    6542. },
    6543. -
    6544. -
    6545. /**
    6546. -
    6547. * @return {patio.sql.OrderedExpression} a copy that is ordered ASC
    6548. -
    6549. */
    6550. -
    6551. asc:function () {
    6552. -
    6553. 0 return new OrderedExpression(this.__expression, false, {nulls:this.__nulls});
    6554. -
    6555. },
    6556. +
    6557. /**@lends patio.sql.BooleanConstant.prototype*/
    6558. /**
    6559. -
    6560. * @return {patio.sql.OrderedExpression} Return a copy that is ordered DESC
    6561. +
    6562. * Converts the {@link patio.sql.BooleanConstant} to a string.
    6563. +
    6564. *
    6565. +
    6566. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6567. +
    6568. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6569. +
    6570. *
    6571. +
    6572. * @return String the SQL version of the {@link patio.sql.BooleanConstant}.
    6573. */
    6574. -
    6575. desc:function () {
    6576. -
    6577. 0 return new OrderedExpression(this.__expression, true, {nulls:this.__nulls});
    6578. -
    6579. },
    6580. +
    6581. toString:function (ds) {
    6582. +
    6583. 10 !Dataset && (Dataset = require("./dataset"));
    6584. +
    6585. 10 ds = ds || new Dataset();
    6586. +
    6587. 10 return ds.booleanConstantSql(this.__constant);
    6588. +
    6589. }
    6590. +
    6591. }
    6592. +
    6593. }).as(sql, "BooleanConstant");
    6594. -
    6595. /**
    6596. -
    6597. * * @return {patio.sql.OrderedExpression} an inverted expression, changing ASC to DESC and NULLS FIRST to NULLS LAST.
    6598. -
    6599. * */
    6600. -
    6601. invert:function () {
    6602. -
    6603. 17 return new OrderedExpression(this.__expression, !this.__descending, {nulls:this._static.INVERT_NULLS[this.__nulls] || this.__nulls});
    6604. -
    6605. },
    6606. +
    6607. /**
    6608. +
    6609. * Represents inverse boolean constants (currently only NOTNULL). A
    6610. +
    6611. * special class to allow for special behavior.
    6612. +
    6613. *
    6614. +
    6615. * @augments patio.sql.BooleanConstant
    6616. +
    6617. * @name NegativeBooleanConstant
    6618. +
    6619. * @memberOf patio.sql
    6620. +
    6621. */
    6622. +
    6623. 1var NegativeBooleanConstant = define(BooleanConstant, {
    6624. +
    6625. instance:{
    6626. +
    6627. /**@lends patio.sql.NegativeBooleanConstant.prototype*/
    6628. /**
    6629. -
    6630. * Converts the {@link patio.sql.OrderedExpression} to a string.
    6631. +
    6632. * Converts the {@link patio.sql.NegativeBooleanConstant} to a string.
    6633. *
    6634. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6635. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6636. *
    6637. -
    6638. * @return String the SQL version of the {@link patio.sql.OrderedExpression}.
    6639. +
    6640. * @return String the SQL version of the {@link patio.sql.NegativeBooleanConstant}.
    6641. */
    6642. toString:function (ds) {
    6643. -
    6644. 73 !Dataset && (Dataset = require("./dataset"));
    6645. -
    6646. 73 ds = ds || new Dataset();
    6647. -
    6648. 73 return ds.orderedExpressionSql(this);
    6649. -
    6650. },
    6651. -
    6652. -
    6653. /**@ignore*/
    6654. -
    6655. getters:{
    6656. -
    6657. expression:function () {
    6658. -
    6659. 75 return this.__expression;
    6660. -
    6661. },
    6662. -
    6663. descending:function () {
    6664. -
    6665. 75 return this.__descending;
    6666. -
    6667. },
    6668. -
    6669. nulls:function () {
    6670. -
    6671. 82 return this.__nulls;
    6672. -
    6673. }
    6674. +
    6675. 2 !Dataset && (Dataset = require("./dataset"));
    6676. +
    6677. 2 ds = ds || new Dataset();
    6678. +
    6679. 2 return ds.negativeBooleanConstantSql(this.__constant);
    6680. }
    6681. -
    6682. },
    6683. -
    6684. static:{
    6685. -
    6686. /**@lends patio.sql.OrderedExpression*/
    6687. -
    6688. /**
    6689. -
    6690. * Hash that contains the inversions for "first" and "last".
    6691. -
    6692. * @type Object
    6693. -
    6694. * @default {first:"last", last:"first"}
    6695. -
    6696. */
    6697. -
    6698. INVERT_NULLS:{first:"last", last:"first"}
    6699. }
    6700. -
    6701. }).as(sql, "OrderedExpression");
    6702. +
    6703. }).as(sql, "NegativeBooleanConstant");
    6704. -
    6705. 1var QualifiedIdentifier = define([GenericExpression, QualifyingMethods], {
    6706. +
    6707. /**
    6708. +
    6709. * @namespace Holds default generic constants that can be referenced. These
    6710. +
    6711. * are included in {@link patio}
    6712. +
    6713. * @name Constants
    6714. +
    6715. * @memberOf patio.sql
    6716. +
    6717. */
    6718. +
    6719. 1sql.Constants = {
    6720. +
    6721. /**@lends patio.sql.Constants*/
    6722. +
    6723. +
    6724. /**
    6725. +
    6726. * Constant for CURRENT DATE
    6727. +
    6728. * @type patio.sql.Constant
    6729. +
    6730. */
    6731. +
    6732. CURRENT_DATE:new Constant("CURRENT_DATE"),
    6733. +
    6734. +
    6735. /**
    6736. +
    6737. * Constant for CURRENT TIME
    6738. +
    6739. * @type patio.sql.Constant
    6740. +
    6741. */
    6742. +
    6743. CURRENT_TIME:new Constant("CURRENT_TIME"),
    6744. +
    6745. +
    6746. /**
    6747. +
    6748. * Constant for CURRENT TIMESTAMP
    6749. +
    6750. * @type patio.sql.Constant
    6751. +
    6752. */
    6753. +
    6754. CURRENT_TIMESTAMP:new Constant("CURRENT_TIMESTAMP"),
    6755. +
    6756. +
    6757. /**
    6758. +
    6759. * Constant for TRUE
    6760. +
    6761. * @type patio.sql.BooleanConstant
    6762. +
    6763. */
    6764. +
    6765. SQLTRUE:new BooleanConstant(1),
    6766. +
    6767. +
    6768. /**
    6769. +
    6770. * Constant for TRUE
    6771. +
    6772. * @type patio.sql.BooleanConstant
    6773. +
    6774. */
    6775. +
    6776. TRUE:new BooleanConstant(1),
    6777. +
    6778. +
    6779. /**
    6780. +
    6781. * Constant for FALSE.
    6782. +
    6783. * @type patio.sql.BooleanConstant
    6784. +
    6785. */
    6786. +
    6787. SQLFALSE:new BooleanConstant(0),
    6788. +
    6789. +
    6790. /**
    6791. +
    6792. * Constant for FALSE
    6793. +
    6794. * @type patio.sql.BooleanConstant
    6795. +
    6796. */
    6797. +
    6798. FALSE:new BooleanConstant(0),
    6799. +
    6800. /**
    6801. +
    6802. * Constant for NULL
    6803. +
    6804. * @type patio.sql.BooleanConstant
    6805. +
    6806. */
    6807. +
    6808. NULL:new BooleanConstant(null),
    6809. +
    6810. +
    6811. /**
    6812. +
    6813. * Constant for NOT NULL
    6814. +
    6815. * @type patio.sql.NegativeBooleanConstant
    6816. +
    6817. */
    6818. +
    6819. NOTNULL:new NegativeBooleanConstant(null)
    6820. +
    6821. +
    6822. };
    6823. +
    6824. +
    6825. 1var Constants = sql.Constants
    6826. +
    6827. +
    6828. 1var Identifier = define([GenericExpression, QualifyingMethods], {
    6829. instance:{
    6830. -
    6831. /**@lends patio.sql.QualifiedIdentifier.prototype*/
    6832. +
    6833. /**@lends patio.sql.Identifier.prototype*/
    6834. /**
    6835. -
    6836. * Represents a qualified identifier (column with table or table with schema).
    6837. +
    6838. * Represents an identifier (column or table). Can be used
    6839. +
    6840. * to specify a String with multiple underscores that should not be
    6841. +
    6842. * split, or for creating an implicit identifier without using a String.
    6843. *
    6844. * @constructs
    6845. * @augments patio.sql.GenericExpression
    6846. * @augments patio.sql.QualifyingMethods
    6847. *
    6848. -
    6849. * @param table the table or schema to qualify the column or table to.
    6850. -
    6851. * @param column the column or table to qualify.
    6852. +
    6853. * @param {String}value the identifier.
    6854. *
    6855. -
    6856. * @property table <b>READ ONLY</b> the table or schema to qualify the column or table to.
    6857. -
    6858. * @property column <b>READ ONLY</b> he column or table to qualify.
    6859. +
    6860. * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
    6861. */
    6862. -
    6863. constructor:function (table, column) {
    6864. -
    6865. 4451 this.__table = table;
    6866. -
    6867. 4451 this.__column = column;
    6868. +
    6869. constructor:function (value) {
    6870. +
    6871. 16414 this.__value = value;
    6872. },
    6873. /**
    6874. -
    6875. * Converts the {@link patio.sql.QualifiedIdentifier} to a string.
    6876. +
    6877. * Converts the {@link patio.sql.Identifier} to a string.
    6878. *
    6879. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    6880. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    6881. *
    6882. -
    6883. * @return String the SQL version of the {@link patio.sql.QualifiedIdentifier}.
    6884. +
    6885. * @return String the SQL version of the {@link patio.sql.Identifier}.
    6886. */
    6887. toString:function (ds) {
    6888. -
    6889. 4532 !Dataset && (Dataset = require("./dataset"));
    6890. -
    6891. 4532 ds = ds || new Dataset();
    6892. -
    6893. 4532 return ds.qualifiedIdentifierSql(this);
    6894. +
    6895. 21906 !Dataset && (Dataset = require("./dataset"));
    6896. +
    6897. 21906 ds = ds || new Dataset();
    6898. +
    6899. 21906 return ds.quoteIdentifier(this);
    6900. },
    6901. /**@ignore*/
    6902. getters:{
    6903. -
    6904. table:function () {
    6905. -
    6906. 4554 return this.__table;
    6907. -
    6908. },
    6909. -
    6910. -
    6911. column:function () {
    6912. -
    6913. 4559 return this.__column;
    6914. +
    6915. value:function () {
    6916. +
    6917. 25154 return this.__value;
    6918. }
    6919. }
    6920. }
    6921. -
    6922. }).as(sql, "QualifiedIdentifier");
    6923. -
    6924. +
    6925. }).as(sql, "Identifier");
    6926. -
    6927. 1var likeElement = function (re) {
    6928. -
    6929. 875 var ret;
    6930. -
    6931. 875 if (isRegExp(re)) {
    6932. -
    6933. 80 ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase]
    6934. -
    6935. } else {
    6936. -
    6937. 795 ret = [re, false, false];
    6938. -
    6939. }
    6940. -
    6941. 875 return ret;
    6942. -
    6943. };
    6944. -
    6945. /**
    6946. -
    6947. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    6948. -
    6949. * in a text/string/varchar value in SQL.
    6950. -
    6951. *
    6952. -
    6953. * @augments patio.sql.ComplexExpression
    6954. -
    6955. * @augments patio.sql.StringMethods
    6956. -
    6957. * @augments patio.sql.StringConcatenationMethods
    6958. -
    6959. * @augments patio.sql.InequalityMethods
    6960. -
    6961. * @augments patio.sql.NoBooleanInputMethods
    6962. -
    6963. * @name StringExpression
    6964. -
    6965. * @memberOf patio.sql
    6966. -
    6967. */
    6968. -
    6969. 1var StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {
    6970. -
    6971. static:{
    6972. -
    6973. /**@lends patio.sql.StringExpression*/
    6974. +
    6975. 1var JoinClause = define(Expression, {
    6976. +
    6977. instance:{
    6978. +
    6979. /**@lends patio.sql.JoinClause.prototype*/
    6980. /**
    6981. -
    6982. * <p>Creates a SQL pattern match expression. left (l) is the SQL string we
    6983. -
    6984. * are matching against, and ces are the patterns we are matching.
    6985. -
    6986. * The match succeeds if any of the patterns match (SQL OR).</p>
    6987. +
    6988. * Represents an SQL JOIN clause, used for joining tables.
    6989. +
    6990. * Created by {@link patio.Dataset} join methods.
    6991. +
    6992. * @constructs
    6993. +
    6994. * @augments patio.sql.Expression
    6995. *
    6996. -
    6997. * <p>If a regular expression is used as a pattern, an SQL regular expression will be
    6998. -
    6999. * used, which is currently only supported on MySQL and PostgreSQL. Be aware
    7000. -
    7001. * that MySQL and PostgreSQL regular expression syntax is similar to javascript
    7002. -
    7003. * regular expression syntax, but it not exactly the same, especially for
    7004. -
    7005. * advanced regular expression features. Patio just uses the source of the
    7006. -
    7007. * regular expression verbatim as the SQL regular expression string.</p>
    7008. +
    7009. * @param {String} joinType the type of join this JoinClause should use
    7010. +
    7011. * @param table the table to join with
    7012. +
    7013. * @param tableAlias the alias to use for this join clause
    7014. *
    7015. -
    7016. * <p>If any other object is used as a regular expression, the SQL LIKE operator will
    7017. -
    7018. * be used, and should be supported by most databases.</p>
    7019. +
    7020. * @property {String} joinType <b>READ ONLY</b> the type of join this JoinClause should use
    7021. +
    7022. * @property table <b>READ ONLY</b> the table to join with
    7023. +
    7024. * @property joinType <b>READ ONLY</b> the alias to use for this join clause
    7025. +
    7026. * */
    7027. +
    7028. constructor:function (joinType, table, tableAlias) {
    7029. +
    7030. 791 this.__joinType = joinType;
    7031. +
    7032. 791 this.__table = table;
    7033. +
    7034. 791 this.__tableAlias = tableAlias || null;
    7035. +
    7036. },
    7037. +
    7038. +
    7039. /**
    7040. +
    7041. * Converts the {@link patio.sql.JoinClause} to a string.
    7042. *
    7043. -
    7044. * <p>The pattern match will be case insensitive if the last argument is a hash
    7045. -
    7046. * with a key of caseInsensitive that is not false or null. Also,
    7047. -
    7048. * if a case insensitive regular expression is used (//i), that particular
    7049. -
    7050. * pattern which will always be case insensitive.</p>
    7051. +
    7052. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    7053. +
    7054. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    7055. *
    7056. -
    7057. * @example
    7058. -
    7059. * StringExpression.like(sql.a, 'a%') //=> "a" LIKE 'a%'
    7060. -
    7061. * StringExpression.like(sql.a, 'a%', {caseInsensitive : true}) //=> "a" ILIKE 'a%'
    7062. -
    7063. * StringExpression.like(sql.a, 'a%', /^a/i) //=> "a" LIKE 'a%' OR "a" ~* '^a'
    7064. +
    7065. * @return String the SQL version of the {@link patio.sql.JoinClause}.
    7066. */
    7067. -
    7068. like:function (l) {
    7069. -
    7070. 437 var args = argsToArray(arguments, 1);
    7071. -
    7072. 437 var params = likeElement(l);
    7073. -
    7074. 437 var likeMap = this.likeMap;
    7075. -
    7076. 437 var lh = params[0], lre = params[1], lci = params[2];
    7077. -
    7078. 437 var last = args[args.length - 1];
    7079. -
    7080. 437 lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;
    7081. -
    7082. 437 args = args.map(function (ce) {
    7083. -
    7084. 438 var r, rre, rci;
    7085. -
    7086. 438 var ceArr = likeElement(ce);
    7087. -
    7088. 438 r = ceArr[0], rre = ceArr[1], rci = ceArr[2];
    7089. -
    7090. 438 return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r)
    7091. -
    7092. }, this);
    7093. -
    7094. 437 return args.length == 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));
    7095. +
    7096. toString:function (ds) {
    7097. +
    7098. 17 !Dataset && (Dataset = require("./dataset"));
    7099. +
    7100. 17 ds = ds || new Dataset();
    7101. +
    7102. 17 return ds.joinClauseSql(this);
    7103. },
    7104. -
    7105. /**
    7106. -
    7107. * Like map used to by {@link patio.sql.StringExpression.like} to create the
    7108. -
    7109. * LIKE expression.
    7110. -
    7111. * @type Object
    7112. -
    7113. */
    7114. -
    7115. likeMap:{"truetrue":'~*', "truefalse":"~", "falsetrue":"ILIKE", "falsefalse":"LIKE"}
    7116. +
    7117. /**@ignore*/
    7118. +
    7119. getters:{
    7120. +
    7121. joinType:function () {
    7122. +
    7123. 927 return this.__joinType;
    7124. +
    7125. },
    7126. +
    7127. table:function () {
    7128. +
    7129. 928 return this.__table;
    7130. +
    7131. },
    7132. +
    7133. tableAlias:function () {
    7134. +
    7135. 926 return this.__tableAlias;
    7136. +
    7137. }
    7138. +
    7139. }
    7140. }
    7141. -
    7142. }).as(sql, "StringExpression");
    7143. +
    7144. }).as(sql, "JoinClause");
    7145. -
    7146. 1var SubScript = define(GenericExpression, {
    7147. -
    7148. instance:{
    7149. -
    7150. /**@lends patio.sql.SubScript.prototype*/
    7151. +
    7152. 1var JoinOnClause = define(JoinClause, {
    7153. +
    7154. instance:{
    7155. +
    7156. /**@lends patio.sql.JoinOnClause.prototype*/
    7157. /**
    7158. -
    7159. * Represents an SQL array access, with multiple possible arguments.
    7160. +
    7161. * Represents an SQL JOIN clause with ON conditions. Created by {@link patio.Dataset} join methods.
    7162. +
    7163. * See {@link patio.sql.JoinClause} for other argument parameters.
    7164. * @constructs
    7165. -
    7166. * @augments patio.sql.GenericExpression
    7167. +
    7168. * @augments patio.sql.JoinClause
    7169. *
    7170. -
    7171. * @param arrCol the SQL array column
    7172. -
    7173. * @param sub The array of subscripts to use (should be an array of numbers)
    7174. -
    7175. */
    7176. -
    7177. constructor:function (arrCol, sub) {
    7178. -
    7179. //The SQL array column
    7180. -
    7181. 109 this.__arrCol = arrCol;
    7182. -
    7183. //The array of subscripts to use (should be an array of numbers)
    7184. -
    7185. 109 this.__sub = sub;
    7186. -
    7187. },
    7188. -
    7189. -
    7190. /**
    7191. -
    7192. * Create a new {@link patio.sql.Subscript} appending the given subscript(s)
    7193. -
    7194. * the the current array of subscripts.
    7195. +
    7196. * @param on the expression to filter with. See {@link patio.Dataset#filter}
    7197. +
    7198. * @property on <b>READ ONLY</b> the filter to use with joining the datasets.
    7199. */
    7200. -
    7201. addSub:function (sub) {
    7202. -
    7203. 0 return new SubScript(this.__arrCol, this.__sub.concat(sub));
    7204. +
    7205. constructor:function (on, joinType, table, tableAlias) {
    7206. +
    7207. 761 this.__on = on;
    7208. +
    7209. 761 this._super(arguments, [joinType, table, tableAlias]);
    7210. },
    7211. /**
    7212. -
    7213. * Converts the {@link patio.sql.SubScript} to a string.
    7214. +
    7215. * Converts the {@link patio.sql.JoinOnClause} to a string.
    7216. *
    7217. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    7218. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    7219. *
    7220. -
    7221. * @return String the SQL version of the {@link patio.sql.SubScript}.
    7222. +
    7223. * @return String the SQL version of the {@link patio.sql.JoinOnClause}.
    7224. */
    7225. toString:function (ds) {
    7226. -
    7227. 109 !Dataset && (Dataset = require("./dataset"));
    7228. -
    7229. 109 ds = ds || new Dataset();
    7230. -
    7231. 109 return ds.subscriptSql(this);
    7232. +
    7233. 813 !Dataset && (Dataset = require("./dataset"));
    7234. +
    7235. 813 ds = ds || new Dataset();
    7236. +
    7237. 813 return ds.joinOnClauseSql(this);
    7238. },
    7239. /**@ignore*/
    7240. getters:{
    7241. -
    7242. f:function () {
    7243. -
    7244. 110 return this.__arrCol;
    7245. -
    7246. },
    7247. -
    7248. -
    7249. sub:function () {
    7250. -
    7251. 110 return this.__sub;
    7252. +
    7253. on:function () {
    7254. +
    7255. 813 return this.__on;
    7256. }
    7257. }
    7258. }
    7259. -
    7260. }).as(sql, "SubScript");
    7261. -
    7262. -
    7263. 1var STRING_METHODS = ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "quote",
    7264. -
    7265. "replace", "search", "slice", "split", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase",
    7266. -
    7267. "toSource", "toString", "toUpperCase", "trim", "trimLeft", "trimRight", "valueOf"];
    7268. -
    7269. +
    7270. }).as(sql, "JoinOnClause");
    7271. -
    7272. 1var addStringMethod = function (op) {
    7273. -
    7274. 24 return function () {
    7275. -
    7276. 4294 return this.__str[op].apply(this.__str, arguments);
    7277. -
    7278. }
    7279. -
    7280. };
    7281. -
    7282. 1var LiteralString = define([OrderedMethods, ComplexExpressionMethods, BooleanMethods, NumericMethods, StringMethods, InequalityMethods, AliasMethods], {
    7283. +
    7284. 1var JoinUsingClause = define(JoinClause, {
    7285. instance:{
    7286. -
    7287. /**@lends patio.sql.LiteralString*/
    7288. +
    7289. /**@lends patio.sql.JoinUsingClause.prototype*/
    7290. /**
    7291. -
    7292. * Represents a string that should be placed into a SQL query literally.
    7293. -
    7294. * <b>This class has all methods that a normal javascript String has.</b>
    7295. +
    7296. * Represents an SQL JOIN clause with USING conditions.
    7297. +
    7298. * Created by {@link patio.Dataset} join methods.
    7299. +
    7300. * See {@link patio.sql.JoinClause} for other argument parameters.
    7301. +
    7302. *
    7303. * @constructs
    7304. -
    7305. * @augments patio.sql.OrderedMethods
    7306. -
    7307. * @augments patio.sql.ComplexExpressionMethods
    7308. -
    7309. * @augments patio.sql.BooleanMethods
    7310. -
    7311. * @augments patio.sql.NumericMethods
    7312. -
    7313. * @augments patio.sql.StringMethods
    7314. -
    7315. * @augments patio.sql.InequalityMethods
    7316. -
    7317. * @augments patio.sql.AliasMethods
    7318. +
    7319. * @augments patio.sql.JoinClause
    7320. *
    7321. -
    7322. * @param {String} str the literal string.
    7323. +
    7324. * @param using the column/s to use when joining.
    7325. +
    7326. * @property using <b>READ ONLY</b> the column/s to use when joining.
    7327. */
    7328. -
    7329. constructor:function (str) {
    7330. -
    7331. 3677 this.__str = str;
    7332. -
    7333. }
    7334. -
    7335. }
    7336. -
    7337. }).as(sql, "LiteralString");
    7338. -
    7339. -
    7340. 1STRING_METHODS.forEach(function (op) {
    7341. -
    7342. 24 LiteralString.prototype[op] = addStringMethod(op);
    7343. -
    7344. }, this);
    7345. -
    7346. -
    7347. -
    7348. -
    7349. -
    7350. -
    7351. -
    7352. -
    7353. -
    7354. -
    7355. -
    7356. -
    7357. +
    7358. constructor:function (using, joinType, table, tableAlias) {
    7359. +
    7360. 8 this.__using = using.map(function (u) {
    7361. +
    7362. 9 return isString(u) ? new Identifier(u) : u;
    7363. +
    7364. });
    7365. +
    7366. 8 this._super(arguments, [joinType, table, tableAlias]);
    7367. +
    7368. },
    7369. +
    7370. /**
    7371. +
    7372. * Converts the {@link patio.sql.JoinUsingClause} to a string.
    7373. +
    7374. *
    7375. +
    7376. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    7377. +
    7378. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    7379. +
    7380. *
    7381. +
    7382. * @return String the SQL version of the {@link patio.sql.JoinUsingClause}.
    7383. +
    7384. */
    7385. +
    7386. toString:function (ds) {
    7387. +
    7388. 95 !Dataset && (Dataset = require("./dataset"));
    7389. +
    7390. 95 ds = ds || new Dataset();
    7391. +
    7392. 95 return ds.joinUsingClauseSql(this);
    7393. +
    7394. },
    7395. -
    -
    - -
    - - - - - -
    -
    migration.js
    -
    -
    - Coverage91.97 - SLOC607 - LOC274 - Missed22 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. hitch = comb.hitch,
    4. -
    5. Promise = comb.Promise,
    6. -
    7. errors = require("./errors"),
    8. -
    9. MigrationError = errors.MigrationError,
    10. -
    11. NotImplemented = errors.NotImplemented(),
    12. -
    13. format = comb.string.format,
    14. -
    15. define = comb.define,
    16. -
    17. isFunction = comb.isFunction,
    18. -
    19. serial = comb.serial,
    20. -
    21. isNumber = comb.isNumber,
    22. -
    23. when = comb.when,
    24. -
    25. isUndefined = comb.isUndefined,
    26. -
    27. fs = require("fs"),
    28. -
    29. path = require("path");
    30. +
    31. /**@ignore*/
    32. +
    33. getters:{
    34. +
    35. using:function () {
    36. +
    37. 95 return this.__using;
    38. +
    39. }
    40. +
    41. }
    42. +
    43. }
    44. +
    45. }).as(sql, "JoinUsingClause");
    46. -
    47. 1var Migrator = define(null, {
    48. +
    49. 1var PlaceHolderLiteralString = define(GenericExpression, {
    50. instance:{
    51. -
    52. /**@lends patio.migrations.Migrator.prototype*/
    53. -
    54. column:null,
    55. -
    56. db:null,
    57. -
    58. directory:null,
    59. -
    60. ds:null,
    61. -
    62. files:null,
    63. -
    64. table:null,
    65. -
    66. target:null,
    67. +
    68. /**@lends patio.sql.PlaceHolderLiteralString.prototype*/
    69. /**
    70. -
    71. * Abstract Migrator class. This class should be be instantiated directly.
    72. +
    73. * Represents a literal string with placeholders and arguments.
    74. +
    75. * This is necessary to ensure delayed literalization of the arguments
    76. +
    77. * required for the prepared statement support and for database-specific
    78. +
    79. * literalization.
    80. *
    81. * @constructs
    82. -
    83. * @param {patio.Database} db the database to migrate
    84. -
    85. * @param {String} directory directory that the migration files reside in
    86. -
    87. * @param {Object} [opts={}] optional parameters.
    88. -
    89. * @param {String} [opts.column] the column in the table that version information should be stored.
    90. -
    91. * @param {String} [opts.table] the table that version information should be stored.
    92. -
    93. * @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
    94. -
    95. * @param {String} [opts.current] the version that the database is currently at if the current version
    96. +
    97. * @augments patio.sql.GenericExpression
    98. +
    99. *
    100. +
    101. * @param {String} str the string that contains placeholders.
    102. +
    103. * @param {Array} args array of arguments that will be literalized using {@link patio.Dataset#literal}, and
    104. +
    105. * replaced in the string.
    106. +
    107. * @param {Boolean} [parens=false] set to true to wrap the string in parens.
    108. +
    109. *
    110. +
    111. * @property {String} str <b>READ ONLY</b> the string that contains placeholders.
    112. +
    113. * @property {Array} args <b>READ ONLY</b> array of arguments that will be literalized using {@link patio.Dataset#literal}, and
    114. +
    115. * replaced in the string.
    116. +
    117. * @property {String} parens <b>READ ONLY</b> set to true to wrap the string in parens.
    118. */
    119. -
    120. constructor:function (db, directory, opts) {
    121. -
    122. 31 this.db = db;
    123. -
    124. 31 this.directory = directory;
    125. -
    126. 31 opts = opts || {};
    127. -
    128. 31 this.table = opts.table || this._static.DEFAULT_SCHEMA_TABLE;
    129. -
    130. 31 this.column = opts.column || this._static.DEFAULT_SCHEMA_COLUMN;
    131. -
    132. 31 this._opts = opts;
    133. +
    134. constructor:function (str, args, parens) {
    135. +
    136. 53 parens = parens || false;
    137. +
    138. 53 var v;
    139. +
    140. 53 this.__str = str;
    141. +
    142. 53 this.__args = isArray(args) && args.length == 1 && isHash((v = args[0])) ? v : args;
    143. +
    144. 53 this.__parens = parens;
    145. },
    146. /**
    147. -
    148. * Runs the migration and returns a promise.
    149. +
    150. * Converts the {@link patio.sql.PlaceHolderLiteralString} to a string.
    151. +
    152. *
    153. +
    154. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    155. +
    156. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    157. +
    158. *
    159. +
    160. * @return String the SQL version of the {@link patio.sql.PlaceHolderLiteralString}.
    161. */
    162. -
    163. run:function () {
    164. -
    165. 0 throw new NotImplemented("patio.migrations.Migrator#run");
    166. +
    167. toString:function (ds) {
    168. +
    169. 52 !Dataset && (Dataset = require("./dataset"));
    170. +
    171. 52 ds = ds || new Dataset();
    172. +
    173. 52 return ds.placeholderLiteralStringSql(this);
    174. },
    175. -
    176. getFileNames:function () {
    177. -
    178. 50 if (!this.__files) {
    179. -
    180. 49 return this._static.getFileNames(this.directory).addCallback(hitch(this, function (files) {
    181. -
    182. 49 this.__files = files;
    183. -
    184. }));
    185. -
    186. } else {
    187. -
    188. 1 return new Promise().callback(this.__files).promise();
    189. +
    190. /**@ignore*/
    191. +
    192. getters:{
    193. +
    194. str:function () {
    195. +
    196. 55 return this.__str;
    197. +
    198. },
    199. +
    200. args:function () {
    201. +
    202. 55 return this.__args;
    203. +
    204. },
    205. +
    206. +
    207. parens:function () {
    208. +
    209. 55 return this.__parens;
    210. }
    211. -
    212. },
    213. -
    214. getMigrationVersionFromFile:function (filename) {
    215. -
    216. 476 return parseInt(path.basename(filename).split(this._static.MIGRATION_SPLITTER)[0], 10);
    217. }
    218. -
    219. },
    220. -
    221. -
    222. "static":{
    223. -
    224. /**@lends patio.migrations.Migrator*/
    225. +
    226. }
    227. +
    228. }).as(sql, "PlaceHolderLiteralString");
    229. -
    230. MIGRATION_FILE_PATTERN:/^\d+\..+\.js$/i,
    231. -
    232. MIGRATION_SPLITTER:'.',
    233. -
    234. MINIMUM_TIMESTAMP:20000101,
    235. -
    236. getFileNames:function (directory) {
    237. -
    238. 80 var ret = new Promise();
    239. -
    240. 80 fs.readdir(directory, hitch(this, function (err, files) {
    241. -
    242. 80 if (err) {
    243. -
    244. 0 ret.errback(err);
    245. -
    246. } else {
    247. -
    248. 80 files = files.filter(function (file) {
    249. -
    250. 394 return file.match(this.MIGRATION_FILE_PATTERN) !== null;
    251. -
    252. }, this);
    253. -
    254. 80 ret.callback(files.map(function (file) {
    255. -
    256. 394 return path.resolve(directory, file);
    257. -
    258. }));
    259. -
    260. }
    261. -
    262. }));
    263. -
    264. 80 return ret.promise();
    265. -
    266. },
    267. +
    268. 1var SQLFunction = define(GenericExpression, {
    269. +
    270. instance:{
    271. +
    272. /**@lends patio.sql.SQLFunction.prototype*/
    273. /**
    274. -
    275. * Migrates the database using migration files found in the supplied directory.
    276. -
    277. * See {@link patio#migrate}
    278. +
    279. * Represents an SQL function call.
    280. *
    281. -
    282. * @example
    283. -
    284. * var DB = patio.connect("my://connection/string");
    285. -
    286. * patio. migrate(DB, __dirname + "/timestamp_migration").then(function(){
    287. -
    288. * console.log("done migrating!");
    289. -
    290. * });
    291. +
    292. * @constructs
    293. +
    294. * @augments patio.sql.GenericExpression
    295. *
    296. -
    297. * patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).then(function(){
    298. -
    299. * console.log("done migrating down!");
    300. -
    301. * });
    302. +
    303. * @param {...} f variable number of arguments where the first argument is the name
    304. +
    305. * of the SQL function to invoke. The rest of the arguments will be literalized through
    306. +
    307. * {@link patio.Dataset#literal} and placed into the SQL function call.
    308. *
    309. +
    310. * @property {String} f <b>READ ONLY</b> the SQL function to call.
    311. +
    312. * @property {Array} args <b>READ ONLY</b> args arguments will be literalized through
    313. +
    314. * {@link patio.Dataset#literal} and placed into the SQL function call.
    315. +
    316. * */
    317. +
    318. constructor:function (f) {
    319. +
    320. 1109 var args = argsToArray(arguments).slice(1);
    321. +
    322. 1109 this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {
    323. +
    324. 773 return isString(a) ? sql.stringToIdentifier(a) : a;
    325. +
    326. });
    327. +
    328. },
    329. +
    330. +
    331. /**
    332. +
    333. * Converts the {@link patio.sql.SQLFunction} to a string.
    334. *
    335. -
    336. * @param {patio.Database} db the database to migrate
    337. -
    338. * @param {String} directory directory that the migration files reside in
    339. -
    340. * @param {Object} [opts={}] optional parameters.
    341. -
    342. * @param {String} [opts.column] the column in the table that version information should be stored.
    343. -
    344. * @param {String} [opts.table] the table that version information should be stored.
    345. -
    346. * @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
    347. -
    348. * @param {String} [opts.current] the version that the database is currently at if the current version
    349. -
    350. * is not provided it is retrieved from the database.
    351. +
    352. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    353. +
    354. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    355. *
    356. -
    357. * @return {Promise} a promise that is resolved once the migration is complete.
    358. +
    359. * @return String the SQL version of the {@link patio.sql.SQLFunction}.
    360. */
    361. -
    362. run:function (db, directory, opts, cb) {
    363. -
    364. 31 if (isFunction(opts)) {
    365. -
    366. 0 cb = opts;
    367. -
    368. 0 opts = {};
    369. -
    370. } else {
    371. -
    372. 31 opts = opts || {};
    373. -
    374. }
    375. -
    376. 31 opts = opts || {};
    377. -
    378. 31 var ret = new Promise();
    379. -
    380. 31 this.__getMigrator(directory).then(function (migrator) {
    381. -
    382. 31 new migrator(db, directory, opts).run().then(ret);
    383. -
    384. }, ret);
    385. -
    386. 31 ret.classic(cb);
    387. -
    388. 31 return ret.promise();
    389. +
    390. toString:function (ds) {
    391. +
    392. 565 !Dataset && (Dataset = require("./dataset"));
    393. +
    394. 565 ds = ds || new Dataset();
    395. +
    396. 565 return ds.functionSql(this);
    397. },
    398. -
    399. // Choose the Migrator subclass to use. Uses the TimestampMigrator
    400. -
    401. // // if the version number appears to be a unix time integer for a year
    402. -
    403. // after 2005, otherwise uses the IntegerMigrator.
    404. -
    405. __getMigrator:function (directory) {
    406. -
    407. 31 var ret = new Promise();
    408. -
    409. 31 var retClass = IntegerMigrator;
    410. -
    411. 31 this.getFileNames(directory).then(hitch(this, function (files) {
    412. -
    413. 31 var l = files.length;
    414. -
    415. 31 if (l) {
    416. -
    417. 31 for (var i = 0; i < l; i++) {
    418. -
    419. 81 var file = files[i];
    420. -
    421. 81 if (parseInt(path.basename(file).split(this.MIGRATION_SPLITTER)[0], 10) > this.MINIMUM_TIMESTAMP) {
    422. -
    423. 18 retClass = TimestampMigrator;
    424. -
    425. 18 break;
    426. -
    427. }
    428. -
    429. }
    430. -
    431. }
    432. -
    433. 31 ret.callback(retClass);
    434. +
    435. /**@ignore*/
    436. +
    437. getters:{
    438. +
    439. f:function () {
    440. +
    441. 567 return this.__f;
    442. +
    443. },
    444. -
    445. }), ret);
    446. -
    447. 31 return ret.promise();
    448. +
    449. args:function () {
    450. +
    451. 567 return this.__args;
    452. +
    453. }
    454. }
    455. }
    456. -
    457. });
    458. -
    459. +
    460. }).as(sql, "SQLFunction");
    461. /**
    462. -
    463. * @class Migrator that uses the file format {migrationName}.{version}.js, where version starts at 0.
    464. -
    465. * <b>Missing migrations are not allowed</b>
    466. +
    467. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    468. +
    469. * in a numeric value in SQL.
    470. *
    471. -
    472. * @augments patio.migrations.Migrator
    473. -
    474. * @name IntegerMigrator
    475. -
    476. * @memberOf patio.migrations
    477. +
    478. * @name NumericExpression
    479. +
    480. * @memberOf patio.sql
    481. +
    482. * @augments patio.sql.ComplexExpression
    483. +
    484. * @augments patio.sql.BitWiseMethods
    485. +
    486. * @augments patio.sql.NumericMethods
    487. +
    488. * @augments patio.sql.InequalityMethods
    489. */
    490. -
    491. 1var IntegerMigrator = define(Migrator, {
    492. -
    493. instance:{
    494. -
    495. /**@lends patio.migrations.IntegerMigrator.prototype*/
    496. -
    497. current:null,
    498. -
    499. direction:null,
    500. -
    501. migrations:null,
    502. +
    503. 1var NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");
    504. -
    505. _migrationFiles:null,
    506. -
    507. run:function () {
    508. -
    509. 13 var ret = new Promise(), DB = this.db;
    510. -
    511. 13 serial([this._getLatestMigrationVersion.bind(this), this._getCurrentMigrationVersion.bind(this)]).then(hitch(this, function (res) {
    512. -
    513. 11 var target = res[0], current = res[1];
    514. -
    515. 11 if (current !== target) {
    516. -
    517. 11 var direction = this.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;
    518. -
    519. 11 this._getMigrations(current, target, direction).then(hitch(this, function (migrations) {
    520. -
    521. 11 var runMigration = hitch(this, function (index) {
    522. -
    523. 60 if (index >= migrations.length) {
    524. -
    525. 11 ret.callback(version);
    526. -
    527. } else {
    528. -
    529. 49 var curr = migrations[index], migration = curr[0];
    530. -
    531. 49 version = curr[1];
    532. -
    533. 49 var now = new Date();
    534. -
    535. 49 var lv = isUp ? version : version - 1;
    536. -
    537. 49 DB.logInfo("Begin applying migration version %d, direction: %s", lv, direction);
    538. -
    539. 49 DB.transaction(hitch(this, function () {
    540. -
    541. 49 var ret = new Promise();
    542. -
    543. 49 if (!isFunction(migration[direction])) {
    544. -
    545. 0 this._setMigrationVersion(lv).then(ret);
    546. -
    547. } else {
    548. -
    549. 49 when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
    550. -
    551. 49 this._setMigrationVersion(lv).then(ret);
    552. -
    553. }), ret);
    554. -
    555. }
    556. -
    557. 49 return ret.promise();
    558. -
    559. })).then(function () {
    560. -
    561. 49 DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);
    562. -
    563. 49 runMigration(index + 1);
    564. -
    565. }, ret);
    566. -
    567. }
    568. +
    569. 1var OrderedExpression = define(Expression, {
    570. +
    571. instance:{
    572. +
    573. /**@lends patio.sql.OrderedExpression.prototype*/
    574. -
    575. });
    576. -
    577. 11 runMigration(0);
    578. -
    579. }), ret);
    580. -
    581. } else {
    582. -
    583. 0 ret.callback(target);
    584. -
    585. }
    586. +
    587. /**
    588. +
    589. * Represents a column/expression to order the result set by.
    590. +
    591. * @constructs
    592. +
    593. * @augments patio.sql.Expression
    594. +
    595. *
    596. +
    597. * @param expression the expression to order
    598. +
    599. * @param {Boolean}[descending=true] set to false to order ASC
    600. +
    601. * @param {String|Object} [opts=null] additional options
    602. +
    603. * <ul>
    604. +
    605. * <li>String: if value is "first" the null values will be first, if "last" then null values
    606. +
    607. * will be last</li>
    608. +
    609. * <li>Object: will pull the nulls property off of the object use use the same rules as if it
    610. +
    611. * were a string</li>
    612. +
    613. * </ul>
    614. +
    615. * @property expression <b>READ ONLY</b> the expression to order.
    616. +
    617. * @property {Boolean} [descending=true] <b>READ ONLY</b> true if decending, false otherwise.
    618. +
    619. * @property {String} [nulls=null] if value is "first" the null values will be first, if "last" then null values
    620. +
    621. * will be last
    622. +
    623. */
    624. +
    625. constructor:function (expression, descending, opts) {
    626. +
    627. 92 descending = isBoolean(descending) ? descending : true;
    628. +
    629. 92 opts = opts || {};
    630. +
    631. 92 this.__expression = expression;
    632. +
    633. 92 this.__descending = descending;
    634. +
    635. 92 var nulls = isString(opts) ? opts : opts.nulls;
    636. +
    637. 92 this.__nulls = isString(nulls) ? nulls.toLowerCase() : null;
    638. +
    639. },
    640. -
    641. }), ret);
    642. -
    643. 13 return ret.promise();
    644. +
    645. /**
    646. +
    647. * @return {patio.sql.OrderedExpression} a copy that is ordered ASC
    648. +
    649. */
    650. +
    651. asc:function () {
    652. +
    653. 0 return new OrderedExpression(this.__expression, false, {nulls:this.__nulls});
    654. },
    655. -
    656. _getMigrations:function (current, target, direction) {
    657. -
    658. 11 var ret = new Promise(), isUp = direction === "up", migrations = [];
    659. -
    660. 11 when(this._getMigrationFiles()).then(function (files) {
    661. -
    662. 11 try {
    663. -
    664. 11 if ((isUp ? target : current - 1) < files.length) {
    665. -
    666. 11 if (isUp) {
    667. -
    668. 8 current++;
    669. -
    670. }
    671. -
    672. 11 for (; isUp ? current <= target : current > target; isUp ? current++ : current--) {
    673. -
    674. 49 migrations.push([require(files[current]), current]);
    675. -
    676. }
    677. -
    678. } else {
    679. -
    680. 0 return ret.errback(new MigrationError("Invalid target " + target));
    681. -
    682. }
    683. -
    684. } catch (e) {
    685. -
    686. 0 return ret.errback(e);
    687. -
    688. }
    689. -
    690. 11 ret.callback(migrations);
    691. -
    692. }, ret);
    693. -
    694. 11 return ret.promise();
    695. +
    696. /**
    697. +
    698. * @return {patio.sql.OrderedExpression} Return a copy that is ordered DESC
    699. +
    700. */
    701. +
    702. desc:function () {
    703. +
    704. 0 return new OrderedExpression(this.__expression, true, {nulls:this.__nulls});
    705. +
    706. },
    707. +
    708. +
    709. /**
    710. +
    711. * * @return {patio.sql.OrderedExpression} an inverted expression, changing ASC to DESC and NULLS FIRST to NULLS LAST.
    712. +
    713. * */
    714. +
    715. invert:function () {
    716. +
    717. 17 return new OrderedExpression(this.__expression, !this.__descending, {nulls:this._static.INVERT_NULLS[this.__nulls] || this.__nulls});
    718. },
    719. +
    720. /**
    721. +
    722. * Converts the {@link patio.sql.OrderedExpression} to a string.
    723. +
    724. *
    725. +
    726. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    727. +
    728. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    729. +
    730. *
    731. +
    732. * @return String the SQL version of the {@link patio.sql.OrderedExpression}.
    733. +
    734. */
    735. +
    736. toString:function (ds) {
    737. +
    738. 73 !Dataset && (Dataset = require("./dataset"));
    739. +
    740. 73 ds = ds || new Dataset();
    741. +
    742. 73 return ds.orderedExpressionSql(this);
    743. +
    744. },
    745. -
    746. _getMigrationFiles:function () {
    747. -
    748. 19 var ret = new Promise();
    749. -
    750. 19 if (!this._migrationFiles) {
    751. -
    752. 13 var retFiles = [];
    753. -
    754. 13 var directory = this.directory;
    755. -
    756. 13 this.getFileNames().then(hitch(this, function (files) {
    757. -
    758. 13 var l = files.length;
    759. -
    760. 13 if (l) {
    761. -
    762. 13 for (var i = 0; i < l; i++) {
    763. -
    764. 59 var file = files[i];
    765. -
    766. 59 var version = this.getMigrationVersionFromFile(file);
    767. -
    768. 59 if (isUndefined(retFiles[version])) {
    769. -
    770. 58 retFiles[version] = file;
    771. -
    772. } else {
    773. -
    774. 1 return ret.errback(new MigrationError("Duplicate migration number " + version));
    775. -
    776. }
    777. -
    778. }
    779. -
    780. 12 if (isUndefined(retFiles[0])) {
    781. -
    782. 0 retFiles.shift();
    783. -
    784. }
    785. -
    786. 12 for (var j = 0; j < l; j++) {
    787. -
    788. 57 if (isUndefined(retFiles[j])) {
    789. -
    790. 1 return ret.errback(new MigrationError("Missing migration for " + j));
    791. -
    792. }
    793. -
    794. }
    795. -
    796. }
    797. -
    798. 11 this._migrationFiles = retFiles;
    799. -
    800. 11 ret.callback(retFiles);
    801. -
    802. }), ret);
    803. -
    804. } else {
    805. -
    806. 6 ret.callback(this._migrationFiles);
    807. +
    808. /**@ignore*/
    809. +
    810. getters:{
    811. +
    812. expression:function () {
    813. +
    814. 75 return this.__expression;
    815. +
    816. },
    817. +
    818. descending:function () {
    819. +
    820. 75 return this.__descending;
    821. +
    822. },
    823. +
    824. nulls:function () {
    825. +
    826. 82 return this.__nulls;
    827. }
    828. -
    829. 19 return ret.promise();
    830. +
    831. }
    832. +
    833. },
    834. +
    835. static:{
    836. +
    837. /**@lends patio.sql.OrderedExpression*/
    838. +
    839. /**
    840. +
    841. * Hash that contains the inversions for "first" and "last".
    842. +
    843. * @type Object
    844. +
    845. * @default {first:"last", last:"first"}
    846. +
    847. */
    848. +
    849. INVERT_NULLS:{first:"last", last:"first"}
    850. +
    851. }
    852. +
    853. }).as(sql, "OrderedExpression");
    854. +
    855. +
    856. 1var QualifiedIdentifier = define([GenericExpression, QualifyingMethods], {
    857. +
    858. instance:{
    859. +
    860. /**@lends patio.sql.QualifiedIdentifier.prototype*/
    861. +
    862. +
    863. /**
    864. +
    865. * Represents a qualified identifier (column with table or table with schema).
    866. +
    867. *
    868. +
    869. * @constructs
    870. +
    871. * @augments patio.sql.GenericExpression
    872. +
    873. * @augments patio.sql.QualifyingMethods
    874. +
    875. *
    876. +
    877. * @param table the table or schema to qualify the column or table to.
    878. +
    879. * @param column the column or table to qualify.
    880. +
    881. *
    882. +
    883. * @property table <b>READ ONLY</b> the table or schema to qualify the column or table to.
    884. +
    885. * @property column <b>READ ONLY</b> he column or table to qualify.
    886. +
    887. */
    888. +
    889. constructor:function (table, column) {
    890. +
    891. 4451 this.__table = table;
    892. +
    893. 4451 this.__column = column;
    894. },
    895. -
    896. _getLatestMigrationVersion:function () {
    897. -
    898. 13 var ret = new Promise();
    899. -
    900. 13 if (!isUndefined(this._opts.target)) {
    901. -
    902. 5 ret.callback(this._opts.target);
    903. -
    904. } else {
    905. -
    906. 8 this._getMigrationFiles().then(hitch(this, function (files) {
    907. -
    908. 6 var l = files[files.length - 1];
    909. -
    910. 6 ret.callback(l ? this.getMigrationVersionFromFile(path.basename(l)) : null);
    911. -
    912. }), ret);
    913. -
    914. }
    915. -
    916. 13 return ret.promise();
    917. +
    918. /**
    919. +
    920. * Converts the {@link patio.sql.QualifiedIdentifier} to a string.
    921. +
    922. *
    923. +
    924. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    925. +
    926. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    927. +
    928. *
    929. +
    930. * @return String the SQL version of the {@link patio.sql.QualifiedIdentifier}.
    931. +
    932. */
    933. +
    934. toString:function (ds) {
    935. +
    936. 4532 !Dataset && (Dataset = require("./dataset"));
    937. +
    938. 4532 ds = ds || new Dataset();
    939. +
    940. 4532 return ds.qualifiedIdentifierSql(this);
    941. },
    942. -
    943. _getCurrentMigrationVersion:function () {
    944. -
    945. 11 var ret = new Promise();
    946. -
    947. 11 if (!isUndefined(this._opts.current)) {
    948. -
    949. 2 ret.callback(this._opts.current);
    950. -
    951. } else {
    952. -
    953. 9 when(this._getSchemaDataset()).then(hitch(this, function (ds) {
    954. -
    955. 9 ds.get(this.column).then(ret);
    956. -
    957. }), ret);
    958. +
    959. /**@ignore*/
    960. +
    961. getters:{
    962. +
    963. table:function () {
    964. +
    965. 4554 return this.__table;
    966. +
    967. },
    968. +
    969. +
    970. column:function () {
    971. +
    972. 4559 return this.__column;
    973. }
    974. -
    975. 11 return ret.promise();
    976. -
    977. },
    978. +
    979. }
    980. +
    981. }
    982. +
    983. }).as(sql, "QualifiedIdentifier");
    984. -
    985. _setMigrationVersion:function (version) {
    986. -
    987. 49 var ret = new Promise(), c = this.column;
    988. -
    989. 49 this._getSchemaDataset().then(function (ds) {
    990. -
    991. 49 var item = {};
    992. -
    993. 49 item[c] = version;
    994. -
    995. 49 ds.update(item).both(ret);
    996. -
    997. }, ret);
    998. -
    999. 49 return ret.promise();
    1000. -
    1001. },
    1002. +
    1003. 1var likeElement = function (re) {
    1004. +
    1005. 875 var ret;
    1006. +
    1007. 875 if (isRegExp(re)) {
    1008. +
    1009. 80 ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase]
    1010. +
    1011. } else {
    1012. +
    1013. 795 ret = [re, false, false];
    1014. +
    1015. }
    1016. +
    1017. 875 return ret;
    1018. +
    1019. };
    1020. +
    1021. /**
    1022. +
    1023. * @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
    1024. +
    1025. * in a text/string/varchar value in SQL.
    1026. +
    1027. *
    1028. +
    1029. * @augments patio.sql.ComplexExpression
    1030. +
    1031. * @augments patio.sql.StringMethods
    1032. +
    1033. * @augments patio.sql.StringConcatenationMethods
    1034. +
    1035. * @augments patio.sql.InequalityMethods
    1036. +
    1037. * @augments patio.sql.NoBooleanInputMethods
    1038. +
    1039. * @name StringExpression
    1040. +
    1041. * @memberOf patio.sql
    1042. +
    1043. */
    1044. +
    1045. 1var StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {
    1046. +
    1047. static:{
    1048. +
    1049. /**@lends patio.sql.StringExpression*/
    1050. -
    1051. _getSchemaDataset:function () {
    1052. -
    1053. 58 var c = this.column, table = this.table;
    1054. -
    1055. 58 var ret = new Promise();
    1056. -
    1057. 58 if (!this.__schemaDataset) {
    1058. -
    1059. 11 var ds = this.db.from(table);
    1060. -
    1061. 11 this.__createOrAlterMigrationTable().then(hitch(this, function () {
    1062. -
    1063. 11 ds.isEmpty().then(hitch(this, function (empty) {
    1064. -
    1065. 11 if (empty) {
    1066. -
    1067. 0 var item = {};
    1068. -
    1069. 0 item[c] = -1;
    1070. -
    1071. 0 this.__schemaDataset = ds;
    1072. -
    1073. 0 ds.insert(item).then(hitch(ret, "callback", ds), ret);
    1074. -
    1075. } else {
    1076. -
    1077. 11 ds.count().then(hitch(this, function (count) {
    1078. -
    1079. 11 if (count > 1) {
    1080. -
    1081. 0 ret.errback(new Error("More than one row in migrator table"));
    1082. -
    1083. } else {
    1084. -
    1085. 11 this.__schemaDataset = ds;
    1086. -
    1087. 11 ret.callback(ds);
    1088. -
    1089. }
    1090. -
    1091. }), ret);
    1092. -
    1093. }
    1094. -
    1095. }), ret);
    1096. -
    1097. }), ret);
    1098. -
    1099. } else {
    1100. -
    1101. 47 ret.callback(this.__schemaDataset);
    1102. -
    1103. }
    1104. -
    1105. 58 return ret.promise();
    1106. +
    1107. /**
    1108. +
    1109. * <p>Creates a SQL pattern match expression. left (l) is the SQL string we
    1110. +
    1111. * are matching against, and ces are the patterns we are matching.
    1112. +
    1113. * The match succeeds if any of the patterns match (SQL OR).</p>
    1114. +
    1115. *
    1116. +
    1117. * <p>If a regular expression is used as a pattern, an SQL regular expression will be
    1118. +
    1119. * used, which is currently only supported on MySQL and PostgreSQL. Be aware
    1120. +
    1121. * that MySQL and PostgreSQL regular expression syntax is similar to javascript
    1122. +
    1123. * regular expression syntax, but it not exactly the same, especially for
    1124. +
    1125. * advanced regular expression features. Patio just uses the source of the
    1126. +
    1127. * regular expression verbatim as the SQL regular expression string.</p>
    1128. +
    1129. *
    1130. +
    1131. * <p>If any other object is used as a regular expression, the SQL LIKE operator will
    1132. +
    1133. * be used, and should be supported by most databases.</p>
    1134. +
    1135. *
    1136. +
    1137. * <p>The pattern match will be case insensitive if the last argument is a hash
    1138. +
    1139. * with a key of caseInsensitive that is not false or null. Also,
    1140. +
    1141. * if a case insensitive regular expression is used (//i), that particular
    1142. +
    1143. * pattern which will always be case insensitive.</p>
    1144. +
    1145. *
    1146. +
    1147. * @example
    1148. +
    1149. * StringExpression.like(sql.a, 'a%') //=> "a" LIKE 'a%'
    1150. +
    1151. * StringExpression.like(sql.a, 'a%', {caseInsensitive : true}) //=> "a" ILIKE 'a%'
    1152. +
    1153. * StringExpression.like(sql.a, 'a%', /^a/i) //=> "a" LIKE 'a%' OR "a" ~* '^a'
    1154. +
    1155. */
    1156. +
    1157. like:function (l) {
    1158. +
    1159. 437 var args = argsToArray(arguments, 1);
    1160. +
    1161. 437 var params = likeElement(l);
    1162. +
    1163. 437 var likeMap = this.likeMap;
    1164. +
    1165. 437 var lh = params[0], lre = params[1], lci = params[2];
    1166. +
    1167. 437 var last = args[args.length - 1];
    1168. +
    1169. 437 lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;
    1170. +
    1171. 437 args = args.map(function (ce) {
    1172. +
    1173. 438 var r, rre, rci;
    1174. +
    1175. 438 var ceArr = likeElement(ce);
    1176. +
    1177. 438 r = ceArr[0], rre = ceArr[1], rci = ceArr[2];
    1178. +
    1179. 438 return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r)
    1180. +
    1181. }, this);
    1182. +
    1183. 437 return args.length == 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));
    1184. },
    1185. -
    1186. __createOrAlterMigrationTable:function () {
    1187. -
    1188. 11 var c = this.column, table = this.table, db = this.db;
    1189. -
    1190. 11 var ds = this.db.from(table);
    1191. -
    1192. 11 var ret = new Promise();
    1193. -
    1194. 11 db.tableExists(table).then(hitch(this, function (exists) {
    1195. -
    1196. 11 if (!exists) {
    1197. -
    1198. 6 db.createTable(table,
    1199. -
    1200. function () {
    1201. -
    1202. 6 this.column(c, "integer", {"default":-1, allowNull:false});
    1203. -
    1204. }).then(ret);
    1205. -
    1206. } else {
    1207. -
    1208. 5 ds.columns.then(function (columns) {
    1209. -
    1210. 5 if (columns.indexOf(c) === -1) {
    1211. -
    1212. 1 db.addColumn(table, c, "integer", {"default":-1, allowNull:false})
    1213. -
    1214. .then(ret);
    1215. -
    1216. } else {
    1217. -
    1218. 4 ret.callback();
    1219. -
    1220. }
    1221. -
    1222. });
    1223. -
    1224. }
    1225. -
    1226. }), ret);
    1227. -
    1228. 11 return ret.promise();
    1229. -
    1230. }
    1231. +
    1232. /**
    1233. +
    1234. * Like map used to by {@link patio.sql.StringExpression.like} to create the
    1235. +
    1236. * LIKE expression.
    1237. +
    1238. * @type Object
    1239. +
    1240. */
    1241. +
    1242. likeMap:{"truetrue":'~*', "truefalse":"~", "falsetrue":"ILIKE", "falsefalse":"LIKE"}
    1243. -
    1244. },
    1245. -
    1246. static:{
    1247. -
    1248. DEFAULT_SCHEMA_COLUMN:"version",
    1249. -
    1250. DEFAULT_SCHEMA_TABLE:"schema_info"
    1251. }
    1252. -
    1253. }).as(exports, "IntegerMigrator");
    1254. -
    1255. +
    1256. }).as(sql, "StringExpression");
    1257. -
    1258. /**
    1259. -
    1260. * @class Migrator that uses the file format {migrationName}.{timestamp}.js, where the timestamp
    1261. -
    1262. * can be anything greater than 20000101.
    1263. -
    1264. *
    1265. -
    1266. * @name TimestampMigrator
    1267. -
    1268. * @augments patio.migrations.Migrator
    1269. -
    1270. * @memberOf patio.migrations
    1271. -
    1272. */
    1273. -
    1274. 1var TimestampMigrator = define(Migrator, {
    1275. +
    1276. 1var SubScript = define(GenericExpression, {
    1277. instance:{
    1278. +
    1279. /**@lends patio.sql.SubScript.prototype*/
    1280. -
    1281. constructor:function (db, directory, opts) {
    1282. -
    1283. 18 this._super(arguments);
    1284. -
    1285. 18 opts = opts || {};
    1286. -
    1287. 18 this.target = opts.target;
    1288. -
    1289. },
    1290. -
    1291. -
    1292. run:function () {
    1293. -
    1294. 18 var ret = new Promise(), DB = this.db, column = this.column;
    1295. -
    1296. 18 serial([this.__getMirationFiles.bind(this), this._getSchemaDataset.bind(this)]).then(function (res) {
    1297. -
    1298. 18 var migrations = res[0], ds = res[1];
    1299. -
    1300. 18 var runMigration = hitch(this, function (index) {
    1301. -
    1302. 86 if (index >= migrations.length) {
    1303. -
    1304. 16 ret.callback();
    1305. -
    1306. } else {
    1307. -
    1308. 70 var curr = migrations[index], file = curr[0], migration = curr[1], direction = curr[2];
    1309. -
    1310. 70 var now = new Date();
    1311. -
    1312. 70 DB.logInfo("Begin applying migration file %s, direction: %s", file, direction);
    1313. -
    1314. 70 DB.transaction(hitch(this, function () {
    1315. -
    1316. 70 var ret = new Promise();
    1317. -
    1318. 70 when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
    1319. -
    1320. 68 var fileLowerCase = file.toLowerCase();
    1321. -
    1322. 68 var query = {};
    1323. -
    1324. 68 query[column] = fileLowerCase;
    1325. -
    1326. 68 (direction === "up" ? ds.insert(query) : ds.filter(query).remove()).then(ret);
    1327. -
    1328. }), ret);
    1329. -
    1330. 68 return ret.promise();
    1331. -
    1332. })).then(function () {
    1333. -
    1334. 68 DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);
    1335. -
    1336. 68 runMigration(index + 1);
    1337. -
    1338. }, ret);
    1339. -
    1340. }
    1341. -
    1342. -
    1343. });
    1344. -
    1345. 18 runMigration(0);
    1346. -
    1347. }, ret);
    1348. -
    1349. 18 return ret.promise();
    1350. -
    1351. },
    1352. -
    1353. -
    1354. getFileNames:function () {
    1355. -
    1356. 37 var ret = new Promise();
    1357. -
    1358. 37 var sup = this._super(arguments);
    1359. -
    1360. 37 sup.then(hitch(this, function (files) {
    1361. -
    1362. 37 ret.callback(files.sort(hitch(this, function (f1, f2) {
    1363. -
    1364. 178 return this.getMigrationVersionFromFile(f1) - this.getMigrationVersionFromFile(f2);
    1365. -
    1366. })));
    1367. -
    1368. }), ret);
    1369. -
    1370. 37 return ret.promise();
    1371. -
    1372. },
    1373. -
    1374. -
    1375. __getAppliedMigrations:function () {
    1376. -
    1377. 18 var ret = new Promise();
    1378. -
    1379. 18 if (!this.__appliedMigrations) {
    1380. -
    1381. 18 this._getSchemaDataset().then(hitch(this, function (ds) {
    1382. -
    1383. 18 when(
    1384. -
    1385. ds.selectOrderMap(this.column),
    1386. -
    1387. this.getFileNames()
    1388. -
    1389. ).then(hitch(this, function (res) {
    1390. -
    1391. 18 var appliedMigrations = res[0], files = res[1].map(function (f) {
    1392. -
    1393. 92 return path.basename(f).toLowerCase();
    1394. -
    1395. });
    1396. -
    1397. 18 var l = appliedMigrations.length;
    1398. -
    1399. 18 if (l) {
    1400. -
    1401. 9 for (var i = 0; i < l; i++) {
    1402. -
    1403. 39 if (files.indexOf(appliedMigrations[i]) == -1) {
    1404. -
    1405. 0 return ret.errback("Applied migrations file not found in directory " + appliedMigrations[i]);
    1406. -
    1407. }
    1408. -
    1409. }
    1410. -
    1411. 9 this.__appliedMigrations = appliedMigrations;
    1412. -
    1413. 9 ret.callback(appliedMigrations);
    1414. -
    1415. } else {
    1416. -
    1417. 9 this.__appliedMigrations = [];
    1418. -
    1419. 9 ret.callback([]);
    1420. -
    1421. }
    1422. -
    1423. }), ret);
    1424. -
    1425. }), ret);
    1426. -
    1427. } else {
    1428. -
    1429. 0 ret.callback(this.__appliedMigrations);
    1430. -
    1431. }
    1432. -
    1433. 18 return ret.promise();
    1434. +
    1435. /**
    1436. +
    1437. * Represents an SQL array access, with multiple possible arguments.
    1438. +
    1439. * @constructs
    1440. +
    1441. * @augments patio.sql.GenericExpression
    1442. +
    1443. *
    1444. +
    1445. * @param arrCol the SQL array column
    1446. +
    1447. * @param sub The array of subscripts to use (should be an array of numbers)
    1448. +
    1449. */
    1450. +
    1451. constructor:function (arrCol, sub) {
    1452. +
    1453. //The SQL array column
    1454. +
    1455. 109 this.__arrCol = arrCol;
    1456. +
    1457. //The array of subscripts to use (should be an array of numbers)
    1458. +
    1459. 109 this.__sub = sub;
    1460. },
    1461. -
    1462. __getMirationFiles:function () {
    1463. -
    1464. 18 var ret = new Promise();
    1465. -
    1466. 18 var upMigrations = [], downMigrations = [], target = this.target;
    1467. -
    1468. 18 if (!this.__migrationFiles) {
    1469. -
    1470. 18 when(
    1471. -
    1472. this.getFileNames(),
    1473. -
    1474. this.__getAppliedMigrations()
    1475. -
    1476. ).then(hitch(this, function (res) {
    1477. -
    1478. 18 var files = res[0], appliedMigrations = res[1];
    1479. -
    1480. 18 var l = files.length, inserts = [];
    1481. -
    1482. 18 if (l > 0) {
    1483. -
    1484. 18 try {
    1485. -
    1486. 18 for (var i = 0; i < l; i++) {
    1487. -
    1488. 92 var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
    1489. -
    1490. 92 if (!isUndefined(target)) {
    1491. -
    1492. 48 var version = this.getMigrationVersionFromFile(f);
    1493. -
    1494. 48 if (version > target || (version === 0 && target === version)) {
    1495. -
    1496. 35 if (appliedMigrations.indexOf(fLowerCase) != -1) {
    1497. -
    1498. 26 downMigrations.push([f, require(file), "down"]);
    1499. -
    1500. }
    1501. -
    1502. 13 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
    1503. -
    1504. 9 upMigrations.push([f, require(file), "up"]);
    1505. -
    1506. }
    1507. -
    1508. 44 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
    1509. -
    1510. 35 upMigrations.push([f, require(file), "up"]);
    1511. -
    1512. }
    1513. -
    1514. }
    1515. -
    1516. } catch (e) {
    1517. -
    1518. 0 return ret.errback(e)
    1519. -
    1520. }
    1521. -
    1522. 18 this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
    1523. -
    1524. 18 ret.callback(this.__migrationFiles);
    1525. -
    1526. } else {
    1527. -
    1528. 0 return ret.callback();
    1529. -
    1530. }
    1531. -
    1532. }), ret);
    1533. -
    1534. } else {
    1535. -
    1536. 0 ret.callback(this.__migrationFiles);
    1537. -
    1538. }
    1539. -
    1540. 18 return ret.promise();
    1541. +
    1542. /**
    1543. +
    1544. * Create a new {@link patio.sql.Subscript} appending the given subscript(s)
    1545. +
    1546. * the the current array of subscripts.
    1547. +
    1548. */
    1549. +
    1550. addSub:function (sub) {
    1551. +
    1552. 0 return new SubScript(this.__arrCol, this.__sub.concat(sub));
    1553. },
    1554. -
    1555. -
    1556. // Returns the dataset for the schema_migrations table. If no such table
    1557. -
    1558. // exists, it is automatically created.
    1559. -
    1560. _getSchemaDataset:function () {
    1561. -
    1562. 36 var ret = new Promise();
    1563. -
    1564. 36 if (!this.__schemaDataset) {
    1565. -
    1566. 18 var ds = this.db.from(this.table);
    1567. -
    1568. 18 this.__createTable().then(hitch(this, function () {
    1569. -
    1570. 18 this.__schemaDataset = ds;
    1571. -
    1572. 18 ret.callback(ds);
    1573. -
    1574. }), ret);
    1575. -
    1576. } else {
    1577. -
    1578. 18 ret.callback(this.__schemaDataset);
    1579. -
    1580. }
    1581. -
    1582. 36 return ret.promise();
    1583. +
    1584. /**
    1585. +
    1586. * Converts the {@link patio.sql.SubScript} to a string.
    1587. +
    1588. *
    1589. +
    1590. * @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
    1591. +
    1592. * the dataset is ommited then the default {@link patio.Dataset} implementation is used.
    1593. +
    1594. *
    1595. +
    1596. * @return String the SQL version of the {@link patio.sql.SubScript}.
    1597. +
    1598. */
    1599. +
    1600. toString:function (ds) {
    1601. +
    1602. 109 !Dataset && (Dataset = require("./dataset"));
    1603. +
    1604. 109 ds = ds || new Dataset();
    1605. +
    1606. 109 return ds.subscriptSql(this);
    1607. },
    1608. -
    1609. __convertSchemaInfo:function () {
    1610. -
    1611. 1 var ret = new Promise(), c = this.column;
    1612. -
    1613. 1 var ds = this.db.from(this.table);
    1614. -
    1615. 1 this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
    1616. -
    1617. 1 this.getFileNames().then(hitch(this, function (files) {
    1618. -
    1619. 1 var l = files.length, inserts = [];
    1620. -
    1621. 1 if (l > 0) {
    1622. -
    1623. 1 for (var i = 0; i < l; i++) {
    1624. -
    1625. 7 var f = path.basename(files[i]);
    1626. -
    1627. 7 if (this.getMigrationVersionFromFile(f) <= version) {
    1628. -
    1629. 5 var insert = {};
    1630. -
    1631. 5 insert[c] = f;
    1632. -
    1633. 5 inserts.push(ds.insert(insert));
    1634. -
    1635. }
    1636. -
    1637. }
    1638. -
    1639. }
    1640. -
    1641. 1 if (inserts.length) {
    1642. -
    1643. 1 when.apply(comb, inserts).then(ret);
    1644. -
    1645. } else {
    1646. -
    1647. 0 ret.callback();
    1648. -
    1649. }
    1650. -
    1651. }), ret);
    1652. -
    1653. }), ret);
    1654. -
    1655. 1 return ret.promise();
    1656. -
    1657. -
    1658. },
    1659. +
    1660. /**@ignore*/
    1661. +
    1662. getters:{
    1663. +
    1664. f:function () {
    1665. +
    1666. 110 return this.__arrCol;
    1667. +
    1668. },
    1669. -
    1670. __createTable:function () {
    1671. -
    1672. 18 var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
    1673. -
    1674. 18 var ds = this.db.from(table);
    1675. -
    1676. 18 var ret = new Promise();
    1677. -
    1678. 18 when(
    1679. -
    1680. db.tableExists(table),
    1681. -
    1682. db.tableExists(intMigrationTable)
    1683. -
    1684. ).then(hitch(this, function (res) {
    1685. -
    1686. 18 var exists = res[0], intMigratorExists = res[1];
    1687. -
    1688. 18 if (!exists) {
    1689. -
    1690. 10 db.createTable(table,
    1691. -
    1692. function () {
    1693. -
    1694. 10 this.column(c, String, {primaryKey:true});
    1695. -
    1696. }).addErrback(ret);
    1697. -
    1698. 10 if (intMigratorExists) {
    1699. -
    1700. 1 db.from(intMigrationTable).all().then(hitch(this, function (versions) {
    1701. -
    1702. 1 var version;
    1703. -
    1704. 1 if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
    1705. -
    1706. 1 this.__convertSchemaInfo().then(ret);
    1707. -
    1708. } else {
    1709. -
    1710. 0 ret.callback();
    1711. -
    1712. }
    1713. -
    1714. }));
    1715. -
    1716. } else {
    1717. -
    1718. 9 ret.callback();
    1719. -
    1720. }
    1721. -
    1722. } else {
    1723. -
    1724. 8 ds.columns.then(hitch(this, function (columns) {
    1725. -
    1726. 8 if (columns.indexOf(c) === -1) {
    1727. -
    1728. 0 ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
    1729. -
    1730. } else {
    1731. -
    1732. 8 ret.callback();
    1733. -
    1734. }
    1735. -
    1736. }), ret);
    1737. -
    1738. }
    1739. -
    1740. }), ret);
    1741. -
    1742. 18 return ret.promise();
    1743. +
    1744. sub:function () {
    1745. +
    1746. 110 return this.__sub;
    1747. +
    1748. }
    1749. }
    1750. -
    1751. },
    1752. +
    1753. }
    1754. +
    1755. }).as(sql, "SubScript");
    1756. -
    1757. static:{
    1758. -
    1759. DEFAULT_SCHEMA_COLUMN:"filename",
    1760. -
    1761. DEFAULT_SCHEMA_TABLE:"schema_migrations"
    1762. +
    1763. 1var STRING_METHODS = ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "quote",
    1764. +
    1765. "replace", "search", "slice", "split", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase",
    1766. +
    1767. "toSource", "toString", "toUpperCase", "trim", "trimLeft", "trimRight", "valueOf"];
    1768. +
    1769. +
    1770. +
    1771. 1var addStringMethod = function (op) {
    1772. +
    1773. 24 return function () {
    1774. +
    1775. 4294 return this.__str[op].apply(this.__str, arguments);
    1776. }
    1777. -
    1778. }).as(exports, "TimestampMigrator");
    1779. +
    1780. };
    1781. -
    1782. 1exports.run = function () {
    1783. -
    1784. 31 return Migrator.run.apply(Migrator, arguments);
    1785. -
    1786. };
    +
  • 1var LiteralString = define([OrderedMethods, ComplexExpressionMethods, BooleanMethods, NumericMethods, StringMethods, InequalityMethods, AliasMethods], {
  • +
  • instance:{
  • +
  • /**@lends patio.sql.LiteralString*/
  • +
  • +
  • /**
  • +
  • * Represents a string that should be placed into a SQL query literally.
  • +
  • * <b>This class has all methods that a normal javascript String has.</b>
  • +
  • * @constructs
  • +
  • * @augments patio.sql.OrderedMethods
  • +
  • * @augments patio.sql.ComplexExpressionMethods
  • +
  • * @augments patio.sql.BooleanMethods
  • +
  • * @augments patio.sql.NumericMethods
  • +
  • * @augments patio.sql.StringMethods
  • +
  • * @augments patio.sql.InequalityMethods
  • +
  • * @augments patio.sql.AliasMethods
  • +
  • *
  • +
  • * @param {String} str the literal string.
  • +
  • */
  • +
  • constructor:function (str) {
  • +
  • 3095 this.__str = str;
  • +
  • }
  • +
  • }
  • +
  • }).as(sql, "LiteralString");
  • +
  • +
  • 1STRING_METHODS.forEach(function (op) {
  • +
  • 24 LiteralString.prototype[op] = addStringMethod(op);
  • +
  • }, this);
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • -
    +
    -
    dataset/index.js
    +
    migration.js
    - Coverage92.54 - SLOC439 - LOC67 - Missed5 + Coverage91.97 + SLOC607 + LOC274 + Missed22
    -
    1. 1var comb = require("comb"),
    2. +
      1. 1var comb = require("comb"),
      2. hitch = comb.hitch,
      3. -
      4. logging = comb.logging,
      5. -
      6. Logger = logging.Logger,
      7. -
      8. errors = require("../errors"),
      9. -
      10. QueryError = errors.QueryError,
      11. -
      12. DatasetError = errors.DatasetError,
      13. Promise = comb.Promise,
      14. -
      15. PromiseList = comb.PromiseList,
      16. -
      17. isUndefined = comb.isUndefined,
      18. -
      19. isUndefinedOrNull = comb.isUndefinedOrNull,
      20. -
      21. isString = comb.isString,
      22. -
      23. isInstanceOf = comb.isInstanceOf,
      24. -
      25. isString = comb.isString,
      26. -
      27. isFunction = comb.isFunction,
      28. -
      29. isNull = comb.isNull,
      30. -
      31. merge = comb.merge,
      32. +
      33. errors = require("./errors"),
      34. +
      35. MigrationError = errors.MigrationError,
      36. +
      37. NotImplemented = errors.NotImplemented(),
      38. +
      39. format = comb.string.format,
      40. define = comb.define,
      41. -
      42. graph = require("./graph"),
      43. -
      44. actions = require("./actions"),
      45. -
      46. features = require("./features"),
      47. -
      48. query = require("./query"),
      49. -
      50. sql = require("./sql"),
      51. -
      52. SQL = require("../sql").sql,
      53. -
      54. AliasedExpression = SQL.AliasedExpression,
      55. -
      56. Identifier = SQL.Identifier,
      57. -
      58. QualifiedIdentifier = SQL.QualifiedIdentifier;
      59. +
      60. isFunction = comb.isFunction,
      61. +
      62. serial = comb.serial,
      63. +
      64. isNumber = comb.isNumber,
      65. +
      66. when = comb.when,
      67. +
      68. isUndefined = comb.isUndefined,
      69. +
      70. fs = require("fs"),
      71. +
      72. path = require("path");
      73. -
      74. 1var LOGGER = comb.logger("patio.Dataset");
      75. -
      76. 1define([actions, graph, features, query, sql], {
      77. +
      78. 1var Migrator = define(null, {
      79. instance:{
      80. -
      81. -
      82. /**@lends patio.Dataset.prototype*/
      83. -
      84. -
      85. /**
      86. -
      87. * Class that is used for querying/retirving datasets from a database.
      88. -
      89. *
      90. -
      91. * <p> Dynamically genertated methods include
      92. -
      93. * <ul>
      94. -
      95. * <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
      96. -
      97. * {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
      98. -
      99. * to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
      100. -
      101. * {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
      102. -
      103. * <ul>
      104. -
      105. * <li>Conditioned join types that accept conditions.
      106. -
      107. * <ul>
      108. -
      109. * <li>inner - INNER JOIN</li>
      110. -
      111. * <li>fullOuter - FULL OUTER</li>
      112. -
      113. * <li>rightOuter - RIGHT OUTER JOIN</li>
      114. -
      115. * <li>leftOuter - LEFT OUTER JOIN</li>
      116. -
      117. * <li>full - FULL JOIN</li>
      118. -
      119. * <li>right - RIGHT JOIN</li>
      120. -
      121. * <li>left - LEFT JOIN</li>
      122. -
      123. * </ul>
      124. -
      125. * </li>
      126. -
      127. * <li>Unconditioned join types that do not accept join conditions
      128. -
      129. * <ul>
      130. -
      131. * <li>natural - NATURAL JOIN</li>
      132. -
      133. * <li>naturalLeft - NATURAL LEFT JOIN</li>
      134. -
      135. * <li>naturalRight - NATURAL RIGHT JOIN</li>
      136. -
      137. * <li>naturalFull - NATURA FULLL JOIN</li>
      138. -
      139. * <li>cross - CROSS JOIN</li>
      140. -
      141. * </ul>
      142. -
      143. * </li>
      144. -
      145. * </ul>
      146. -
      147. * </li>
      148. -
      149. * </li>
      150. -
      151. * </ul>
      152. -
      153. *
      154. -
      155. * <p>
      156. -
      157. * <h4>Features:</h4>
      158. -
      159. * <p>
      160. -
      161. * Features that a particular {@link patio.Dataset} supports are shown in the example below.
      162. -
      163. * If you wish to implement an adapter please override these values depending on the database that
      164. -
      165. * you are developing the adapter for.
      166. -
      167. * </p>
      168. -
      169. * <pre class="code">
      170. -
      171. * var ds = DB.from("test");
      172. -
      173. *
      174. -
      175. * //The default values returned
      176. -
      177. *
      178. -
      179. * //Whether this dataset quotes identifiers.
      180. -
      181. * //Whether this dataset quotes identifiers.
      182. -
      183. * ds.quoteIdentifiers //=>true
      184. -
      185. *
      186. -
      187. * //Whether this dataset will provide accurate number of rows matched for
      188. -
      189. * //delete and update statements. Accurate in this case is the number of
      190. -
      191. * //rows matched by the dataset's filter.
      192. -
      193. * ds.providesAccurateRowsMatched; //=>true
      194. -
      195. *
      196. -
      197. * //Times Whether the dataset requires SQL standard datetimes (false by default,
      198. -
      199. * // as most allow strings with ISO 8601 format).
      200. -
      201. * ds.requiresSqlStandardDate; //=>false
      202. -
      203. *
      204. -
      205. * //Whether the dataset supports common table expressions (the WITH clause).
      206. -
      207. * ds.supportsCte; //=>true
      208. -
      209. *
      210. -
      211. * //Whether the dataset supports the DISTINCT ON clause, false by default.
      212. -
      213. * ds.supportsDistinctOn; //=>false
      214. -
      215. *
      216. -
      217. * //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      218. -
      219. * ds.supportsIntersectExcept; //=>true
      220. -
      221. *
      222. -
      223. * //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
      224. -
      225. * ds.supportsIntersectExceptAll; //=>true
      226. -
      227. *
      228. -
      229. * //Whether the dataset supports the IS TRUE syntax.
      230. -
      231. * ds.supportsIsTrue; //=>true
      232. -
      233. *
      234. -
      235. * //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      236. -
      237. * ds.supportsJoinUsing; //=>true
      238. -
      239. *
      240. -
      241. * //Whether modifying joined datasets is supported.
      242. -
      243. * ds.supportsModifyingJoin; //=>false
      244. -
      245. *
      246. -
      247. * //Whether the IN/NOT IN operators support multiple columns when an
      248. -
      249. * ds.supportsMultipleColumnIn; //=>true
      250. -
      251. *
      252. -
      253. * //Whether the dataset supports timezones in literal timestamps
      254. -
      255. * ds.supportsTimestampTimezone; //=>false
      256. -
      257. *
      258. -
      259. * //Whether the dataset supports fractional seconds in literal timestamps
      260. -
      261. * ds.supportsTimestampUsecs; //=>true
      262. -
      263. *
      264. -
      265. * //Whether the dataset supports window functions.
      266. -
      267. * ds.supportsWindowFunctions; //=>false
      268. -
      269. * </pre>
      270. -
      271. * <p>
      272. -
      273. * <p>
      274. -
      275. * <h4>Actions</h4>
      276. -
      277. * <p>
      278. -
      279. * Each dataset does not actually send any query to the database until an action method has
      280. -
      281. * been called upon it(with the exception of {@link patio.Dataset#graph} because columns
      282. -
      283. * from the other table might need retrived in order to set up the graph). Each action
      284. -
      285. * returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
      286. -
      287. * that you account for errors otherwise it can be difficult to track down issues.
      288. -
      289. * The list of action methods is:
      290. -
      291. * <ul>
      292. -
      293. * <li>{@link patio.Dataset#all}</li>
      294. -
      295. * <li>{@link patio.Dataset#one}</li>
      296. -
      297. * <li>{@link patio.Dataset#avg}</li>
      298. -
      299. * <li>{@link patio.Dataset#count}</li>
      300. -
      301. * <li>{@link patio.Dataset#columns}</li>
      302. -
      303. * <li>{@link patio.Dataset#remove}</li>
      304. -
      305. * <li>{@link patio.Dataset#forEach}</li>
      306. -
      307. * <li>{@link patio.Dataset#empty}</li>
      308. -
      309. * <li>{@link patio.Dataset#first}</li>
      310. -
      311. * <li>{@link patio.Dataset#get}</li>
      312. -
      313. * <li>{@link patio.Dataset#import}</li>
      314. -
      315. * <li>{@link patio.Dataset#insert}</li>
      316. -
      317. * <li>{@link patio.Dataset#save}</li>
      318. -
      319. * <li>{@link patio.Dataset#insertMultiple}</li>
      320. -
      321. * <li>{@link patio.Dataset#saveMultiple}</li>
      322. -
      323. * <li>{@link patio.Dataset#interval}</li>
      324. -
      325. * <li>{@link patio.Dataset#last}</li>
      326. -
      327. * <li>{@link patio.Dataset#map}</li>
      328. -
      329. * <li>{@link patio.Dataset#max}</li>
      330. -
      331. * <li>{@link patio.Dataset#min}</li>
      332. -
      333. * <li>{@link patio.Dataset#multiInsert}</li>
      334. -
      335. * <li>{@link patio.Dataset#range}</li>
      336. -
      337. * <li>{@link patio.Dataset#selectHash}</li>
      338. -
      339. * <li>{@link patio.Dataset#selectMap}</li>
      340. -
      341. * <li>{@link patio.Dataset#selectOrderMap}</li>
      342. -
      343. * <li>{@link patio.Dataset#set}</li>
      344. -
      345. * <li>{@link patio.Dataset#singleRecord}</li>
      346. -
      347. * <li>{@link patio.Dataset#singleValue}</li>
      348. -
      349. * <li>{@link patio.Dataset#sum}</li>
      350. -
      351. * <li>{@link patio.Dataset#toCsv}</li>
      352. -
      353. * <li>{@link patio.Dataset#toHash}</li>
      354. -
      355. * <li>{@link patio.Dataset#truncate}</li>
      356. -
      357. * <li>{@link patio.Dataset#update}</li>
      358. -
      359. * </ul>
      360. -
      361. *
      362. -
      363. * </p>
      364. -
      365. * </p>
      366. +
      367. /**@lends patio.migrations.Migrator.prototype*/
      368. +
      369. column:null,
      370. +
      371. db:null,
      372. +
      373. directory:null,
      374. +
      375. ds:null,
      376. +
      377. files:null,
      378. +
      379. table:null,
      380. +
      381. target:null,
      382. +
      383. +
      384. /**
      385. +
      386. * Abstract Migrator class. This class should be be instantiated directly.
      387. *
      388. * @constructs
      389. +
      390. * @param {patio.Database} db the database to migrate
      391. +
      392. * @param {String} directory directory that the migration files reside in
      393. +
      394. * @param {Object} [opts={}] optional parameters.
      395. +
      396. * @param {String} [opts.column] the column in the table that version information should be stored.
      397. +
      398. * @param {String} [opts.table] the table that version information should be stored.
      399. +
      400. * @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
      401. +
      402. * @param {String} [opts.current] the version that the database is currently at if the current version
      403. +
      404. */
      405. +
      406. constructor:function (db, directory, opts) {
      407. +
      408. 31 this.db = db;
      409. +
      410. 31 this.directory = directory;
      411. +
      412. 31 opts = opts || {};
      413. +
      414. 31 this.table = opts.table || this._static.DEFAULT_SCHEMA_TABLE;
      415. +
      416. 31 this.column = opts.column || this._static.DEFAULT_SCHEMA_COLUMN;
      417. +
      418. 31 this._opts = opts;
      419. +
      420. },
      421. +
      422. +
      423. /**
      424. +
      425. * Runs the migration and returns a promise.
      426. +
      427. */
      428. +
      429. run:function () {
      430. +
      431. 0 throw new NotImplemented("patio.migrations.Migrator#run");
      432. +
      433. },
      434. +
      435. +
      436. getFileNames:function () {
      437. +
      438. 50 if (!this.__files) {
      439. +
      440. 49 return this._static.getFileNames(this.directory).addCallback(hitch(this, function (files) {
      441. +
      442. 49 this.__files = files;
      443. +
      444. }));
      445. +
      446. } else {
      447. +
      448. 1 return new Promise().callback(this.__files).promise();
      449. +
      450. }
      451. +
      452. },
      453. +
      454. +
      455. getMigrationVersionFromFile:function (filename) {
      456. +
      457. 476 return parseInt(path.basename(filename).split(this._static.MIGRATION_SPLITTER)[0], 10);
      458. +
      459. }
      460. +
      461. },
      462. +
      463. +
      464. "static":{
      465. +
      466. /**@lends patio.migrations.Migrator*/
      467. +
      468. +
      469. MIGRATION_FILE_PATTERN:/^\d+\..+\.js$/i,
      470. +
      471. MIGRATION_SPLITTER:'.',
      472. +
      473. MINIMUM_TIMESTAMP:20000101,
      474. +
      475. +
      476. getFileNames:function (directory) {
      477. +
      478. 80 var ret = new Promise();
      479. +
      480. 80 fs.readdir(directory, hitch(this, function (err, files) {
      481. +
      482. 80 if (err) {
      483. +
      484. 0 ret.errback(err);
      485. +
      486. } else {
      487. +
      488. 80 files = files.filter(function (file) {
      489. +
      490. 394 return file.match(this.MIGRATION_FILE_PATTERN) !== null;
      491. +
      492. }, this);
      493. +
      494. 80 ret.callback(files.map(function (file) {
      495. +
      496. 394 return path.resolve(directory, file);
      497. +
      498. }));
      499. +
      500. }
      501. +
      502. }));
      503. +
      504. 80 return ret.promise();
      505. +
      506. },
      507. +
      508. +
      509. /**
      510. +
      511. * Migrates the database using migration files found in the supplied directory.
      512. +
      513. * See {@link patio#migrate}
      514. *
      515. +
      516. * @example
      517. +
      518. * var DB = patio.connect("my://connection/string");
      519. +
      520. * patio. migrate(DB, __dirname + "/timestamp_migration").then(function(){
      521. +
      522. * console.log("done migrating!");
      523. +
      524. * });
      525. *
      526. -
      527. * @param {patio.Database} db the database this dataset should use when querying for data.
      528. -
      529. * @param {Object} opts options to set on this dataset instance
      530. -
      531. *
      532. -
      533. * @property {Function} rowCb callback to be invoked for each row returned from the database.
      534. -
      535. * the return value will be used as the result of query. The rowCb can also return a promise,
      536. -
      537. * The resolved value of the promise will be used as result.
      538. -
      539. *
      540. -
      541. * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
      542. -
      543. * This value will be defaulted to whatever the identifierInputMethod
      544. -
      545. * is on the database used in initialization.
      546. -
      547. *
      548. -
      549. * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
      550. -
      551. * This value will be defaulted to whatever the identifierOutputMethod
      552. -
      553. * is on the database used in initialization.
      554. -
      555. * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
      556. -
      557. * throws a {patio.DatasetError} tf the dataset doesn't have a table.
      558. -
      559. * <pre class="code">
      560. -
      561. * DB.from("table").firstSourceAlias;
      562. -
      563. * //=> "table"
      564. -
      565. *
      566. -
      567. * DB.from("table___t").firstSourceAlias;
      568. -
      569. * //=> "t"
      570. -
      571. * </pre>
      572. +
      573. * patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).then(function(){
      574. +
      575. * console.log("done migrating down!");
      576. +
      577. * });
      578. *
      579. -
      580. * @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
      581. -
      582. * have a table, raises a {@link patio.erros.DatasetError}.
      583. -
      584. *<pre class="code">
      585. *
      586. -
      587. * DB.from("table").firstSourceTable;
      588. -
      589. * //=> "table"
      590. +
      591. * @param {patio.Database} db the database to migrate
      592. +
      593. * @param {String} directory directory that the migration files reside in
      594. +
      595. * @param {Object} [opts={}] optional parameters.
      596. +
      597. * @param {String} [opts.column] the column in the table that version information should be stored.
      598. +
      599. * @param {String} [opts.table] the table that version information should be stored.
      600. +
      601. * @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
      602. +
      603. * @param {String} [opts.current] the version that the database is currently at if the current version
      604. +
      605. * is not provided it is retrieved from the database.
      606. *
      607. -
      608. * DB.from("table___t").firstSourceTable;
      609. -
      610. * //=> "t"
      611. -
      612. * </pre>
      613. -
      614. * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
      615. -
      616. * <pre class="code">
      617. -
      618. * DB.from("items").isSimpleSelectAll; //=> true
      619. -
      620. * DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
      621. -
      622. * </pre>
      623. -
      624. * @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
      625. -
      626. * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
      627. -
      628. * delete and update statements. Accurate in this case is the number of
      629. -
      630. * rows matched by the dataset's filter.
      631. -
      632. * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
      633. -
      634. * as most allow strings with ISO 8601 format).
      635. -
      636. * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
      637. -
      638. * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
      639. -
      640. * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      641. -
      642. * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
      643. -
      644. * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
      645. -
      646. * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      647. -
      648. * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
      649. -
      650. * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
      651. -
      652. * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
      653. -
      654. * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
      655. -
      656. * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
      657. +
      658. * @return {Promise} a promise that is resolved once the migration is complete.
      659. */
      660. -
      661. constructor:function (db, opts) {
      662. -
      663. 29216 this._super(arguments);
      664. -
      665. 29216 this.db = db;
      666. -
      667. 29216 this.__opts = {};
      668. -
      669. 29216 this.__rowCb = null;
      670. -
      671. 29216 if (db) {
      672. -
      673. 15039 this.__quoteIdentifiers = db.quoteIdentifiers;
      674. -
      675. 15039 this.__identifierInputMethod = db.identifierInputMethod;
      676. -
      677. 15039 this.__identifierOutputMethod = db.identifierOutputMethod;
      678. +
      679. run:function (db, directory, opts, cb) {
      680. +
      681. 31 if (isFunction(opts)) {
      682. +
      683. 0 cb = opts;
      684. +
      685. 0 opts = {};
      686. +
      687. } else {
      688. +
      689. 31 opts = opts || {};
      690. +
      691. }
      692. +
      693. 31 opts = opts || {};
      694. +
      695. 31 var ret = new Promise();
      696. +
      697. 31 this.__getMigrator(directory).then(function (migrator) {
      698. +
      699. 31 new migrator(db, directory, opts).run().then(ret);
      700. +
      701. }, ret);
      702. +
      703. 31 ret.classic(cb);
      704. +
      705. 31 return ret.promise();
      706. +
      707. },
      708. +
      709. +
      710. // Choose the Migrator subclass to use. Uses the TimestampMigrator
      711. +
      712. // // if the version number appears to be a unix time integer for a year
      713. +
      714. // after 2005, otherwise uses the IntegerMigrator.
      715. +
      716. __getMigrator:function (directory) {
      717. +
      718. 31 var ret = new Promise();
      719. +
      720. 31 var retClass = IntegerMigrator;
      721. +
      722. 31 this.getFileNames(directory).then(hitch(this, function (files) {
      723. +
      724. 31 var l = files.length;
      725. +
      726. 31 if (l) {
      727. +
      728. 31 for (var i = 0; i < l; i++) {
      729. +
      730. 81 var file = files[i];
      731. +
      732. 81 if (parseInt(path.basename(file).split(this.MIGRATION_SPLITTER)[0], 10) > this.MINIMUM_TIMESTAMP) {
      733. +
      734. 18 retClass = TimestampMigrator;
      735. +
      736. 18 break;
      737. +
      738. }
      739. +
      740. }
      741. +
      742. }
      743. +
      744. 31 ret.callback(retClass);
      745. +
      746. +
      747. }), ret);
      748. +
      749. 31 return ret.promise();
      750. +
      751. }
      752. +
      753. }
      754. +
      755. });
      756. +
      757. +
      758. +
      759. /**
      760. +
      761. * @class Migrator that uses the file format {migrationName}.{version}.js, where version starts at 0.
      762. +
      763. * <b>Missing migrations are not allowed</b>
      764. +
      765. *
      766. +
      767. * @augments patio.migrations.Migrator
      768. +
      769. * @name IntegerMigrator
      770. +
      771. * @memberOf patio.migrations
      772. +
      773. */
      774. +
      775. 1var IntegerMigrator = define(Migrator, {
      776. +
      777. instance:{
      778. +
      779. /**@lends patio.migrations.IntegerMigrator.prototype*/
      780. +
      781. current:null,
      782. +
      783. direction:null,
      784. +
      785. migrations:null,
      786. +
      787. +
      788. _migrationFiles:null,
      789. +
      790. +
      791. run:function () {
      792. +
      793. 13 var ret = new Promise(), DB = this.db;
      794. +
      795. 13 serial([this._getLatestMigrationVersion.bind(this), this._getCurrentMigrationVersion.bind(this)]).then(hitch(this, function (res) {
      796. +
      797. 11 var target = res[0], current = res[1];
      798. +
      799. 11 if (current !== target) {
      800. +
      801. 11 var direction = this.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;
      802. +
      803. 11 this._getMigrations(current, target, direction).then(hitch(this, function (migrations) {
      804. +
      805. 11 var runMigration = hitch(this, function (index) {
      806. +
      807. 60 if (index >= migrations.length) {
      808. +
      809. 11 ret.callback(version);
      810. +
      811. } else {
      812. +
      813. 49 var curr = migrations[index], migration = curr[0];
      814. +
      815. 49 version = curr[1];
      816. +
      817. 49 var now = new Date();
      818. +
      819. 49 var lv = isUp ? version : version - 1;
      820. +
      821. 49 DB.logInfo("Begin applying migration version %d, direction: %s", lv, direction);
      822. +
      823. 49 DB.transaction(hitch(this, function () {
      824. +
      825. 49 var ret = new Promise();
      826. +
      827. 49 if (!isFunction(migration[direction])) {
      828. +
      829. 0 this._setMigrationVersion(lv).then(ret);
      830. +
      831. } else {
      832. +
      833. 49 when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
      834. +
      835. 49 this._setMigrationVersion(lv).then(ret);
      836. +
      837. }), ret);
      838. +
      839. }
      840. +
      841. 49 return ret.promise();
      842. +
      843. })).then(function () {
      844. +
      845. 49 DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);
      846. +
      847. 49 runMigration(index + 1);
      848. +
      849. }, ret);
      850. +
      851. }
      852. +
      853. +
      854. });
      855. +
      856. 11 runMigration(0);
      857. +
      858. }), ret);
      859. +
      860. } else {
      861. +
      862. 0 ret.callback(target);
      863. +
      864. }
      865. +
      866. +
      867. }), ret);
      868. +
      869. 13 return ret.promise();
      870. +
      871. },
      872. +
      873. +
      874. _getMigrations:function (current, target, direction) {
      875. +
      876. 11 var ret = new Promise(), isUp = direction === "up", migrations = [];
      877. +
      878. 11 when(this._getMigrationFiles()).then(function (files) {
      879. +
      880. 11 try {
      881. +
      882. 11 if ((isUp ? target : current - 1) < files.length) {
      883. +
      884. 11 if (isUp) {
      885. +
      886. 8 current++;
      887. +
      888. }
      889. +
      890. 11 for (; isUp ? current <= target : current > target; isUp ? current++ : current--) {
      891. +
      892. 49 migrations.push([require(files[current]), current]);
      893. +
      894. }
      895. +
      896. } else {
      897. +
      898. 0 return ret.errback(new MigrationError("Invalid target " + target));
      899. +
      900. }
      901. +
      902. } catch (e) {
      903. +
      904. 0 return ret.errback(e);
      905. +
      906. }
      907. +
      908. 11 ret.callback(migrations);
      909. +
      910. }, ret);
      911. +
      912. 11 return ret.promise();
      913. +
      914. },
      915. +
      916. +
      917. +
      918. _getMigrationFiles:function () {
      919. +
      920. 19 var ret = new Promise();
      921. +
      922. 19 if (!this._migrationFiles) {
      923. +
      924. 13 var retFiles = [];
      925. +
      926. 13 var directory = this.directory;
      927. +
      928. 13 this.getFileNames().then(hitch(this, function (files) {
      929. +
      930. 13 var l = files.length;
      931. +
      932. 13 if (l) {
      933. +
      934. 13 for (var i = 0; i < l; i++) {
      935. +
      936. 59 var file = files[i];
      937. +
      938. 59 var version = this.getMigrationVersionFromFile(file);
      939. +
      940. 59 if (isUndefined(retFiles[version])) {
      941. +
      942. 58 retFiles[version] = file;
      943. +
      944. } else {
      945. +
      946. 1 return ret.errback(new MigrationError("Duplicate migration number " + version));
      947. +
      948. }
      949. +
      950. }
      951. +
      952. 12 if (isUndefined(retFiles[0])) {
      953. +
      954. 0 retFiles.shift();
      955. +
      956. }
      957. +
      958. 12 for (var j = 0; j < l; j++) {
      959. +
      960. 57 if (isUndefined(retFiles[j])) {
      961. +
      962. 1 return ret.errback(new MigrationError("Missing migration for " + j));
      963. +
      964. }
      965. +
      966. }
      967. +
      968. }
      969. +
      970. 11 this._migrationFiles = retFiles;
      971. +
      972. 11 ret.callback(retFiles);
      973. +
      974. }), ret);
      975. +
      976. } else {
      977. +
      978. 6 ret.callback(this._migrationFiles);
      979. }
      980. +
      981. 19 return ret.promise();
      982. },
      983. +
      984. _getLatestMigrationVersion:function () {
      985. +
      986. 13 var ret = new Promise();
      987. +
      988. 13 if (!isUndefined(this._opts.target)) {
      989. +
      990. 5 ret.callback(this._opts.target);
      991. +
      992. } else {
      993. +
      994. 8 this._getMigrationFiles().then(hitch(this, function (files) {
      995. +
      996. 6 var l = files[files.length - 1];
      997. +
      998. 6 ret.callback(l ? this.getMigrationVersionFromFile(path.basename(l)) : null);
      999. +
      1000. }), ret);
      1001. +
      1002. }
      1003. +
      1004. 13 return ret.promise();
      1005. +
      1006. },
      1007. -
      1008. /**
      1009. -
      1010. * Returns a new clone of the dataset with with the given options merged into the current datasets options.
      1011. -
      1012. * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
      1013. -
      1014. * columns are deleted. This method should generally not be called
      1015. -
      1016. * directly by user code.
      1017. -
      1018. *
      1019. -
      1020. * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
      1021. -
      1022. * @return [patio.Dataset] a cloned dataset with the merged options
      1023. -
      1024. **/
      1025. -
      1026. mergeOptions:function (opts) {
      1027. -
      1028. 14948 opts = isUndefined(opts) ? {} : opts;
      1029. -
      1030. 14948 var ds = new this._static(this.db, {});
      1031. -
      1032. 14948 ds.rowCb = this.rowCb;
      1033. -
      1034. 14948 this._static.FEATURES.forEach(function (f) {
      1035. -
      1036. 209272 ds[f] = this[f];
      1037. -
      1038. }, this);
      1039. -
      1040. 14948 ds.__opts = merge({}, this.__opts, opts);
      1041. -
      1042. 14948 ds.identifierInputMethod = this.identifierInputMethod;
      1043. -
      1044. 14948 ds.identifierOutputMethod = this.identifierOutputMethod;
      1045. -
      1046. 14948 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
      1047. -
      1048. 14948 if (Object.keys(opts).some(function (o) {
      1049. -
      1050. 13536 return columnChangeOpts.indexOf(o) != -1;
      1051. -
      1052. })) {
      1053. -
      1054. 2456 ds.__opts.columns = null;
      1055. +
      1056. _getCurrentMigrationVersion:function () {
      1057. +
      1058. 11 var ret = new Promise();
      1059. +
      1060. 11 if (!isUndefined(this._opts.current)) {
      1061. +
      1062. 2 ret.callback(this._opts.current);
      1063. +
      1064. } else {
      1065. +
      1066. 9 when(this._getSchemaDataset()).then(hitch(this, function (ds) {
      1067. +
      1068. 9 ds.get(this.column).then(ret);
      1069. +
      1070. }), ret);
      1071. }
      1072. -
      1073. 14948 return ds;
      1074. +
      1075. 11 return ret.promise();
      1076. },
      1077. +
      1078. _setMigrationVersion:function (version) {
      1079. +
      1080. 49 var ret = new Promise(), c = this.column;
      1081. +
      1082. 49 this._getSchemaDataset().then(function (ds) {
      1083. +
      1084. 49 var item = {};
      1085. +
      1086. 49 item[c] = version;
      1087. +
      1088. 49 ds.update(item).both(ret);
      1089. +
      1090. }, ret);
      1091. -
      1092. /**
      1093. -
      1094. * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      1095. -
      1096. * or {@link patio.sql.AliasedExpression}, depending on the format:
      1097. -
      1098. *
      1099. -
      1100. * <ul>
      1101. -
      1102. * <li>For columns : table__column___alias.</li>
      1103. -
      1104. * <li>For tables : schema__table___alias.</li>
      1105. -
      1106. * </ul>
      1107. -
      1108. * each portion of the identifier is optional. See example below
      1109. -
      1110. *
      1111. -
      1112. * @example
      1113. -
      1114. *
      1115. -
      1116. * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
      1117. -
      1118. * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
      1119. -
      1120. * ds.stringToIdentifier("table__column___alias");
      1121. -
      1122. * //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
      1123. -
      1124. *
      1125. -
      1126. * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      1127. -
      1128. * or {@link patio.sql.AliasedExpression}.
      1129. -
      1130. *
      1131. -
      1132. * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
      1133. -
      1134. */
      1135. -
      1136. stringToIdentifier:function (name) {
      1137. -
      1138. 15093 if (isString(name)) {
      1139. -
      1140. 10138 var parts = this._splitString(name);
      1141. -
      1142. 10138 var schema = parts[0], table = parts[1], alias = parts[2];
      1143. -
      1144. 10138 return (schema && table && alias
      1145. -
      1146. ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
      1147. -
      1148. : (schema && table
      1149. -
      1150. ? new QualifiedIdentifier(schema, table)
      1151. -
      1152. : (table && alias
      1153. -
      1154. ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
      1155. +
      1156. 49 return ret.promise();
      1157. +
      1158. },
      1159. +
      1160. +
      1161. _getSchemaDataset:function () {
      1162. +
      1163. 58 var c = this.column, table = this.table;
      1164. +
      1165. 58 var ret = new Promise();
      1166. +
      1167. 58 if (!this.__schemaDataset) {
      1168. +
      1169. 11 var ds = this.db.from(table);
      1170. +
      1171. 11 this.__createOrAlterMigrationTable().then(hitch(this, function () {
      1172. +
      1173. 11 ds.isEmpty().then(hitch(this, function (empty) {
      1174. +
      1175. 11 if (empty) {
      1176. +
      1177. 0 var item = {};
      1178. +
      1179. 0 item[c] = -1;
      1180. +
      1181. 0 this.__schemaDataset = ds;
      1182. +
      1183. 0 ds.insert(item).then(hitch(ret, "callback", ds), ret);
      1184. +
      1185. } else {
      1186. +
      1187. 11 ds.count().then(hitch(this, function (count) {
      1188. +
      1189. 11 if (count > 1) {
      1190. +
      1191. 0 ret.errback(new Error("More than one row in migrator table"));
      1192. +
      1193. } else {
      1194. +
      1195. 11 this.__schemaDataset = ds;
      1196. +
      1197. 11 ret.callback(ds);
      1198. +
      1199. }
      1200. +
      1201. }), ret);
      1202. +
      1203. }
      1204. +
      1205. }), ret);
      1206. +
      1207. }), ret);
      1208. +
      1209. } else {
      1210. +
      1211. 47 ret.callback(this.__schemaDataset);
      1212. +
      1213. }
      1214. +
      1215. 58 return ret.promise();
      1216. +
      1217. },
      1218. +
      1219. +
      1220. __createOrAlterMigrationTable:function () {
      1221. +
      1222. 11 var c = this.column, table = this.table, db = this.db;
      1223. +
      1224. 11 var ds = this.db.from(table);
      1225. +
      1226. 11 var ret = new Promise();
      1227. +
      1228. 11 db.tableExists(table).then(hitch(this, function (exists) {
      1229. +
      1230. 11 if (!exists) {
      1231. +
      1232. 6 db.createTable(table,
      1233. +
      1234. function () {
      1235. +
      1236. 6 this.column(c, "integer", {"default":-1, allowNull:false});
      1237. +
      1238. }).then(ret);
      1239. +
      1240. } else {
      1241. +
      1242. 5 ds.columns.then(function (columns) {
      1243. +
      1244. 5 if (columns.indexOf(c) === -1) {
      1245. +
      1246. 1 db.addColumn(table, c, "integer", {"default":-1, allowNull:false})
      1247. +
      1248. .then(ret);
      1249. +
      1250. } else {
      1251. +
      1252. 4 ret.callback();
      1253. +
      1254. }
      1255. +
      1256. });
      1257. +
      1258. }
      1259. +
      1260. }), ret);
      1261. +
      1262. 11 return ret.promise();
      1263. +
      1264. }
      1265. +
      1266. +
      1267. },
      1268. +
      1269. +
      1270. static:{
      1271. +
      1272. DEFAULT_SCHEMA_COLUMN:"version",
      1273. +
      1274. DEFAULT_SCHEMA_TABLE:"schema_info"
      1275. +
      1276. }
      1277. +
      1278. }).as(exports, "IntegerMigrator");
      1279. +
      1280. +
      1281. +
      1282. /**
      1283. +
      1284. * @class Migrator that uses the file format {migrationName}.{timestamp}.js, where the timestamp
      1285. +
      1286. * can be anything greater than 20000101.
      1287. +
      1288. *
      1289. +
      1290. * @name TimestampMigrator
      1291. +
      1292. * @augments patio.migrations.Migrator
      1293. +
      1294. * @memberOf patio.migrations
      1295. +
      1296. */
      1297. +
      1298. 1var TimestampMigrator = define(Migrator, {
      1299. +
      1300. instance:{
      1301. +
      1302. +
      1303. constructor:function (db, directory, opts) {
      1304. +
      1305. 18 this._super(arguments);
      1306. +
      1307. 18 opts = opts || {};
      1308. +
      1309. 18 this.target = opts.target;
      1310. +
      1311. },
      1312. +
      1313. +
      1314. run:function () {
      1315. +
      1316. 18 var ret = new Promise(), DB = this.db, column = this.column;
      1317. +
      1318. 18 serial([this.__getMirationFiles.bind(this), this._getSchemaDataset.bind(this)]).then(function (res) {
      1319. +
      1320. 18 var migrations = res[0], ds = res[1];
      1321. +
      1322. 18 var runMigration = hitch(this, function (index) {
      1323. +
      1324. 86 if (index >= migrations.length) {
      1325. +
      1326. 16 ret.callback();
      1327. +
      1328. } else {
      1329. +
      1330. 70 var curr = migrations[index], file = curr[0], migration = curr[1], direction = curr[2];
      1331. +
      1332. 70 var now = new Date();
      1333. +
      1334. 70 DB.logInfo("Begin applying migration file %s, direction: %s", file, direction);
      1335. +
      1336. 70 DB.transaction(hitch(this, function () {
      1337. +
      1338. 70 var ret = new Promise();
      1339. +
      1340. 70 when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
      1341. +
      1342. 68 var fileLowerCase = file.toLowerCase();
      1343. +
      1344. 68 var query = {};
      1345. +
      1346. 68 query[column] = fileLowerCase;
      1347. +
      1348. 68 (direction === "up" ? ds.insert(query) : ds.filter(query).remove()).then(ret);
      1349. +
      1350. }), ret);
      1351. +
      1352. 68 return ret.promise();
      1353. +
      1354. })).then(function () {
      1355. +
      1356. 68 DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);
      1357. +
      1358. 68 runMigration(index + 1);
      1359. +
      1360. }, ret);
      1361. +
      1362. }
      1363. +
      1364. +
      1365. });
      1366. +
      1367. 18 runMigration(0);
      1368. +
      1369. }, ret);
      1370. +
      1371. 18 return ret.promise();
      1372. +
      1373. },
      1374. +
      1375. +
      1376. getFileNames:function () {
      1377. +
      1378. 37 var ret = new Promise();
      1379. +
      1380. 37 var sup = this._super(arguments);
      1381. +
      1382. 37 sup.then(hitch(this, function (files) {
      1383. +
      1384. 37 ret.callback(files.sort(hitch(this, function (f1, f2) {
      1385. +
      1386. 178 return this.getMigrationVersionFromFile(f1) - this.getMigrationVersionFromFile(f2);
      1387. +
      1388. })));
      1389. +
      1390. }), ret);
      1391. +
      1392. 37 return ret.promise();
      1393. +
      1394. },
      1395. +
      1396. +
      1397. __getAppliedMigrations:function () {
      1398. +
      1399. 18 var ret = new Promise();
      1400. +
      1401. 18 if (!this.__appliedMigrations) {
      1402. +
      1403. 18 this._getSchemaDataset().then(hitch(this, function (ds) {
      1404. +
      1405. 18 when(
      1406. +
      1407. ds.selectOrderMap(this.column),
      1408. +
      1409. this.getFileNames()
      1410. +
      1411. ).then(hitch(this, function (res) {
      1412. +
      1413. 18 var appliedMigrations = res[0], files = res[1].map(function (f) {
      1414. +
      1415. 92 return path.basename(f).toLowerCase();
      1416. +
      1417. });
      1418. +
      1419. 18 var l = appliedMigrations.length;
      1420. +
      1421. 18 if (l) {
      1422. +
      1423. 9 for (var i = 0; i < l; i++) {
      1424. +
      1425. 39 if (files.indexOf(appliedMigrations[i]) == -1) {
      1426. +
      1427. 0 return ret.errback("Applied migrations file not found in directory " + appliedMigrations[i]);
      1428. +
      1429. }
      1430. +
      1431. }
      1432. +
      1433. 9 this.__appliedMigrations = appliedMigrations;
      1434. +
      1435. 9 ret.callback(appliedMigrations);
      1436. +
      1437. } else {
      1438. +
      1439. 9 this.__appliedMigrations = [];
      1440. +
      1441. 9 ret.callback([]);
      1442. +
      1443. }
      1444. +
      1445. }), ret);
      1446. +
      1447. }), ret);
      1448. } else {
      1449. -
      1450. 4955 return name;
      1451. +
      1452. 0 ret.callback(this.__appliedMigrations);
      1453. }
      1454. +
      1455. 18 return ret.promise();
      1456. },
      1457. -
      1458. /**
      1459. -
      1460. * Can either be a string or null.
      1461. -
      1462. *
      1463. -
      1464. *
      1465. -
      1466. * @example
      1467. -
      1468. * //columns
      1469. -
      1470. * table__column___alias //=> table.column as alias
      1471. -
      1472. * table__column //=> table.column
      1473. -
      1474. * //tables
      1475. -
      1476. * schema__table___alias //=> schema.table as alias
      1477. -
      1478. * schema__table //=> schema.table
      1479. -
      1480. *
      1481. -
      1482. * //name and alias
      1483. -
      1484. * columnOrTable___alias //=> columnOrTable as alias
      1485. -
      1486. *
      1487. -
      1488. *
      1489. -
      1490. *
      1491. -
      1492. * @return {String[]} an array with the elements being:
      1493. -
      1494. * <ul>
      1495. -
      1496. * <li>For columns :[table, column, alias].</li>
      1497. -
      1498. * <li>For tables : [schema, table, alias].</li>
      1499. -
      1500. * </ul>
      1501. -
      1502. */
      1503. -
      1504. _splitString:function (s) {
      1505. -
      1506. 19662 var ret, m;
      1507. -
      1508. 19662 if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
      1509. -
      1510. 189 ret = m.slice(1);
      1511. -
      1512. }
      1513. -
      1514. 19473 else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
      1515. -
      1516. 28 ret = [null, m[1], m[2]];
      1517. -
      1518. }
      1519. -
      1520. 19445 else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
      1521. -
      1522. 2079 ret = [m[1], m[2], null];
      1523. -
      1524. }
      1525. -
      1526. else {
      1527. -
      1528. 17366 ret = [null, s, null];
      1529. +
      1530. __getMirationFiles:function () {
      1531. +
      1532. 18 var ret = new Promise();
      1533. +
      1534. 18 var upMigrations = [], downMigrations = [], target = this.target;
      1535. +
      1536. 18 if (!this.__migrationFiles) {
      1537. +
      1538. 18 when(
      1539. +
      1540. this.getFileNames(),
      1541. +
      1542. this.__getAppliedMigrations()
      1543. +
      1544. ).then(hitch(this, function (res) {
      1545. +
      1546. 18 var files = res[0], appliedMigrations = res[1];
      1547. +
      1548. 18 var l = files.length, inserts = [];
      1549. +
      1550. 18 if (l > 0) {
      1551. +
      1552. 18 try {
      1553. +
      1554. 18 for (var i = 0; i < l; i++) {
      1555. +
      1556. 92 var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
      1557. +
      1558. 92 if (!isUndefined(target)) {
      1559. +
      1560. 48 var version = this.getMigrationVersionFromFile(f);
      1561. +
      1562. 48 if (version > target || (version === 0 && target === version)) {
      1563. +
      1564. 35 if (appliedMigrations.indexOf(fLowerCase) != -1) {
      1565. +
      1566. 26 downMigrations.push([f, require(file), "down"]);
      1567. +
      1568. }
      1569. +
      1570. 13 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
      1571. +
      1572. 9 upMigrations.push([f, require(file), "up"]);
      1573. +
      1574. }
      1575. +
      1576. 44 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
      1577. +
      1578. 35 upMigrations.push([f, require(file), "up"]);
      1579. +
      1580. }
      1581. +
      1582. }
      1583. +
      1584. } catch (e) {
      1585. +
      1586. 0 return ret.errback(e)
      1587. +
      1588. }
      1589. +
      1590. 18 this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
      1591. +
      1592. 18 ret.callback(this.__migrationFiles);
      1593. +
      1594. } else {
      1595. +
      1596. 0 return ret.callback();
      1597. +
      1598. }
      1599. +
      1600. }), ret);
      1601. +
      1602. } else {
      1603. +
      1604. 0 ret.callback(this.__migrationFiles);
      1605. }
      1606. -
      1607. 19662 return ret;
      1608. +
      1609. 18 return ret.promise();
      1610. },
      1611. -
      1612. /**
      1613. -
      1614. * @ignore
      1615. -
      1616. **/
      1617. -
      1618. getters:{
      1619. -
      1620. -
      1621. rowCb:function () {
      1622. -
      1623. 23052 return this.__rowCb;
      1624. -
      1625. },
      1626. -
      1627. -
      1628. identifierInputMethod:function () {
      1629. -
      1630. 14948 return this.__identifierInputMethod;
      1631. -
      1632. },
      1633. -
      1634. -
      1635. identifierOutputMethod:function () {
      1636. -
      1637. 14948 return this.__identifierOutputMethod;
      1638. -
      1639. },
      1640. -
      1641. -
      1642. firstSourceAlias:function () {
      1643. -
      1644. 579 var source = this.__opts.from;
      1645. -
      1646. 579 if (isUndefinedOrNull(source) || !source.length) {
      1647. -
      1648. 2 throw new DatasetError("No source specified for the query");
      1649. -
      1650. }
      1651. -
      1652. 577 source = source[0];
      1653. -
      1654. 577 if (isInstanceOf(source, AliasedExpression)) {
      1655. -
      1656. 20 return source.alias;
      1657. -
      1658. 557 } else if (isString(source)) {
      1659. -
      1660. 0 var parts = this._splitString(source);
      1661. -
      1662. 0 var alias = parts[2];
      1663. -
      1664. 0 return alias ? alias : source;
      1665. -
      1666. } else {
      1667. -
      1668. 557 return source;
      1669. -
      1670. }
      1671. -
      1672. },
      1673. -
      1674. firstSourceTable:function () {
      1675. -
      1676. 15 var source = this.__opts.from;
      1677. -
      1678. 15 if (isUndefinedOrNull(source) || !source.length) {
      1679. -
      1680. 1 throw new QueryError("No source specified for the query");
      1681. -
      1682. }
      1683. -
      1684. 14 var source = source[0];
      1685. -
      1686. 14 if (isInstanceOf(source, AliasedExpression)) {
      1687. -
      1688. 3 return source.expression;
      1689. -
      1690. 11 } else if (isString(source)) {
      1691. -
      1692. 0 var parts = this._splitString(source);
      1693. -
      1694. 0 return source;
      1695. -
      1696. } else {
      1697. -
      1698. 11 return source;
      1699. -
      1700. }
      1701. +
      1702. // Returns the dataset for the schema_migrations table. If no such table
      1703. +
      1704. // exists, it is automatically created.
      1705. +
      1706. _getSchemaDataset:function () {
      1707. +
      1708. 36 var ret = new Promise();
      1709. +
      1710. 36 if (!this.__schemaDataset) {
      1711. +
      1712. 18 var ds = this.db.from(this.table);
      1713. +
      1714. 18 this.__createTable().then(hitch(this, function () {
      1715. +
      1716. 18 this.__schemaDataset = ds;
      1717. +
      1718. 18 ret.callback(ds);
      1719. +
      1720. }), ret);
      1721. +
      1722. } else {
      1723. +
      1724. 18 ret.callback(this.__schemaDataset);
      1725. }
      1726. +
      1727. 36 return ret.promise();
      1728. },
      1729. -
      1730. /**
      1731. -
      1732. * @ignore
      1733. -
      1734. **/
      1735. -
      1736. setters:{
      1737. -
      1738. /**@lends patio.Dataset.prototype*/
      1739. -
      1740. -
      1741. identifierInputMethod:function (meth) {
      1742. -
      1743. 15038 this.__identifierInputMethod = meth;
      1744. -
      1745. },
      1746. +
      1747. __convertSchemaInfo:function () {
      1748. +
      1749. 1 var ret = new Promise(), c = this.column;
      1750. +
      1751. 1 var ds = this.db.from(this.table);
      1752. +
      1753. 1 this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
      1754. +
      1755. 1 this.getFileNames().then(hitch(this, function (files) {
      1756. +
      1757. 1 var l = files.length, inserts = [];
      1758. +
      1759. 1 if (l > 0) {
      1760. +
      1761. 1 for (var i = 0; i < l; i++) {
      1762. +
      1763. 7 var f = path.basename(files[i]);
      1764. +
      1765. 7 if (this.getMigrationVersionFromFile(f) <= version) {
      1766. +
      1767. 5 var insert = {};
      1768. +
      1769. 5 insert[c] = f;
      1770. +
      1771. 5 inserts.push(ds.insert(insert));
      1772. +
      1773. }
      1774. +
      1775. }
      1776. +
      1777. }
      1778. +
      1779. 1 if (inserts.length) {
      1780. +
      1781. 1 when.apply(comb, inserts).then(ret);
      1782. +
      1783. } else {
      1784. +
      1785. 0 ret.callback();
      1786. +
      1787. }
      1788. +
      1789. }), ret);
      1790. +
      1791. }), ret);
      1792. +
      1793. 1 return ret.promise();
      1794. -
      1795. identifierOutputMethod:function (meth) {
      1796. -
      1797. 15038 this.__identifierOutputMethod = meth;
      1798. -
      1799. },
      1800. +
      1801. },
      1802. -
      1803. rowCb:function (cb) {
      1804. -
      1805. 18564 if (isFunction(cb) || isNull(cb)) {
      1806. -
      1807. 18559 this.__rowCb = cb;
      1808. +
      1809. __createTable:function () {
      1810. +
      1811. 18 var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
      1812. +
      1813. 18 var ds = this.db.from(table);
      1814. +
      1815. 18 var ret = new Promise();
      1816. +
      1817. 18 when(
      1818. +
      1819. db.tableExists(table),
      1820. +
      1821. db.tableExists(intMigrationTable)
      1822. +
      1823. ).then(hitch(this, function (res) {
      1824. +
      1825. 18 var exists = res[0], intMigratorExists = res[1];
      1826. +
      1827. 18 if (!exists) {
      1828. +
      1829. 10 db.createTable(table,
      1830. +
      1831. function () {
      1832. +
      1833. 10 this.column(c, String, {primaryKey:true});
      1834. +
      1835. }).addErrback(ret);
      1836. +
      1837. 10 if (intMigratorExists) {
      1838. +
      1839. 1 db.from(intMigrationTable).all().then(hitch(this, function (versions) {
      1840. +
      1841. 1 var version;
      1842. +
      1843. 1 if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
      1844. +
      1845. 1 this.__convertSchemaInfo().then(ret);
      1846. +
      1847. } else {
      1848. +
      1849. 0 ret.callback();
      1850. +
      1851. }
      1852. +
      1853. }));
      1854. +
      1855. } else {
      1856. +
      1857. 9 ret.callback();
      1858. +
      1859. }
      1860. } else {
      1861. -
      1862. 5 throw new DatasetError("rowCb mus be a function");
      1863. +
      1864. 8 ds.columns.then(hitch(this, function (columns) {
      1865. +
      1866. 8 if (columns.indexOf(c) === -1) {
      1867. +
      1868. 0 ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
      1869. +
      1870. } else {
      1871. +
      1872. 8 ret.callback();
      1873. +
      1874. }
      1875. +
      1876. }), ret);
      1877. }
      1878. -
      1879. }
      1880. +
      1881. }), ret);
      1882. +
      1883. 18 return ret.promise();
      1884. }
      1885. },
      1886. static:{
      1887. -
      1888. COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
      1889. -
      1890. COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
      1891. -
      1892. COLUMN_REF_RE3:/^(\w+)__(\w+)$/
      1893. +
      1894. DEFAULT_SCHEMA_COLUMN:"filename",
      1895. +
      1896. DEFAULT_SCHEMA_TABLE:"schema_migrations"
      1897. }
      1898. -
      1899. }).as(module);
      1900. +
      1901. }).as(exports, "TimestampMigrator");
      1902. -
      +
    3. 1exports.run = function () {
    4. +
    5. 31 return Migrator.run.apply(Migrator, arguments);
    6. +
    7. };
    @@ -13867,7 +13892,7 @@
    Coverage92.86 - SLOC1683 + SLOC1684 LOC504 Missed36
    @@ -13875,44 +13900,45 @@
    1. 1var comb = require("comb"),
    2. -
    3. define = comb.define,
    4. -
    5. array = comb.array,
    6. -
    7. intersect = array.intersect,
    8. -
    9. compact = array.compact,
    10. -
    11. string = comb.string,
    12. -
    13. format = string.format,
    14. -
    15. argsToArray = comb.argsToArray,
    16. -
    17. isInstanceOf = comb.isInstanceOf,
    18. -
    19. isArray = comb.isArray,
    20. -
    21. isNumber = comb.isNumber,
    22. -
    23. isDate = comb.isDate,
    24. -
    25. isNull = comb.isNull,
    26. -
    27. isBoolean = comb.isBoolean,
    28. -
    29. isFunction = comb.isFunction,
    30. -
    31. isUndefined = comb.isUndefined,
    32. -
    33. isObject = comb.isObject,
    34. -
    35. isHash = comb.isHash,
    36. -
    37. merge = comb.merge,
    38. -
    39. isUndefinedOrNull = comb.isUndefinedOrNull,
    40. -
    41. isString = comb.isString,
    42. -
    43. sql = require("../sql").sql,
    44. -
    45. Expression = sql.Expression,
    46. -
    47. ComplexExpression = sql.ComplexExpression,
    48. -
    49. AliasedExpression = sql.AliasedExpression,
    50. -
    51. Identifier = sql.Identifier,
    52. -
    53. QualifiedIdentifier = sql.QualifiedIdentifier,
    54. -
    55. OrderedExpression = sql.OrderedExpression,
    56. -
    57. CaseExpression = sql.CaseExpression,
    58. -
    59. SubScript = sql.SubScript,
    60. -
    61. NumericExpression = sql.NumericExpression,
    62. -
    63. ColumnAll = sql.ColumnAll,
    64. -
    65. Cast = sql.Cast,
    66. -
    67. StringExpression = sql.StringExpression,
    68. -
    69. BooleanExpression = sql.BooleanExpression,
    70. -
    71. SQLFunction = sql.SQLFunction,
    72. -
    73. LiteralString = sql.LiteralString,
    74. -
    75. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    76. -
    77. QueryError = require("../errors").QueryError, patio;
    78. +
    79. define = comb.define,
    80. +
    81. array = comb.array,
    82. +
    83. intersect = array.intersect,
    84. +
    85. compact = array.compact,
    86. +
    87. string = comb.string,
    88. +
    89. format = string.format,
    90. +
    91. argsToArray = comb.argsToArray,
    92. +
    93. isInstanceOf = comb.isInstanceOf,
    94. +
    95. isArray = comb.isArray,
    96. +
    97. isNumber = comb.isNumber,
    98. +
    99. isDate = comb.isDate,
    100. +
    101. isNull = comb.isNull,
    102. +
    103. isBoolean = comb.isBoolean,
    104. +
    105. isFunction = comb.isFunction,
    106. +
    107. isUndefined = comb.isUndefined,
    108. +
    109. isObject = comb.isObject,
    110. +
    111. isHash = comb.isHash,
    112. +
    113. isEmpty = comb.isEmpty,
    114. +
    115. merge = comb.merge,
    116. +
    117. isUndefinedOrNull = comb.isUndefinedOrNull,
    118. +
    119. isString = comb.isString,
    120. +
    121. sql = require("../sql").sql,
    122. +
    123. Expression = sql.Expression,
    124. +
    125. ComplexExpression = sql.ComplexExpression,
    126. +
    127. AliasedExpression = sql.AliasedExpression,
    128. +
    129. Identifier = sql.Identifier,
    130. +
    131. QualifiedIdentifier = sql.QualifiedIdentifier,
    132. +
    133. OrderedExpression = sql.OrderedExpression,
    134. +
    135. CaseExpression = sql.CaseExpression,
    136. +
    137. SubScript = sql.SubScript,
    138. +
    139. NumericExpression = sql.NumericExpression,
    140. +
    141. ColumnAll = sql.ColumnAll,
    142. +
    143. Cast = sql.Cast,
    144. +
    145. StringExpression = sql.StringExpression,
    146. +
    147. BooleanExpression = sql.BooleanExpression,
    148. +
    149. SQLFunction = sql.SQLFunction,
    150. +
    151. LiteralString = sql.LiteralString,
    152. +
    153. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    154. +
    155. QueryError = require("../errors").QueryError, patio;
    156. 1var Dataset;
    157. @@ -14323,7 +14349,7 @@
    158. 59 return new QualifiedIdentifier(table, e);
    159. 217 } else if (isInstanceOf(e, OrderedExpression)) {
    160. 2 return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending,
    161. -
    162. {nulls:e.nulls});
    163. +
    164. {nulls:e.nulls});
    165. 215 } else if (isInstanceOf(e, AliasedExpression)) {
    166. 72 return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias);
    167. 143 } else if (isInstanceOf(e, CaseExpression)) {
    168. @@ -14491,9 +14517,9 @@
    169. 1440 var columns = this.__opts.columns, ret = "";
    170. 1440 if (columns && columns.length) {
    171. 1387 ret = " (" + columns.map(
    172. -
    173. function (c) {
    174. -
    175. 6691 return c.toString(this);
    176. -
    177. }, this).join(this._static.COMMA_SEPARATOR) + ")";
    178. +
    179. function (c) {
    180. +
    181. 6691 return c.toString(this);
    182. +
    183. }, this).join(this._static.COMMA_SEPARATOR) + ")";
    184. }
    185. 1440 return ret;
    186. },
    187. @@ -14605,8 +14631,8 @@
    188. 0 return this.complexExpressionSql("EQ", args);
    189. } else {
    190. 0 return this.complexExpressionSql("OR",
    191. -
    192. [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
    193. -
    194. null)]);
    195. +
    196. [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
    197. +
    198. null)]);
    199. }
    200. 6454 } else if (["IN", "NOTIN"].indexOf(op) != -1) {
    201. @@ -14637,8 +14663,8 @@
    202. //literal so that if values is an array of two element arrays, it
    203. //will be treated as a value list instead of a condition specifier.
    204. 4 return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
    205. -
    206. ComplexExpression.IN_OPERATORS[op],
    207. -
    208. valArray ? this._arraySql(vals) : this.literal(vals));
    209. +
    210. ComplexExpression.IN_OPERATORS[op],
    211. +
    212. valArray ? this._arraySql(vals) : this.literal(vals));
    213. }
    214. }
    215. else {
    216. @@ -14652,13 +14678,13 @@
    217. }
    218. } else {
    219. 10 return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
    220. -
    221. ComplexExpression.IN_OPERATORS[op], this.literal(vals));
    222. +
    223. ComplexExpression.IN_OPERATORS[op], this.literal(vals));
    224. }
    225. }
    226. 6437 } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
    227. 5427 var l = args[0];
    228. 5427 return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
    229. -
    230. this.literal(args[1]));
    231. +
    232. this.literal(args[1]));
    233. 1010 } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
    234. 976 return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
    235. 34 } else if (op == "NOT") {
    236. @@ -14701,7 +14727,7 @@
    237. }
    238. 925 var tref = this.__tableRef(table);
    239. 925 return string.format(" %s %s", this._joinTypeSql(jc.joinType),
    240. -
    241. tableAlias ? this.__asSql(tref, tableAlias) : tref);
    242. +
    243. tableAlias ? this.__asSql(tref, tableAlias) : tref);
    244. },
    245. /**
    246. @@ -14774,11 +14800,11 @@
    247. */
    248. qualifiedIdentifierSql:function (qcr) {
    249. 4532 return [qcr.table, qcr.column].map(
    250. -
    251. function (x) {
    252. -
    253. 9064 return [QualifiedIdentifier, Identifier, String].some(function (c) {
    254. -
    255. 25671 return x instanceof c
    256. -
    257. }) ? this.literal(x) : this.quoteIdentifier(x)
    258. -
    259. }, this).join('.');
    260. +
    261. function (x) {
    262. +
    263. 9064 return [QualifiedIdentifier, Identifier, String].some(function (c) {
    264. +
    265. 25671 return x instanceof c
    266. +
    267. }) ? this.literal(x) : this.quoteIdentifier(x)
    268. +
    269. }, this).join('.');
    270. },
    271. /**
    272. @@ -14813,12 +14839,12 @@
    273. 33693 var i = this.__identifierInputMethod;
    274. 33693 v = v.toString(this);
    275. 33693 return !isUndefinedOrNull(i) ?
    276. -
    277. isFunction(v[i]) ?
    278. -
    279. v[i]() :
    280. -
    281. isFunction(comb[i]) ?
    282. -
    283. comb[i](v)
    284. -
    285. : v
    286. -
    287. : v;
    288. +
    289. isFunction(v[i]) ?
    290. +
    291. v[i]() :
    292. +
    293. isFunction(comb[i]) ?
    294. +
    295. comb[i](v)
    296. +
    297. : v
    298. +
    299. : v;
    300. },
    301. /**
    302. @@ -14831,12 +14857,12 @@
    303. 19885 (v == '' && (v = 'untitled'));
    304. 19885 var i = this.__identifierOutputMethod;
    305. 19885 return !isUndefinedOrNull(i) ?
    306. -
    307. isFunction(v[i]) ?
    308. -
    309. v[i]() :
    310. -
    311. isFunction(comb[i]) ?
    312. -
    313. comb[i](v)
    314. -
    315. : v
    316. -
    317. : v;
    318. +
    319. isFunction(v[i]) ?
    320. +
    321. v[i]() :
    322. +
    323. isFunction(comb[i]) ?
    324. +
    325. comb[i](v)
    326. +
    327. : v
    328. +
    329. : v;
    330. },
    331. /**
    332. @@ -14932,9 +14958,9 @@
    333. */
    334. _joinTypeSql:function (joinType) {
    335. 922 return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g,
    336. -
    337. function (m) {
    338. -
    339. 1127 return m.toUpperCase() + " ";
    340. -
    341. }
    342. +
    343. function (m) {
    344. +
    345. 1127 return m.toUpperCase() + " ";
    346. +
    347. }
    348. ).trimRight() + " JOIN";
    349. },
    350. @@ -15060,10 +15086,10 @@
    351. 6059 var table = parts[0], column = parts[1], alias = parts[2];
    352. 6059 if (!alias) {
    353. 6059 return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'"
    354. -
    355. + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
    356. +
    357. + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
    358. } else {
    359. 0 return this.literal(new AliasedExpression(column
    360. -
    361. && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
    362. +
    363. && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
    364. }
    365. },
    366. @@ -15356,9 +15382,9 @@
    367. 0 throw new QueryError("No source specified for the query");
    368. }
    369. 6657 return " " + source.map(
    370. -
    371. function (s) {
    372. -
    373. 6971 return this.__tableRef(s);
    374. -
    375. }, this).join(this._static.COMMA_SEPARATOR);
    376. +
    377. function (s) {
    378. +
    379. 6971 return this.__tableRef(s);
    380. +
    381. }, this).join(this._static.COMMA_SEPARATOR);
    382. },
    383. /**
    384. @@ -15904,7 +15930,7 @@
    385. 2734 var a = [];
    386. 2734 var ret = new Promise().classic(cb);
    387. 2734 this.forEach(hitch(this, function (r) {
    388. -
    389. 2936 a.push(r);
    390. +
    391. 2947 a.push(r);
    392. })).then(hitch(this, function () {
    393. 2731 this.postLoad(a);
    394. 2731 if (block) {
    395. @@ -17150,10 +17176,10 @@
    396. *
    397. */
    398. constructor:function () {
    399. -
    400. 3218 if (comb.isUndefinedOrNull(this.__associations)) {
    401. -
    402. 3055 this.__associations = {};
    403. +
    404. 3107 if (comb.isUndefinedOrNull(this.__associations)) {
    405. +
    406. 3000 this.__associations = {};
    407. }
    408. -
    409. 3218 this._super(arguments);
    410. +
    411. 3107 this._super(arguments);
    412. },
    413. reload:function () {
    414. @@ -18080,240 +18106,29 @@
    415. * });
    416. * }
    417. *
    418. -
    419. *
    420. -
    421. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    422. -
    423. *
    424. -
    425. * @throws {patio.ModelError} if name is not a function or string.
    426. -
    427. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    428. -
    429. */
    430. -
    431. validate:function (name) {
    432. -
    433. 41 this.__initValidation();
    434. -
    435. 41 var ret;
    436. -
    437. 41 if (isFunction(name)) {
    438. -
    439. 1 name.apply(this, [this.__getValidator.bind(this)]);
    440. -
    441. 1 ret = this;
    442. -
    443. 40 } else if (isString(name)) {
    444. -
    445. 40 ret = this.__getValidator(name);
    446. -
    447. } else {
    448. -
    449. 0 throw new ModelError("name is must be a string or function when validating");
    450. -
    451. }
    452. -
    453. 41 return ret;
    454. -
    455. }
    456. -
    457. }
    458. -
    459. -
    460. }).as(module);
    461. -
    -
    - -
    - - - - - -
    -
    database/logging.js
    -
    -
    - Coverage97.96 - SLOC193 - LOC49 - Missed1 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. define = comb.define,
    4. -
    5. Promise = comb.Promise,
    6. -
    7. isFunction = comb.isFunction,
    8. -
    9. logging = comb.logging,
    10. -
    11. Logger = logging.Logger,
    12. -
    13. hitch = comb.hitch,
    14. -
    15. format = comb.string.format,
    16. -
    17. QueryError = require("../errors").QueryError;
    18. -
    19. -
    20. -
    21. 1var LOGGER = Logger.getLogger("patio.Database");
    22. -
    23. -
    24. 1define(null, {
    25. -
    26. instance:{
    27. -
    28. /**@lends patio.Database.prototype*/
    29. -
    30. -
    31. /**
    32. -
    33. * Logs an INFO level message to the "patio.Database" logger.
    34. -
    35. */
    36. -
    37. logInfo:function () {
    38. -
    39. 8337 if (LOGGER.isInfo) {
    40. -
    41. 8337 LOGGER.info.apply(LOGGER, arguments);
    42. -
    43. }
    44. -
    45. },
    46. -
    47. -
    48. /**
    49. -
    50. * Logs a DEBUG level message to the "patio.Database" logger.
    51. -
    52. */
    53. -
    54. logDebug:function () {
    55. -
    56. 8027 if (LOGGER.isDebug) {
    57. -
    58. 8027 LOGGER.debug.apply(LOGGER, arguments);
    59. -
    60. }
    61. -
    62. },
    63. -
    64. -
    65. /**
    66. -
    67. * Logs an ERROR level message to the "patio.Database" logger.
    68. -
    69. */
    70. -
    71. logError:function (error) {
    72. -
    73. 75 if (LOGGER.isError) {
    74. -
    75. 75 LOGGER.error.apply(LOGGER, arguments);
    76. -
    77. }
    78. -
    79. },
    80. -
    81. -
    82. /**
    83. -
    84. * Logs a WARN level message to the "patio.Database" logger.
    85. -
    86. */
    87. -
    88. logWarn:function () {
    89. -
    90. 1 if (LOGGER.isWarn) {
    91. -
    92. 1 LOGGER.warn.apply(LOGGER, arguments);
    93. -
    94. }
    95. -
    96. },
    97. -
    98. -
    99. /**
    100. -
    101. * Logs a TRACE level message to the "patio.Database" logger.
    102. -
    103. */
    104. -
    105. logTrace:function () {
    106. -
    107. 1 if (LOGGER.isTrace) {
    108. -
    109. 1 LOGGER.trace.apply(LOGGER, arguments);
    110. -
    111. }
    112. -
    113. },
    114. -
    115. -
    116. /**
    117. -
    118. * Logs a FATAL level message to the "patio.Database" logger.
    119. -
    120. */
    121. -
    122. logFatal:function () {
    123. -
    124. 1 if (LOGGER.isFatal) {
    125. -
    126. 1 LOGGER.fatal.apply(LOGGER, arguments);
    127. -
    128. }
    129. -
    130. },
    131. -
    132. -
    133. /* Yield to the block, logging any errors at error level to all loggers,
    134. -
    135. * and all other queries with the duration at warn or info level.
    136. -
    137. * */
    138. -
    139. __logAndExecute:function (sql, args, cb) {
    140. -
    141. 8101 if (isFunction(args)) {
    142. -
    143. 8100 cb = args;
    144. -
    145. 8100 args = null;
    146. -
    147. }
    148. -
    149. -
    150. 8101 if (args) {
    151. -
    152. 0 sql = format("%s; %j", sql, args);
    153. -
    154. }
    155. -
    156. 8101 sql = sql.trim();
    157. -
    158. 8101 var start = new Date();
    159. -
    160. 8101 var ret = new Promise();
    161. -
    162. 8101 if (isFunction(cb)) {
    163. -
    164. 8100 this.logInfo("Executing; %s", sql);
    165. -
    166. 8100 cb().then(hitch(this, function () {
    167. -
    168. 8026 this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
    169. -
    170. 8026 ret.callback.apply(ret, arguments);
    171. -
    172. }), hitch(this, function (err) {
    173. -
    174. 74 err = new QueryError(format("%s: %s", err.message, sql));
    175. -
    176. 74 this.logError(err);
    177. -
    178. 74 ret.errback.apply(ret, [err]);
    179. -
    180. }));
    181. -
    182. } else {
    183. -
    184. 1 throw new QueryError("CB is required");
    185. -
    186. }
    187. -
    188. 8100 return ret.promise();
    189. -
    190. },
    191. -
    192. -
    193. /*Log the given SQL and then execute it on the connection, used by
    194. -
    195. *the transaction code.
    196. -
    197. * */
    198. -
    199. __logConnectionExecute:function (conn, sql) {
    200. -
    201. 2079 return this.__logAndExecute(sql, hitch(this, function () {
    202. -
    203. 2079 return conn[this.connectionExecuteMethod](sql);
    204. -
    205. }));
    206. -
    207. },
    208. -
    209. -
    210. -
    211. getters:{
    212. -
    213. /**@lends patio.Database.prototype*/
    214. -
    215. /**
    216. -
    217. * The "patio.Database" logger.
    218. -
    219. * @field
    220. -
    221. */
    222. -
    223. logger:function () {
    224. -
    225. 2 return LOGGER;
    226. -
    227. }
    228. -
    229. }
    230. -
    231. },
    232. -
    233. -
    234. "static":{
    235. -
    236. /**@lends patio.Database*/
    237. -
    238. /**
    239. -
    240. * Logs an INFO level message to the "patio.Database" logger.
    241. -
    242. */
    243. -
    244. logInfo:function () {
    245. -
    246. 1 if (LOGGER.isInfo) {
    247. -
    248. 1 LOGGER.info.apply(LOGGER, arguments);
    249. -
    250. }
    251. -
    252. },
    253. -
    254. -
    255. /**
    256. -
    257. * Logs a DEBUG level message to the "patio.Database" logger.
    258. -
    259. */
    260. -
    261. logDebug:function () {
    262. -
    263. 1 if (LOGGER.isDebug) {
    264. -
    265. 1 LOGGER.debug.apply(LOGGER, arguments);
    266. -
    267. }
    268. -
    269. },
    270. -
    271. -
    272. /**
    273. -
    274. * Logs a ERROR level message to the "patio.Database" logger.
    275. -
    276. */
    277. -
    278. logError:function () {
    279. -
    280. 1 if (LOGGER.isError) {
    281. -
    282. 1 LOGGER.error.apply(LOGGER, arguments);
    283. -
    284. }
    285. -
    286. },
    287. -
    288. -
    289. /**
    290. -
    291. * Logs a WARN level message to the "patio.Database" logger.
    292. -
    293. */
    294. -
    295. logWarn:function () {
    296. -
    297. 1 if (LOGGER.isWarn) {
    298. -
    299. 1 LOGGER.warn.apply(LOGGER, arguments);
    300. -
    301. }
    302. -
    303. },
    304. -
    305. -
    306. /**
    307. -
    308. * Logs a TRACE level message to the "patio.Database" logger.
    309. -
    310. */
    311. -
    312. logTrace:function () {
    313. -
    314. 1 if (LOGGER.isTrace) {
    315. -
    316. 1 LOGGER.trace.apply(LOGGER, arguments);
    317. -
    318. }
    319. -
    320. },
    321. -
    322. -
    323. /**
    324. -
    325. * Logs a FATAL level message to the "patio.Database" logger.
    326. +
    327. *
    328. +
    329. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    330. +
    331. *
    332. +
    333. * @throws {patio.ModelError} if name is not a function or string.
    334. +
    335. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    336. */
    337. -
    338. logFatal:function () {
    339. -
    340. 1 if (LOGGER.isFatal) {
    341. -
    342. 1 LOGGER.fatal.apply(LOGGER, arguments);
    343. -
    344. }
    345. -
    346. },
    347. -
    348. -
    349. getters:{
    350. -
    351. /**@lends patio.Database*/
    352. -
    353. /**
    354. -
    355. * The "patio.Database" logger.
    356. -
    357. * @field
    358. -
    359. */
    360. -
    361. logger:function () {
    362. -
    363. 1 return LOGGER;
    364. +
    365. validate:function (name) {
    366. +
    367. 41 this.__initValidation();
    368. +
    369. 41 var ret;
    370. +
    371. 41 if (isFunction(name)) {
    372. +
    373. 1 name.apply(this, [this.__getValidator.bind(this)]);
    374. +
    375. 1 ret = this;
    376. +
    377. 40 } else if (isString(name)) {
    378. +
    379. 40 ret = this.__getValidator(name);
    380. +
    381. } else {
    382. +
    383. 0 throw new ModelError("name is must be a string or function when validating");
    384. }
    385. +
    386. 41 return ret;
    387. }
    388. }
    389. -
    390. }).as(module);
    +
  • }).as(module);
  • +
  • @@ -18324,10 +18139,10 @@
    - Coverage98.84 - SLOC2136 - LOC431 - Missed5 + Coverage97.71 + SLOC2160 + LOC436 + Missed10
    @@ -19850,6 +19665,30 @@
  • },
  • /**
  • +
  • * Selects the columns if only if there is not already select sources.
  • +
  • *
  • +
  • * @example
  • +
  • *
  • +
  • * var ds = DB.from("items"); //SELECT * FROM items
  • +
  • *
  • +
  • * ds.select("a"); //SELECT a FROM items;
  • +
  • * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
  • +
  • * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
  • +
  • *
  • +
  • * @param cols columns to select if there is not already select sources.
  • +
  • * @return {patio.Dataset} a cloned dataset with the appropriate select sources.
  • +
  • */
  • +
  • selectIfNoSource:function (cols) {
  • +
  • 0 var ret;
  • +
  • 0 if (!this.hasSelectSource) {
  • +
  • 0 ret = this.select.apply(this, arguments);
  • +
  • } else {
  • +
  • 0 ret = this.mergeOptions();
  • +
  • }
  • +
  • 0 return ret;
  • +
  • },
  • +
  • +
  • /**
  • * Returns a copy of the dataset with the given columns added
  • * to the existing selected columns. If no columns are currently selected,
  • * it will select the columns given in addition to *.
  • @@ -20470,6 +20309,217 @@
  • as(module);
  • +
    + + + + + +
    +
    database/logging.js
    +
    +
    + Coverage97.96 + SLOC193 + LOC49 + Missed1 +
    +
    +
    1. 1var comb = require("comb"),
    2. +
    3. define = comb.define,
    4. +
    5. Promise = comb.Promise,
    6. +
    7. isFunction = comb.isFunction,
    8. +
    9. logging = comb.logging,
    10. +
    11. Logger = logging.Logger,
    12. +
    13. hitch = comb.hitch,
    14. +
    15. format = comb.string.format,
    16. +
    17. QueryError = require("../errors").QueryError;
    18. +
    19. +
    20. +
    21. 1var LOGGER = Logger.getLogger("patio.Database");
    22. +
    23. +
    24. 1define(null, {
    25. +
    26. instance:{
    27. +
    28. /**@lends patio.Database.prototype*/
    29. +
    30. +
    31. /**
    32. +
    33. * Logs an INFO level message to the "patio.Database" logger.
    34. +
    35. */
    36. +
    37. logInfo:function () {
    38. +
    39. 8337 if (LOGGER.isInfo) {
    40. +
    41. 8337 LOGGER.info.apply(LOGGER, arguments);
    42. +
    43. }
    44. +
    45. },
    46. +
    47. +
    48. /**
    49. +
    50. * Logs a DEBUG level message to the "patio.Database" logger.
    51. +
    52. */
    53. +
    54. logDebug:function () {
    55. +
    56. 8027 if (LOGGER.isDebug) {
    57. +
    58. 8027 LOGGER.debug.apply(LOGGER, arguments);
    59. +
    60. }
    61. +
    62. },
    63. +
    64. +
    65. /**
    66. +
    67. * Logs an ERROR level message to the "patio.Database" logger.
    68. +
    69. */
    70. +
    71. logError:function (error) {
    72. +
    73. 75 if (LOGGER.isError) {
    74. +
    75. 75 LOGGER.error.apply(LOGGER, arguments);
    76. +
    77. }
    78. +
    79. },
    80. +
    81. +
    82. /**
    83. +
    84. * Logs a WARN level message to the "patio.Database" logger.
    85. +
    86. */
    87. +
    88. logWarn:function () {
    89. +
    90. 1 if (LOGGER.isWarn) {
    91. +
    92. 1 LOGGER.warn.apply(LOGGER, arguments);
    93. +
    94. }
    95. +
    96. },
    97. +
    98. +
    99. /**
    100. +
    101. * Logs a TRACE level message to the "patio.Database" logger.
    102. +
    103. */
    104. +
    105. logTrace:function () {
    106. +
    107. 1 if (LOGGER.isTrace) {
    108. +
    109. 1 LOGGER.trace.apply(LOGGER, arguments);
    110. +
    111. }
    112. +
    113. },
    114. +
    115. +
    116. /**
    117. +
    118. * Logs a FATAL level message to the "patio.Database" logger.
    119. +
    120. */
    121. +
    122. logFatal:function () {
    123. +
    124. 1 if (LOGGER.isFatal) {
    125. +
    126. 1 LOGGER.fatal.apply(LOGGER, arguments);
    127. +
    128. }
    129. +
    130. },
    131. +
    132. +
    133. /* Yield to the block, logging any errors at error level to all loggers,
    134. +
    135. * and all other queries with the duration at warn or info level.
    136. +
    137. * */
    138. +
    139. __logAndExecute:function (sql, args, cb) {
    140. +
    141. 8101 if (isFunction(args)) {
    142. +
    143. 8100 cb = args;
    144. +
    145. 8100 args = null;
    146. +
    147. }
    148. +
    149. +
    150. 8101 if (args) {
    151. +
    152. 0 sql = format("%s; %j", sql, args);
    153. +
    154. }
    155. +
    156. 8101 sql = sql.trim();
    157. +
    158. 8101 var start = new Date();
    159. +
    160. 8101 var ret = new Promise();
    161. +
    162. 8101 if (isFunction(cb)) {
    163. +
    164. 8100 this.logInfo("Executing; %s", sql);
    165. +
    166. 8100 cb().then(hitch(this, function () {
    167. +
    168. 8026 this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
    169. +
    170. 8026 ret.callback.apply(ret, arguments);
    171. +
    172. }), hitch(this, function (err) {
    173. +
    174. 74 err = new QueryError(format("%s: %s", err.message, sql));
    175. +
    176. 74 this.logError(err);
    177. +
    178. 74 ret.errback.apply(ret, [err]);
    179. +
    180. }));
    181. +
    182. } else {
    183. +
    184. 1 throw new QueryError("CB is required");
    185. +
    186. }
    187. +
    188. 8100 return ret.promise();
    189. +
    190. },
    191. +
    192. +
    193. /*Log the given SQL and then execute it on the connection, used by
    194. +
    195. *the transaction code.
    196. +
    197. * */
    198. +
    199. __logConnectionExecute:function (conn, sql) {
    200. +
    201. 2079 return this.__logAndExecute(sql, hitch(this, function () {
    202. +
    203. 2079 return conn[this.connectionExecuteMethod](sql);
    204. +
    205. }));
    206. +
    207. },
    208. +
    209. +
    210. +
    211. getters:{
    212. +
    213. /**@lends patio.Database.prototype*/
    214. +
    215. /**
    216. +
    217. * The "patio.Database" logger.
    218. +
    219. * @field
    220. +
    221. */
    222. +
    223. logger:function () {
    224. +
    225. 2 return LOGGER;
    226. +
    227. }
    228. +
    229. }
    230. +
    231. },
    232. +
    233. +
    234. "static":{
    235. +
    236. /**@lends patio.Database*/
    237. +
    238. /**
    239. +
    240. * Logs an INFO level message to the "patio.Database" logger.
    241. +
    242. */
    243. +
    244. logInfo:function () {
    245. +
    246. 1 if (LOGGER.isInfo) {
    247. +
    248. 1 LOGGER.info.apply(LOGGER, arguments);
    249. +
    250. }
    251. +
    252. },
    253. +
    254. +
    255. /**
    256. +
    257. * Logs a DEBUG level message to the "patio.Database" logger.
    258. +
    259. */
    260. +
    261. logDebug:function () {
    262. +
    263. 1 if (LOGGER.isDebug) {
    264. +
    265. 1 LOGGER.debug.apply(LOGGER, arguments);
    266. +
    267. }
    268. +
    269. },
    270. +
    271. +
    272. /**
    273. +
    274. * Logs a ERROR level message to the "patio.Database" logger.
    275. +
    276. */
    277. +
    278. logError:function () {
    279. +
    280. 1 if (LOGGER.isError) {
    281. +
    282. 1 LOGGER.error.apply(LOGGER, arguments);
    283. +
    284. }
    285. +
    286. },
    287. +
    288. +
    289. /**
    290. +
    291. * Logs a WARN level message to the "patio.Database" logger.
    292. +
    293. */
    294. +
    295. logWarn:function () {
    296. +
    297. 1 if (LOGGER.isWarn) {
    298. +
    299. 1 LOGGER.warn.apply(LOGGER, arguments);
    300. +
    301. }
    302. +
    303. },
    304. +
    305. +
    306. /**
    307. +
    308. * Logs a TRACE level message to the "patio.Database" logger.
    309. +
    310. */
    311. +
    312. logTrace:function () {
    313. +
    314. 1 if (LOGGER.isTrace) {
    315. +
    316. 1 LOGGER.trace.apply(LOGGER, arguments);
    317. +
    318. }
    319. +
    320. },
    321. +
    322. +
    323. /**
    324. +
    325. * Logs a FATAL level message to the "patio.Database" logger.
    326. +
    327. */
    328. +
    329. logFatal:function () {
    330. +
    331. 1 if (LOGGER.isFatal) {
    332. +
    333. 1 LOGGER.fatal.apply(LOGGER, arguments);
    334. +
    335. }
    336. +
    337. },
    338. +
    339. +
    340. getters:{
    341. +
    342. /**@lends patio.Database*/
    343. +
    344. /**
    345. +
    346. * The "patio.Database" logger.
    347. +
    348. * @field
    349. +
    350. */
    351. +
    352. logger:function () {
    353. +
    354. 1 return LOGGER;
    355. +
    356. }
    357. +
    358. }
    359. +
    360. }
    361. +
    362. +
    363. }).as(module);
    +
    +
    diff --git a/docs/patio.html b/docs/patio.html index cb1c74bb..9625917a 100644 --- a/docs/patio.html +++ b/docs/patio.html @@ -959,8 +959,10 @@

    Source
     function (opts){
    -   comb.logger.configure();
    -   LOGGER.level = "info";
    +   comb.logger.configure(opts);
    +   if (!opts) {
    +       LOGGER.level = "info";
    +   }
                
     }
         
    diff --git a/docs/patio_Dataset.html b/docs/patio_Dataset.html index 71c7ccc9..a1c82fc1 100644 --- a/docs/patio_Dataset.html +++ b/docs/patio_Dataset.html @@ -2624,6 +2624,20 @@ +
  • + + + F + + + + P + + + selectIfNoSource + +
  • +
  • @@ -3298,9 +3312,11 @@

    Actions

    }
  • firstSourceAliasString

    The first source (primary table) for this dataset. If the table is aliased, returns the aliased name. throws a {patio.DatasetError} tf the dataset doesn't have a table.

       DB.from("table").firstSourceAlias;   //=> "table"    DB.from("table___t").firstSourceAlias;   //=> "t" 

    firstSourceTableString

    The first source (primary table) for this dataset. If the dataset doesn't have a table, raises a patio.erros.DatasetError.

       DB.from("table").firstSourceTable;         //=> "table"   DB.from("table___t").firstSourceTable;         //=> "t" 

    +
    hasSelectSourceBoolean

    true if this dataset already has a select sources.

    identifierInputMethodString

    this is the method that will be called on each identifier returned from the database. This value will be defaulted to whatever the identifierInputMethod is on the database used in initialization.

    identifierOutputMethodString

    this is the method that will be called on each identifier sent to the database. This value will be defaulted to whatever the identifierOutputMethod is on the database used in initialization.

    isSimpleSelectAllBoolean

    Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.

         DB.from("items").isSimpleSelectAll; //=> true     DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false 

    +
    joinSourceListpatio.sql.Identifier[][]

    a list of join sources

    providesAccurateRowsMatchedbooleantrue

    Whether this dataset will provide accurate number of rows matched for delete and update statements. Accurate in this case is the number of rows matched by the dataset's filter.

    @@ -3308,6 +3324,7 @@

    Actions

    requiresSqlStandardDatebooleanfalse

    Whether the dataset requires SQL standard datetimes (false by default, as most allow strings with ISO 8601 format).

    rowCbFunction

    callback to be invoked for each row returned from the database. the return value will be used as the result of query. The rowCb can also return a promise, The resolved value of the promise will be used as result.

    +
    sourceListpatio.sql.Identifier[][]

    a list of sources for this dataset.

    supportsCtebooleantrue

    Whether the dataset supports common table expressions (the WITH clause).

    supportsDistinctOnbooleanfalse

    Whether the dataset supports the DISTINCT ON clause, false by default.

    supportsIntersectExceptbooleantrue

    Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.

    @@ -4297,9 +4314,9 @@

     function (joinType){
        return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g,
    -       function (m) {
    -           return m.toUpperCase() + " ";
    -       }
    +           function (m) {
    +               return m.toUpperCase() + " ";
    +           }
        ).trimRight() + " JOIN";
                
     }
    @@ -4886,10 +4903,10 @@ 

    var table = parts[0], column = parts[1], alias = parts[2]; if (!alias) { return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'" - + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'" + + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'" } else { return this.literal(new AliasedExpression(column - && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias)); + && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias)); } } @@ -5151,7 +5168,7 @@

    return new QualifiedIdentifier(table, e); } else if (isInstanceOf(e, OrderedExpression)) { return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending, - {nulls:e.nulls}); + {nulls:e.nulls}); } else if (isInstanceOf(e, AliasedExpression)) { return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias); } else if (isInstanceOf(e, CaseExpression)) { @@ -5829,9 +5846,9 @@

    throw new QueryError("No source specified for the query"); } return " " + source.map( - function (s) { - return this.__tableRef(s); - }, this).join(this._static.COMMA_SEPARATOR); + function (s) { + return this.__tableRef(s); + }, this).join(this._static.COMMA_SEPARATOR); }

    @@ -7091,8 +7108,8 @@

    return this.complexExpressionSql("EQ", args); } else { return this.complexExpressionSql("OR", - [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0], - null)]); + [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0], + null)]); } } else if (["IN", "NOTIN"].indexOf(op) != -1) { var cols = args[0], vals = args[1], colArray = isArray(cols), valArray = false, emptyValArray = false; @@ -7121,8 +7138,8 @@

    //literal so that if values is an array of two element arrays, it //will be treated as a value list instead of a condition specifier. return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols), - ComplexExpression.IN_OPERATORS[op], - valArray ? this._arraySql(vals) : this.literal(vals)); + ComplexExpression.IN_OPERATORS[op], + valArray ? this._arraySql(vals) : this.literal(vals)); } } else { @@ -7136,13 +7153,13 @@

    } } else { return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols), - ComplexExpression.IN_OPERATORS[op], this.literal(vals)); + ComplexExpression.IN_OPERATORS[op], this.literal(vals)); } } } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) { var l = args[0]; return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp, - this.literal(args[1])); + this.literal(args[1])); } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) { return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " ")); } else if (op == "NOT") { @@ -9529,12 +9546,12 @@

    var i = this.__identifierInputMethod; v = v.toString(this); return !isUndefinedOrNull(i) ? - isFunction(v[i]) ? - v[i]() : - isFunction(comb[i]) ? - comb[i](v) - : v - : v; + isFunction(v[i]) ? + v[i]() : + isFunction(comb[i]) ? + comb[i](v) + : v + : v; } @@ -10750,7 +10767,7 @@

    } var tref = this.__tableRef(table); return string.format(" %s %s", this._joinTypeSql(jc.joinType), - tableAlias ? this.__asSql(tref, tableAlias) : tref); + tableAlias ? this.__asSql(tref, tableAlias) : tref); } @@ -12767,12 +12784,12 @@

    (v == '' && (v = 'untitled')); var i = this.__identifierOutputMethod; return !isUndefinedOrNull(i) ? - isFunction(v[i]) ? - v[i]() : - isFunction(comb[i]) ? - comb[i](v) - : v - : v; + isFunction(v[i]) ? + v[i]() : + isFunction(comb[i]) ? + comb[i](v) + : v + : v; } @@ -13012,11 +13029,11 @@

     function (qcr){
        return [qcr.table, qcr.column].map(
    -       function (x) {
    -           return [QualifiedIdentifier, Identifier, String].some(function (c) {
    -               return x instanceof c
    -           }) ? this.literal(x) : this.quoteIdentifier(x)
    -       }, this).join('.');
    +           function (x) {
    +               return [QualifiedIdentifier, Identifier, String].some(function (c) {
    +                   return x instanceof c
    +               }) ? this.literal(x) : this.quoteIdentifier(x)
    +           }, this).join('.');
                
     }
         
    @@ -14036,6 +14053,75 @@

    + +

    + selectIfNoSource + + + Function + + + + Public + + +

    + +
    +Defined dataset/query.js + +

    Selects the columns if only if there is not already select sources. + +

    +

    + + Example +
    +var ds = DB.from("items"); //SELECT * FROM items
    +
    +ds.select("a"); //SELECT a FROM items;
    +ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
    +ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
    +        
    + + + Arguments +
      + +
    • cols :

      columns to select if there is not already select sources.

      +
    • + +
    + + + Returns +
      + +
    • patio.Dataset

      a cloned dataset with the appropriate select sources.

      +
    • + +
    + + + + + Source +
    +function (cols){
    +   var ret;
    +   if (!this.hasSelectSource) {
    +       ret = this.select.apply(this, arguments);
    +   } else {
    +       ret = this.mergeOptions();
    +   }
    +   return ret;
    +           
    +}
    +    
    + + + +

    selectMap diff --git a/docs/querying.html b/docs/querying.html index 6784893d..5373e59d 100644 --- a/docs/querying.html +++ b/docs/querying.html @@ -557,466 +557,481 @@

    Filtering With Objects

    //SELECT * FROM user WHERE id IN (1, 2, 3) AND name ~ 'Bo$'
    - User.filter({id : [1,2,3], name : /Bo$/});
    -`` 
    + User.filter({id : [1,2,3], name : /Bo$/});
    +

    This works the same as if you used two separate filter calls: -This works the same as if you used two separate filter calls: -

    // SELECT * FROM user WHERE id IN (1, 2, 3) AND name ~ 'Bo$' - User.filter({id : [1,2,3]}).filter({name : /Bo$/});

    -
    
    -If you nest hashes for a top level key then each condition will be applied to the key. This can often be used inplace of a filter block.
    -

    // SELECT * FROM user WHERE ((name ~ 'ob$') AND (name >= 'A') AND (name <= 'Z')) - User.filter({name : {like : /ob$/, between : ["A", "Z"]}}); +

     // SELECT * FROM user WHERE id IN (1, 2, 3) AND name ~ 'Bo$'
    + User.filter({id : [1,2,3]}).filter({name : /Bo$/});
    +

    If you nest hashes for a top level key then each condition will be applied to the key. This can often be used inplace of a filter block.

    -

    // SELECT * FROM user WHERE ((name ~ 'ob$') AND (name >= 'A') AND (name <= 'Z')) +

     // SELECT * FROM user WHERE ((name ~ 'ob$') AND (name >= 'A') AND (name <= 'Z'))
    + User.filter({name : {like : /ob$/, between : ["A", "Z"]}});
    +
    +  // SELECT * FROM user WHERE ((name ~ 'ob$') AND (name >= 'A') AND (name <= 'Z'))
      User.filter(function(){
         this.name.like(/ob$/).and(this.name.between("A", "Z"));
    - });
    -

    -
    
    -###Array of Two Element Arrays
    + });
    +

    Array of Two Element Arrays

    +

    If you use an array of two element arrays, it is treated as an Object. The only advantage to using an array of two element arrays is that it allows you to use values other than strings for keys, so you can do: -If you use an array of two element arrays, it is treated as an Object. The only advantage to using an array of two element arrays is that it allows you to use values other than strings for keys, so you can do:

    -

    // SELECT * FROM user WHERE name ~ 'oB$' AND name ~ '^Bo' - User.filter([["name", /oB$/], [sql.name, /^Bo/]]);

    -
    
    -###Filter Blocks
    +
     // SELECT * FROM user WHERE name ~ 'oB$' AND name ~ '^Bo'
    + User.filter([["name", /oB$/], [sql.name, /^Bo/]]);
    +

    Filter Blocks

    +

    Functions can also be provided to the filter method. Functions act as a "virtual" filter. -Functions can also be provided to the filter method. Functions act as a "virtual" filter.

    -

    // SELECT * FROM user WHERE id > 5 +

    +
     // SELECT * FROM user WHERE id > 5
      User.filter(function(){
         return this.id.gt(5)
    - });
    + });
    +

    If you provde both regular arguments and a function the results will be ANDed together. +

    -
    
    -If you provde both regular arguments and a function the results will be ANDed together.
    -

    // SELECT * FROM user WHERE name >= 'K' AND name <= 'M' AND id > 5
    User.filter({name : {between : ['K', 'M']}}, function(){ +

    // SELECT * FROM user WHERE name >= 'K' AND name <= 'M' AND id > 5                
    +User.filter({name : {between : ['K', 'M']}}, function(){
         return this.id.gt(5);
    -}); 
    +});
    +

    Strings

    +

    If you have a Boolean column in the database you can provide just the column name. +

    -
    
    -###Strings
    +
     // SELECT * FROM user WHERE is_active
    + User.filter("isActive");
    +

    Note: if you want the literal representation of string you must use the (literal)[./patio_sql.html#.literal] method. -If you have a Boolean column in the database you can provide just the column name.

    -

    // SELECT * FROM user WHERE is_active - User.filter("isActive");

    -
    
    -**Note:** if you want the literal representation of string you must use the (literal)[./patio_sql.html#.literal] method.
    -

    // SELECT * FROM user WHERE name < 'A' - User.filter(sql.literal("name < 'A'")); +

     // SELECT * FROM user WHERE name < 'A'
    + User.filter(sql.literal("name < 'A'"));
    +

    Expressions

    +

    patio SQL expressions are instances of subclasses of [Epression][./patio_sql_Expression.html]. +

    -
    
    -###Expressions
    +
    //SELECT * FROM user WHERE name LIKE 'B%'
    +User.filter(sql.name.like('B%'));
    +

    In this case patio.sql.StringExpression.like returns a patio.sql.BooleanExpression object, which is used directly in the filter. -patio SQL expressions are instances of subclasses of [Epression][./patio_sql_Expression.html].

    -

    //SELECT * FROM user WHERE name LIKE 'B%' -User.filter(sql.name.like('B%'));

    -
    
    -In this case [patio.sql.StringExpression](./patio_sql_StringExpression.html).[like](./patio_sql_StringExpression.html#like) returns a [patio.sql.BooleanExpression](./patio_sql_BooleanExpression.html) object, which is used directly in the filter.
    +

    You can use the sql methodMissing feature to create arbitrary complex expression. -You can use the sql methodMissing feature to create arbitrary complex expression.

    -

    // SELECT * FROM user WHERE name LIKE 'B%' AND (b = 1 OR c != 3) -User.filter(sql.name.like('B%').and(sql.b.eq(1).or(sql.c.neq(3))));

    -
    
    -You can combine these expression operators with functions:
    -

    // SELECT * FROM user WHERE ((a <= 1) OR NOT b(c) OR NOT d) +

    // SELECT * FROM user WHERE name LIKE 'B%' AND (b = 1 OR c != 3)
    +User.filter(sql.name.like('B%').and(sql.b.eq(1).or(sql.c.neq(3))));
    +

    You can combine these expression operators with functions: + +

    +
    // SELECT * FROM user WHERE ((a <= 1) OR NOT b(c) OR NOT d)
     User.filter(function(){
         return this.a.gt(1).and(this.b("c").and(this.d)).not();
     });
     ``
     
    -

    -

    Strings with Placeholders

    -

    patio also supports place holder strings. +###Strings with Placeholders -

    -
    // SELECT * FROM user WHERE name LIKE 'B%'
    -User.filter("name LIKE ?", 'B%')
    -

    This is the most common type of placeholder, where each question mark is substituted with the corresponding argument. +patio also supports place holder strings.

    +

    // SELECT * FROM user WHERE name LIKE 'B%' +User.filter("name LIKE ?", 'B%')

    -
    // SELECT * FROM user WHERE name LIKE 'B%' AND id = 1
    -User.filter("name LIKE ? AND id = ?", 'B%', 1)
    -

    You can also use named placeholders with an object, where the named placeholders use {} that contain the placeholder key name - +

    
    +This is the most common type of placeholder, where each question mark is substituted with the corresponding argument.
    +

    // SELECT * FROM user WHERE name LIKE 'B%' AND id = 1 +User.filter("name LIKE ? AND id = ?", 'B%', 1)

    -
    //SELECT * FROM user WHERE name LIKE 'B%' AND id = 1
    -User.filter("name LIKE {name} AND id = {id}", {name : 'B%', id : 1});
    -

    Literal Strings

    -

    You can also provide a literal string using the literal - +

    
    +You can also use named placeholders with an object, where the named placeholders use `{}` that contain the placeholder key name
    +

    //SELECT * FROM user WHERE name LIKE 'B%' AND id = 1 +User.filter("name LIKE {name} AND id = {id}", {name : 'B%', id : 1});

    -
    // SELECT * FROM user WHERE id = 2
    -User.filter(sql.literal("id = 2"))
    -

    However, if you are using any untrusted input, you should use placeholders. In general, unless you are hardcoding values in the strings, you should use placeholders. You should never pass a string that has been built using concatenation becuase it can lead to SQL injection. +

    
    +###Literal Strings
     
    +You can also provide a literal string using the [literal](./patio_sql.html#.literal)
    +

    // SELECT * FROM user WHERE id = 2 +User.filter(sql.literal("id = 2"))

    -
    //id is some user input
    +
    
    +However, if you are using any untrusted input, you should use placeholders. In general, unless you are hardcoding values in the strings, you should use placeholders. You should never pass a string that has been built using concatenation becuase it can lead to SQL injection.
    +

    //id is some user input - User.filter("id = " + id) //id could be anything so dont do it! +

    +

    User.filter("id = " + id) //id could be anything so dont do it! User.filter("id = ?", id) //Do this as patio will escape it - User.filter({ id : id}) // Best solution!

    -

    Inverting filters

    -

    You can use the invert method. - + User.filter({ id : id}) // Best solution!

    -
    // SELECT * FROM user WHERE id != 5
    -User.filter({id : 5}).invert(); 
    +
    
    +###Inverting filters               
     
    -//OR
    +You can use the [invert](.patio_Dataset.html#invert) method.
    +

    // SELECT * FROM user WHERE id != 5 +User.filter({id : 5}).invert(); - // SELECT * FROM user WHERE id != 5 -User.filter({id : {neq : 5}});

    -

    NOTE: the invert method inverts the entire filter! +

    +

    //OR

    -
    // SELECT * FROM user WHERE id != 5 OR name <= 'A'
    +

    // SELECT * FROM user WHERE id != 5 +User.filter({id : {neq : 5}}); +

    +
    
    +**NOTE:** the [invert](./patio_Dataset.html#invert) method inverts the entire filter!
    +

    // SELECT * FROM user WHERE id != 5 OR name <= 'A' User.filter({id : 5}).filter(function(){ return this.name.gt('A'); -}).invert();

    -

    Excluding filters

    -

    You can use exclude to invert only specific filters: - +}).invert();

    -
    // SELECT * FROM user WHERE id != 5
    +
    
    +###Excluding filters
    +You can use [exclude](./patio_Dataset.html#exclude) to invert only specific filters:
    +

    // SELECT * FROM user WHERE id != 5 User.exclude({id : 5}); -// SELECT * FROM user WHERE id = 5 OR name <= 'A' +

    +

    // SELECT * FROM user WHERE id = 5 OR name <= 'A' User.filter({id : 5}).exclude(function(){ return this.name.gt('A') -});

    -

    So to do a NOT IN with an array: - -

    -
    // SELECT * FROM user WHERE id NOT IN (1, 2)
    -User.exclude({id : [1, 2]});
    -

    Or to use the NOT LIKE operator: - +});

    -
    // SELECT * FROM user WHERE name NOT LIKE '%o%'
    -User.exclude(sql.name.like('%o%'))
    -

    Removing

    -

    To remove all existing filters, use the unfiltered method: - +

    
    +So to do a NOT IN with an array:
    +

    // SELECT * FROM user WHERE id NOT IN (1, 2) +User.exclude({id : [1, 2]});

    -
     // SELECT * FROM user
    - User.filter({id : 1}).unfiltered();
    -

    Ordering

    -

    To add order to an SQL statement use the order method. - +

    Or to use the NOT LIKE operator:
    +

    // SELECT * FROM user WHERE name NOT LIKE '%o%' +User.exclude(sql.name.like('%o%'))

    -
     // SELECT * FROM user ORDER BY id
    - User.order("id");
    -

    You can also provide multiple columns to the order method. +

    
    +###Removing
     
    +To remove all existing filters, use the [unfiltered](./patio_Dataset.html#unfiltered) method:
    +

    // SELECT * FROM user + User.filter({id : 1}).unfiltered();

    -
     // SELECT * FROM album ORDER BY user_id, id
    - User.order("userId", "id");
    -

    Note: order replaces any existing order +

    
    +##Ordering
     
    +To add order to an SQL statement use the [order](./patio_Dataset.html#order) method.
    +

    // SELECT * FROM user ORDER BY id + User.order("id");

    -
     User.order("id").order("name");
    - // SELECT * FROM user ORDER BY name
    -

    If you want to append a column to the existing order use the orderAppend method. - +

    
    +You can also provide multiple columns to the order method.
    +

    // SELECT * FROM album ORDER BY user_id, id + User.order("userId", "id");

    -
     // SELECT * FROM user ORDER BY id, name
    - User.order("id").orderAppend("name");
    -

    If you want to prepend a column to the existing order use the orderPrepend method. - +

    **Note:** order replaces any existing order
    +

    User.order("id").order("name"); + // SELECT * FROM user ORDER BY name

    -
     User.order("id").orderPrepend("name");
    - // SELECT * FROM user ORDER BY name, id
    -

    Reversing

    -

    To reverse the order of a SQL query use the reverse method. - +

    
    +If you want to append a column to the existing order use the [orderAppend](./patio_Dataset.html#orderAppend) method.
    +

    // SELECT * FROM user ORDER BY id, name + User.order("id").orderAppend("name");

    -
     // SELECT FROM user ORDER BY id DESC
    - User.order("id").reverse();
    -

    You can also use the desc method. - +

    
    +If you want to prepend a column to the existing order use the [orderPrepend](./patio_Dataset.html#orderPrepend) method.
    +

    User.order("id").orderPrepend("name"); + // SELECT * FROM user ORDER BY name, id

    -
     // SELECT FROM user ORDER BY id DESC
    - User.order(sql.id.desc());
    -

    This allows for finer grained control of the ordering of columns. +

    
    +###Reversing                
     
    +To reverse the order of a SQL query use the [reverse](./patio_Dataset.html#reverse) method.
    +

    // SELECT FROM user ORDER BY id DESC + User.order("id").reverse();

    -
     // SELECT FROM user ORDER BY name, id DESC
    - User.order("name", sql.id.desc());
    -

    Removing

    -

    To remove ordering use the unordered method: - - +

    
    +You can also use the [desc](./patio_sql_OrderedMethods.html#desc) method.
    +

    // SELECT FROM user ORDER BY id DESC + User.order(sql.id.desc());

    -
     User.order("name").unordered();
    - // SELECT * FROM user
    -

    Selecting columns

    -

    To only return certain columns use the select method. - +

    
    +This allows for finer grained control of the ordering of columns.
    +

    // SELECT FROM user ORDER BY name, id DESC + User.order("name", sql.id.desc());

    -
    // SELECT id, name FROM user
    -User.select("id", "name");
    -

    NOTE: If you are dealing with Model objects, you'll want to include the primary key if you want to update or remove the object. You'll also want to include any keys (primary or foreign) related to associations you plan to use. +

    
    +###Removing
     
    +To remove ordering use the [unordered](./patio_Dataset.html#unordered) method:
    +

    User.order("name").unordered(); + // SELECT * FROM user

    -

    NOTE: If a column is not selected, and you attempt to access it, you will get null: +

    
    +##Selecting columns
     
    +To only return certain columns use the [select](./patio_Dataset.html#select) method.
    +

    // SELECT id, name FROM user +User.select("id", "name");

    -
    // SELECT name FROM user LIMIT 1                    
    -User.select("name").first().then(function(user){
    -    //user.id === null                    
    -});
    -

    select replaces any columns previously selected. +

    
    +**NOTE:** If you are dealing with Model objects, you'll want to include the primary key if you want to update or remove the object. You'll also want to include any keys (primary or foreign) related to associations you plan to use.
     
    +**NOTE:** If a column is not selected, and you attempt to access it, you will get null:
    +

    // SELECT name FROM user LIMIT 1
    User.select("name").first().then(function(user){ + //user.id === null
    });

    -
     User.select("id").select("name");
    - // SELECT name FROM user
    -

    Like order you can use the selectAppend method to append columns to be returned. - +

    
    +select replaces any columns previously selected.
    +

    User.select("id").select("name"); + // SELECT name FROM user

    -
    // SELECT id, name FROM user
    -User.select("id").selectAppend("name");
    -

    To remove selected columns and revert to SELECT * use the selectAll method. - +

    
    +Like order you can use the [selectAppend](./patio_Dataset.html#selectAppend) method to append columns to be returned.
    +

    // SELECT id, name FROM user +User.select("id").selectAppend("name");

    -
     User.select("id").selectAll();
    - // SELECT * FROM user
    -

    Distinct

    -

    To add a DISTINCT clause to filter out duplicate rows use the distinct method. - +

    
    +To remove selected columns and revert to `SELECT *` use the [selectAll](./patio_Dataset.html#selectAll) method.
    +

    User.select("id").selectAll(); + // SELECT * FROM user

    -

    Note: DISTINCT is separate from the select clause, +

    
    +##Distinct
     
    -

    -
    // SELECT DISTINCT name FROM user
    -User.distinct().select("name")
    -

    Limit and Offset

    -

    To limit the number of rows returned use the limit method. +To add a `DISTINCT` clause to filter out duplicate rows use the [distinct](./patio_Dataset.html#distinct) method. +**Note:** `DISTINCT` is separate from the select clause,

    +

    // SELECT DISTINCT name FROM user +User.distinct().select("name")

    -
    //SELECT * FROM user LIMIT 5
    -User.limit(5);
    -

    To provide an offset you can provide limit with a second argument. +

    
    +##Limit and Offset
     
    +To limit the number of rows returned use the [limit](./patio_Dataset.html#limit) method.
    +

    //SELECT * FROM user LIMIT 5 +User.limit(5);

    -

    The following would return the 11th through 15th records in the original dataset. +

    
    +To provide an offset you can provide limit with a second argument.
     
    +The following would return the 11th through 15th records in the original dataset.
    +

    // SELECT * FROM user LIMIT 5 OFFSET 10 + User.limit(5, 10);

    -
    // SELECT * FROM user LIMIT 5 OFFSET 10
    - User.limit(5, 10);
    -

    To remove the LIMIT and OFFSET clause use the unlimited method. +

    
    +To remove the LIMIT and OFFSET clause use the [unlimited](./patio_Dataset.html#unlimited) method.
    +

    // SELECT * FROM user + User.limit(5, 10).unlimited();

    -
     // SELECT * FROM user
    - User.limit(5, 10).unlimited();
    -

    Grouping

    -

    The GROUP clause is used to results based on the values of a given group of columns. To provide grouping use the group method: +

    
     
    -

    -
     // SELECT * FROM user GROUP BY user_id
    - User.group("userId");
    -

    You can remove an existing grouping use the ungrouped method: +##Grouping +The GROUP clause is used to results based on the values of a given group of columns. To provide grouping use the [group](./patio_Dataset.html#group) method:

    +

    // SELECT * FROM user GROUP BY user_id + User.group("userId");

    -
     User.group("userId").ungrouped();
    - // SELECT * FROM user
    -

    A common use of grouping is to count based on the number of grouped rows, so patio provides a groupAndCount method. - +

    
    +You can remove an existing grouping use the [ungrouped](./patio_Dataset.html#ungrouped) method:
    +

    User.group("userId").ungrouped(); + // SELECT * FROM user

    -
    // SELECT user_id, COUNT(*) AS count FROM user GROUP BY user_id
    - User.groupAndCount("userId");
    -

    Having

    -

    The HAVING clause filters the results after the grouping has been applied, instead of before. - +

    
    +A common use of grouping is to count based on the number of grouped rows, so patio provides a [groupAndCount](./patio_Dataset.html#groupAndCount) method.
    +

    // SELECT user_id, COUNT(*) AS count FROM user GROUP BY user_id + User.groupAndCount("userId");

    -
     // SELECT user_id, COUNT(*) AS count FROM user GROUP BY user_id HAVING count >= 10
    - User.groupAndCount("dateOfBirth").having({count : {gte : 10}});
    -

    If you have a HAVING clause then filter will apply the filter to the HAVING clause. +

    
    +##Having
     
    -

    -
     // SELECT user_id, COUNT(*) AS count FROM user GROUP BY user_id HAVING count >= 10 AND count < 15
    - User.groupAndCount("dateOfBirth").having({count : {gte : 10}}).filter({count : {lt : 15}});
    -

    Where

    -

    Unlike filter, the where method will always affect the WHERE clause: +The HAVING clause filters the results after the grouping has been applied, instead of before.

    +

    // SELECT user_id, COUNT(*) AS count FROM user GROUP BY user_id HAVING count >= 10 + User.groupAndCount("dateOfBirth").having({count : {gte : 10}});

    -
    // SELECT user_id, COUNT(*) AS count FROM user WHERE name LIKE 'A%' GROUP BY id HAVING count >= 10
    - User.groupAndCount("id").having({count : {gte : 10}}).where({name  : {like : 'A%'}});
    -

    Both the WHERE clause and the HAVING clause can be removed by using the unfiltered method: - +

    
    +If you have a HAVING clause then filter will apply the filter to the HAVING clause.
    +

    // SELECT user_id, COUNT(*) AS count FROM user GROUP BY user_id HAVING count >= 10 AND count < 15 + User.groupAndCount("dateOfBirth").having({count : {gte : 10}}).filter({count : {lt : 15}});

    -
    // SELECT user_id, COUNT(*) AS count FROM user GROUP BY user_id
    -User.groupAndCount("id").having({count : {gte : 10}}).where({name  : {like : 'A%'}}).unfiltered();
    -

    Joins

    -

    To join a dataset to another table or dataset. The underlying method used is joinTable: +

    
    +##Where                
     
    +Unlike filter, the where method will always affect the WHERE clause:
    +

    // SELECT user_id, COUNT(*) AS count FROM user WHERE name LIKE 'A%' GROUP BY id HAVING count >= 10 + User.groupAndCount("id").having({count : {gte : 10}}).where({name : {like : 'A%'}}); +

    +
    
    +Both the WHERE clause and the HAVING clause can be removed by using the unfiltered method:
    +

    // SELECT user_id, COUNT(*) AS count FROM user GROUP BY user_id +User.groupAndCount("id").having({count : {gte : 10}}).where({name : {like : 'A%'}}).unfiltered();

    -
    // SELECT * FROM user INNER JOIN user ON blog.user_id = user.id
    -User.joinTable("inner", "blog", {userId : sql.id});
    -

    Note: unlike other querying methods when specifying the join condition you must specify the value as a sql.Identifier if it is a column otherwise it will be assumed to be a literal value. +

    
    +##Joins
     
    +To join a dataset to another table or dataset. The underlying method used is [joinTable](./patio_Dataset.html#joinTable):
    +

    // SELECT * FROM user INNER JOIN user ON blog.user_id = user.id +User.joinTable("inner", "blog", {userId : sql.id});

    -
    // SELECT * FROM user INNER JOIN blog ON blog.user_id = user.id
    +
    **Note:** unlike other querying methods when specifying the join condition you must specify the value as a [sql.Identifier](./patio_sql.html#.identifier) if it is a column otherwise it will be assumed to be a literal value.
    +

    // SELECT * FROM user INNER JOIN blog ON blog.user_id = user.id User.joinTable("inner", "blog", {userId : sql.id}); -// SELECT * FROM user INNER JOIN blog ON blog.userId = 'id' -User.joinTable("inner", "blog", {userId : "id"});

    -

    joinTable is not typically used directly, but instead by named join methods: -

    -
    // SELECT * FROM user INNER JOIN blog ON blog.user_id = user.id
    +

    // SELECT * FROM user INNER JOIN blog ON blog.userId = 'id' +User.joinTable("inner", "blog", {userId : "id"}); +

    +
    
    +joinTable is not typically used directly, but instead by named join methods:
    +

    // SELECT * FROM user INNER JOIN blog ON blog.user_id = user.id User.join("blog", {userId : sql.id}); - // SELECT * FROM user LEFT JOIN blog ON blog.user_id = user.id - User.leftJoin("blog", {userId: sql.id});

    -

    Table/Dataset to Join

    -

    For the join methods, the first argument is generally the name of the table to which you are joining. However, you can also provide a -

    -
      -
    • model class:
    • -
    -
     User.join(Blog, {userId : sql.id});
    -
      -
    • dataset, in which case a subselect is used:
      //SELECT * FROM user INNER JOIN (SELECT * FROM blog WHERE (title < 'A')) AS t1 ON (t1.user_id = user.id)
      -User.join(Blog.filter({title : {lt : 'A'}}), {userId : sql.id});
      -
    • -
    -

    Join Conditions

    -

    The second argument to the specialized join methods is the conditions to use when joining, which is similar to a filter expression, with a few minor exceptions. +

    // SELECT * FROM user LEFT JOIN blog ON blog.user_id = user.id + User.leftJoin("blog", {userId: sql.id}); +

    +
    
     
    +###Table/Dataset to Join
     
    -

    -

    Implicit Qualification +For the join methods, the first argument is generally the name of the table to which you are joining. However, you can also provide a +* model class:

    +

    User.join(Blog, {userId : sql.id});

    -

    An object used as the join conditions operates similarly to a filter, except that keys are automatically qualified with the table from the first argument, and unqualified values, that are sql.Identifiers, are automatically qualified with the first table or the last table joined. - +

    
    +* dataset, in which case a subselect is used:
    +

    //SELECT FROM user INNER JOIN (SELECT FROM blog WHERE (title < 'A')) AS t1 ON (t1.user_id = user.id) + User.join(Blog.filter({title : {lt : 'A'}}), {userId : sql.id});

    -

    Note: both the id key and the userId value are qualified. +

    
    +###Join Conditions
     
    -

    -
     //SELECT * FROM user INNER JOIN blog ON (blog.userId = user.id)
    - User.join("blog", {userId : sql.id});
    -

    Because patio uses the last joined table for implicit qualifications of values, you can do things like: +The second argument to the specialized join methods is the conditions to use when joining, which is similar to a filter expression, with a few minor exceptions. + + +**Implicit Qualification** + +An object used as the join conditions operates similarly to a filter, except that keys are automatically qualified with the table from the first argument, and unqualified values, that are sql.Identifiers, are automatically qualified with the first table or the last table joined. +**Note:** both the id key and the userId value are qualified.

    +

    //SELECT * FROM user INNER JOIN blog ON (blog.userId = user.id) + User.join("blog", {userId : sql.id});

    -
     //SELECT * FROM user
    +
    
    +
    +Because patio uses the last joined table for implicit qualifications of values, you can do things like:
    +

    //SELECT * FROM user // INNER JOIN blog ON (blog.user_id = user.id) // INNER JOIN posts ON (posts.blog_id = blog.id) - User.join("blog", {userId : sql.id}).join("posts", {blogId : sql.id});

    -

    Note blogId is qualified with posts and id is qualified with blog. - -

    -

    Implicit qualification is not always correct: - +

    User.join("blog", {userId : sql.id}).join("posts", {blogId : sql.id});

    -
    // SELECT * FROM user INNER JOIN blog ON (blog.user_id = user.id) INNER JOIN posts ON (posts.user_id = blog.id)
    -User.join("blog", {userId : sql.id}).join("posts", {userId : sql.id});
    -

    id is qualified with blog instead of user. This is wrong as the foreign key posts.user_id refers to user.id, not blog.id. To fix this, you need to explicitly qualify when joining: +

    **Note** blogId is qualified with posts and id is qualified with blog.
     
    +
    +Implicit qualification is not always correct:
    +

    // SELECT * FROM user INNER JOIN blog ON (blog.user_id = user.id) INNER JOIN posts ON (posts.user_id = blog.id) +User.join("blog", {userId : sql.id}).join("posts", {userId : sql.id});

    -
    //SELECT * FROM user
    +
    id is qualified with blog instead of user. This is wrong as the foreign key posts.user_id refers to user.id, not blog.id. To fix this, you need to explicitly qualify when joining:
    +

    //SELECT * FROM user // INNER JOIN blog ON (blog.user_id = user.id) // INNER JOIN posts ON (posts.user_id = user.id) -User.join("blog", {userId : sql.id}).join("posts", {userId : sql.id.qualify("user")}); +

    +

    User.join("blog", {userId : sql.id}).join("posts", {userId : sql.id.qualify("user")}); -//OR +

    +

    //OR -User.join("blog", {userId : sql.id}).join("posts", {userId : sql.user__id}).sql

    -

    Just like the dataset filter method the join expression can be an array of two element arrays. +

    +

    User.join("blog", {userId : sql.id}).join("posts", {userId : sql.user__id}).sql

    -
    // SELECT * FROM user
    +
    
    +Just like the dataset filter method the join expression can be an array of two element arrays.
    +

    // SELECT * FROM user // INNER JOIN blog ON ((blog.user_id = user.id) AND (blog.id >= 1) AND (blog.id <= 5)) -User.join("blog", [[sql.userId, sql.id], [sql.id, {between : [1, 5]}]]).sql

    -

    USING Joins

    -

    JOIN ON is the most common type of join condition, however USING is also another valid SQL join expr that patio supports. +

    +

    User.join("blog", [[sql.userId, sql.id], [sql.id, {between : [1, 5]}]]).sql

    -

    JOIN USING is useful when the columns you are using have the same names in both tables. +

    
    +###USING Joins
    +
    +JOIN ON is the most common type of join condition, however USING is also another valid SQL join expr that patio supports.
     
    +JOIN USING is useful when the columns you are using have the same names in both tables.
    +

    // SELECT * FROM user INNER JOIN blog USING (user_id) + User.join("blog", [sql.userId])

    -
    // SELECT * FROM user INNER JOIN blog USING (user_id)
    - User.join("blog", [sql.userId])
    -

    NATURAL Joins

    -

    NATURAL Joins assume that all columns with the same names used for joining, so you do not need to use a join expression. +

    
    +###NATURAL Joins
     
    +NATURAL Joins assume that all columns with the same names used for joining, so you do not need to use a join expression.
    +

    // SELECT * FROM user NATURAL JOIN blog + User.naturalJoin("blog");

    -
    // SELECT * FROM user NATURAL JOIN blog
    - User.naturalJoin("blog");
    -

    Join Blocks

    -

    The block should accept 3 arguments, the table alias for the table currently being joined, the table alias for the last table joined (or first table), and an array of previous patio.sql.JoinClauses. +

    
    +###Join Blocks
     
    +The block should accept 3 arguments, the table alias for the table currently being joined, the table alias for the last table joined (or first table), and an array of previous [patio.sql.JoinClause](./patio_sql_JoinClause.html)s.
     
    -

    -

    This allows you to qualify columns similar to how the implicit qualification works, without worrying about the specific aliases being used. For example, if you wanted to join the user and blog tables, but only want user where the user's name comes before the blog's title. -

    -
     //SELECT * FROM user INNER JOIN blog
    +This allows you to qualify columns similar to how the implicit qualification works, without worrying about the specific aliases being used. For example, if you wanted to join the user and blog tables, but only want user where the user's name comes before the blog's title.
    +

    //SELECT * FROM user INNER JOIN blog // ON ((blog.user_id = user.id) AND (user.name < blog.title)) User.join("blog", {userId : sql.id}, function(currAlias, lastAlias, previousJoins){ return sql.name.qualify(lastAlias).lt(sql.title.qualify(currAlias)); - })

    -

    or you could do this which is the same thing: - + })

    -
     User.join("blog", {userId : sql.id, title : {gt : sql.name.qualify("user")}});
    +
    
    +or you could do this which is the same thing:
    +

    User.join("blog", {userId : sql.id, title : {gt : sql.name.qualify("user")}}); //SELECT * FROM user INNER JOIN blog -// ON ((blog.user_id = user.id) AND (blog.title > user.name))

    -

    From

    -

    The FROM table is typically the first clause populated when creating a dataset. For a standard patio.Model, the dataset already has the FROM clause populated, and the most common way to create datasets is with the Database from method. - +// ON ((blog.user_id = user.id) AND (blog.title > user.name))

    -
    DB.from("user");
    -// SELECT * FROM user
    -

    However, you can also use the from method on the Dataset. +

    
    +##From
     
    +The FROM table is typically the first clause populated when creating a dataset. For a standard [patio.Model](./patio_Model.html), the dataset already has the FROM clause populated, and the most common way to create datasets is with the Database from method.
    +

    DB.from("user"); +// SELECT * FROM user

    -
     User.from("user", "oldUser");
    +
    
    +However, you can also use the from method on the Dataset.
    +

    User.from("user", "oldUser"); // SELECT * FROM user, old_user -//Using from again will remove the previous FROM clause. +

    +

    //Using from again will remove the previous FROM clause. DB.from("user").from("oldUser"); - // SELECT * FROM old_user

    -

    Note: multiple tables in the FROM clause use a cross join by default, so the number of rows will be number of user times the number of old user. + // SELECT * FROM old_user +

    +
    
    +**Note:** multiple tables in the FROM clause use a cross join by default, so the number of rows will be number of user times the number of old user.
     
     
    -

    -

    Subselects

    -

    If you want to perform a subselect you can use the fromSelf method. +##Subselects +If you want to perform a subselect you can use the [fromSelf](./patio_Dataset.html#fromSelf) method.

    +

    Blog.order("userId").limit(100).fromSelf().group("userId"); + //SELECT FROM (SELECT FROM user ORDER BY user_id LIMIT 100) AS t1 GROUP BY user_id

    -
     Blog.order("userId").limit(100).fromSelf().group("userId");
    - //SELECT * FROM (SELECT * FROM user ORDER BY user_id LIMIT 100) AS t1 GROUP BY user_id
    -

    If you did not use the fromSelf method the query would be: - +

    
    +If you did not use the `fromSelf` method the query would be:
    +

    // SELECT * FROM user GROUP BY user_id ORDER BY user_id LIMIT 100 + Blog.order("userId").limit(100).group("userId")

    -
     // SELECT * FROM user GROUP BY user_id ORDER BY user_id LIMIT 100
    - Blog.order("userId").limit(100).group("userId")
    -

    Without fromSelf, you are doing the grouping, and limiting the number of grouped records returned to 100. So assuming you have blogs written by more than 100 user, you'll end up with 100 results. +

    
    +Without fromSelf, you are doing the grouping, and limiting the number of grouped records returned to 100. So assuming you have blogs written by more than 100 user, you'll end up with 100 results.
     
     
    -

    -

    With fromSelf, you are limiting the number of records before grouping. So if the user with the lowest id had 100 blogs, you'd get 1 result, not 100. +With fromSelf, you are limiting the number of records before grouping. So if the user with the lowest id had 100 blogs, you'd get 1 result, not 100. -

    -

    Locking for Update

    -

    patio allows you to easily add a FOR UPDATE clause to your queries so that the records returned can't be modified by another query until the current transaction commits. You just use the forUpdate method: +##Locking for Update -

    -
     DB.transaction(function(){
    +patio allows you to easily add a FOR UPDATE clause to your queries so that the records returned can't be modified by another query until the current transaction commits. You just use the [forUpdate](./patio_Dataset.html#forUpdate) method:
    +

    DB.transaction(function(){ var ret = new comb.Promise(); User.forUpdate().first({id : 1}).then(function(){ // SELECT * FROM user WHERE id = 1 FOR UPDATE @@ -1024,33 +1039,36 @@

    Locking for Update

    user.save().then(ret); }, ret); return ret; - });
    -

    This will ensure that no other connection modifies the row between when you select it and when the transaction ends. - + });

    -

    Custom SQL

    -

    patio makes it easy to use custom SQL by providing the [fetch][./patio_Database.html#fetch] method. +

    
    +This will ensure that no other connection modifies the row between when you select it and when the transaction ends.
    +
    +##Custom SQL
     
    +patio makes it easy to use custom SQL by providing the [fetch][./patio_Database.html#fetch] method.
    +

    // SELECT FROM user + DB.fetch("SELECT FROM user")

    -
     // SELECT * FROM user
    - DB.fetch("SELECT * FROM user")
    -

    You can also use the withSql dataset method. +

    
    +You can also use the withSql dataset method.
    +

    DB.from("user").withSql("SELECT FROM user"); + // SELECT FROM user

    -
     DB.from("user").withSql("SELECT * FROM user");
    - // SELECT * FROM user
    -

    You can also use placeholders: +

    
    +You can also use placeholders:
    +

    // SELECT FROM user WHERE id = 5 + DB.fetch("SELECT FROM user WHERE id = ?", 5);

    -
     // SELECT * FROM user WHERE id = 5
    - DB.fetch("SELECT * FROM user WHERE id = ?", 5);
    -
    - // SELECT * FROM user WHERE id = 5
    - DB.from("user").withSql("SELECT * FROM user WHERE id = {id}", {id : 5});
    -

    Checking for Records

    -

    To test if there are any records in the database use the isEmpty method - +

    // SELECT FROM user WHERE id = 5 + DB.from("user").withSql("SELECT FROM user WHERE id = {id}", {id : 5});

    -
     User.isEmpty().then(function(isEmpty){
    +
    
    +##Checking for Records
    +
    +To test if there are any records in the database use the isEmpty method
    +

    User.isEmpty().then(function(isEmpty){ // SELECT 1 FROM user LIMIT 1 }, errorHandler); User.filter({id : 0}).isEmpty().then(function(isEmpty){ @@ -1058,41 +1076,43 @@

    Checking for Records

    },errorHandler); User.filter(sql.name.like('B%')).isEmpty().then(function(isEmpty){ // SELECT 1 FROM user WHERE name LIKE 'B%' LIMIT 1 - },errorHandler);
    -

    Aggregate Calculations

    -

    There are dataset methods for each of the following aggregate calculations: + },errorHandler);

    -
      -
    • count : count just returns the number of records in the dataset.
    • -
    -
     User.count().then(function(count){
    +
    ##Aggregate Calculations
    +
    +There are dataset methods for each of the following aggregate calculations:
    +
    +* [count](./patio_Dataset.html#count) : count just returns the number of records in the dataset.
    +

    User.count().then(function(count){ // SELECT COUNT(*) AS count FROM user LIMIT 1 - });

    -
      -
    • sum : makes a sum aggregate function call for the column.
    • -
    -
     User.sum("id").then(function(){
    + });
    +

    +
    
    +* [sum](./patio_Dataset.html#sum) : makes a sum aggregate function call for the column.
    +

    User.sum("id").then(function(){ // SELECT sum(id) FROM user LIMIT 1 - });

    -
      -
    • avg: makes a avg aggregate function call for the column.
    • -
    -
     User.avg("id").then(function(){
    + });
    +
    +

    +
    
    +* [avg](./patio_Dataset.html#avg): makes a avg aggregate function call for the column.
    +

    User.avg("id").then(function(){ // SELECT avg(id) FROM user LIMIT 1 - });

    -
      -
    • min : makes a min aggregate function call for the column.
    • -
    -
     User.min("id").then(function(){
    + });
    +

    +
    
    +* [min](./patio_Dataset.html#min) : makes a min aggregate function call for the column.
    +

    User.min("id").then(function(){ // SELECT sum(id) FROM user LIMIT 1 - });

    -
      -
    • max: makes a max aggregate function call for the column.
    • -
    -
     User.max("id").then(function(){
    + });
    +

    +
    
    +* [max](./patio_Dataset.html#max): makes a max aggregate function call for the column.
    +

    User.max("id").then(function(){ // SELECT sum(id) FROM user LIMIT 1 - });

    + }); +```


    diff --git a/lib/dataset/index.js b/lib/dataset/index.js index c979f199..27e586e4 100755 --- a/lib/dataset/index.js +++ b/lib/dataset/index.js @@ -234,6 +234,9 @@ define([actions, graph, features, query, sql], { * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions. + * @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset. + * @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources + * @property {Boolean} hasSelectSource true if this dataset already has a select sources. */ constructor:function (db, opts) { this._super(arguments); @@ -402,6 +405,21 @@ define([actions, graph, features, query, sql], { } else { return source; } + }, + + sourceList:function () { + return (this.__opts.from || []).map(this.stringToIdentifier, this); + }, + + joinSourceList:function () { + return (this.__opts.join || []).map(function (join) { + return this.stringToIdentifier(join.tableAlias || join.table); + }, this); + }, + + hasSelectSource:function () { + var select = this.__opts.select; + return !(isUndefinedOrNull(select) || select.length === 0); } }, diff --git a/lib/dataset/query.js b/lib/dataset/query.js index 897511e4..e0bc583e 100755 --- a/lib/dataset/query.js +++ b/lib/dataset/query.js @@ -1515,6 +1515,30 @@ define(null, { return this.mergeOptions({select:null}); }, + /** + * Selects the columns if only if there is not already select sources. + * + * @example + * + * var ds = DB.from("items"); //SELECT * FROM items + * + * ds.select("a"); //SELECT a FROM items; + * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items; + * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items; + * + * @param cols columns to select if there is not already select sources. + * @return {patio.Dataset} a cloned dataset with the appropriate select sources. + */ + selectIfNoSource:function (cols) { + var ret; + if (!this.hasSelectSource) { + ret = this.select.apply(this, arguments); + } else { + ret = this.mergeOptions(); + } + return ret; + }, + /** * Returns a copy of the dataset with the given columns added * to the existing selected columns. If no columns are currently selected, diff --git a/lib/dataset/sql.js b/lib/dataset/sql.js index bb627e9f..6fbcdaf1 100755 --- a/lib/dataset/sql.js +++ b/lib/dataset/sql.js @@ -1,42 +1,43 @@ var comb = require("comb"), - define = comb.define, - array = comb.array, - intersect = array.intersect, - compact = array.compact, - string = comb.string, - format = string.format, - argsToArray = comb.argsToArray, - isInstanceOf = comb.isInstanceOf, - isArray = comb.isArray, - isNumber = comb.isNumber, - isDate = comb.isDate, - isNull = comb.isNull, - isBoolean = comb.isBoolean, - isFunction = comb.isFunction, - isUndefined = comb.isUndefined, - isObject = comb.isObject, - isHash = comb.isHash, - merge = comb.merge, - isUndefinedOrNull = comb.isUndefinedOrNull, - isString = comb.isString, - sql = require("../sql").sql, - Expression = sql.Expression, - ComplexExpression = sql.ComplexExpression, - AliasedExpression = sql.AliasedExpression, - Identifier = sql.Identifier, - QualifiedIdentifier = sql.QualifiedIdentifier, - OrderedExpression = sql.OrderedExpression, - CaseExpression = sql.CaseExpression, - SubScript = sql.SubScript, - NumericExpression = sql.NumericExpression, - ColumnAll = sql.ColumnAll, - Cast = sql.Cast, - StringExpression = sql.StringExpression, - BooleanExpression = sql.BooleanExpression, - SQLFunction = sql.SQLFunction, - LiteralString = sql.LiteralString, - PlaceHolderLiteralString = sql.PlaceHolderLiteralString, - QueryError = require("../errors").QueryError, patio; + define = comb.define, + array = comb.array, + intersect = array.intersect, + compact = array.compact, + string = comb.string, + format = string.format, + argsToArray = comb.argsToArray, + isInstanceOf = comb.isInstanceOf, + isArray = comb.isArray, + isNumber = comb.isNumber, + isDate = comb.isDate, + isNull = comb.isNull, + isBoolean = comb.isBoolean, + isFunction = comb.isFunction, + isUndefined = comb.isUndefined, + isObject = comb.isObject, + isHash = comb.isHash, + isEmpty = comb.isEmpty, + merge = comb.merge, + isUndefinedOrNull = comb.isUndefinedOrNull, + isString = comb.isString, + sql = require("../sql").sql, + Expression = sql.Expression, + ComplexExpression = sql.ComplexExpression, + AliasedExpression = sql.AliasedExpression, + Identifier = sql.Identifier, + QualifiedIdentifier = sql.QualifiedIdentifier, + OrderedExpression = sql.OrderedExpression, + CaseExpression = sql.CaseExpression, + SubScript = sql.SubScript, + NumericExpression = sql.NumericExpression, + ColumnAll = sql.ColumnAll, + Cast = sql.Cast, + StringExpression = sql.StringExpression, + BooleanExpression = sql.BooleanExpression, + SQLFunction = sql.SQLFunction, + LiteralString = sql.LiteralString, + PlaceHolderLiteralString = sql.PlaceHolderLiteralString, + QueryError = require("../errors").QueryError, patio; var Dataset; @@ -447,7 +448,7 @@ define(null, { return new QualifiedIdentifier(table, e); } else if (isInstanceOf(e, OrderedExpression)) { return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending, - {nulls:e.nulls}); + {nulls:e.nulls}); } else if (isInstanceOf(e, AliasedExpression)) { return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias); } else if (isInstanceOf(e, CaseExpression)) { @@ -615,9 +616,9 @@ define(null, { var columns = this.__opts.columns, ret = ""; if (columns && columns.length) { ret = " (" + columns.map( - function (c) { - return c.toString(this); - }, this).join(this._static.COMMA_SEPARATOR) + ")"; + function (c) { + return c.toString(this); + }, this).join(this._static.COMMA_SEPARATOR) + ")"; } return ret; }, @@ -729,8 +730,8 @@ define(null, { return this.complexExpressionSql("EQ", args); } else { return this.complexExpressionSql("OR", - [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0], - null)]); + [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0], + null)]); } } else if (["IN", "NOTIN"].indexOf(op) != -1) { @@ -761,8 +762,8 @@ define(null, { //literal so that if values is an array of two element arrays, it //will be treated as a value list instead of a condition specifier. return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols), - ComplexExpression.IN_OPERATORS[op], - valArray ? this._arraySql(vals) : this.literal(vals)); + ComplexExpression.IN_OPERATORS[op], + valArray ? this._arraySql(vals) : this.literal(vals)); } } else { @@ -776,13 +777,13 @@ define(null, { } } else { return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols), - ComplexExpression.IN_OPERATORS[op], this.literal(vals)); + ComplexExpression.IN_OPERATORS[op], this.literal(vals)); } } } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) { var l = args[0]; return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp, - this.literal(args[1])); + this.literal(args[1])); } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) { return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " ")); } else if (op == "NOT") { @@ -825,7 +826,7 @@ define(null, { } var tref = this.__tableRef(table); return string.format(" %s %s", this._joinTypeSql(jc.joinType), - tableAlias ? this.__asSql(tref, tableAlias) : tref); + tableAlias ? this.__asSql(tref, tableAlias) : tref); }, /** @@ -898,11 +899,11 @@ define(null, { */ qualifiedIdentifierSql:function (qcr) { return [qcr.table, qcr.column].map( - function (x) { - return [QualifiedIdentifier, Identifier, String].some(function (c) { - return x instanceof c - }) ? this.literal(x) : this.quoteIdentifier(x) - }, this).join('.'); + function (x) { + return [QualifiedIdentifier, Identifier, String].some(function (c) { + return x instanceof c + }) ? this.literal(x) : this.quoteIdentifier(x) + }, this).join('.'); }, /** @@ -937,12 +938,12 @@ define(null, { var i = this.__identifierInputMethod; v = v.toString(this); return !isUndefinedOrNull(i) ? - isFunction(v[i]) ? - v[i]() : - isFunction(comb[i]) ? - comb[i](v) - : v - : v; + isFunction(v[i]) ? + v[i]() : + isFunction(comb[i]) ? + comb[i](v) + : v + : v; }, /** @@ -955,12 +956,12 @@ define(null, { (v == '' && (v = 'untitled')); var i = this.__identifierOutputMethod; return !isUndefinedOrNull(i) ? - isFunction(v[i]) ? - v[i]() : - isFunction(comb[i]) ? - comb[i](v) - : v - : v; + isFunction(v[i]) ? + v[i]() : + isFunction(comb[i]) ? + comb[i](v) + : v + : v; }, /** @@ -1056,9 +1057,9 @@ define(null, { */ _joinTypeSql:function (joinType) { return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g, - function (m) { - return m.toUpperCase() + " "; - } + function (m) { + return m.toUpperCase() + " "; + } ).trimRight() + " JOIN"; }, @@ -1184,10 +1185,10 @@ define(null, { var table = parts[0], column = parts[1], alias = parts[2]; if (!alias) { return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'" - + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'" + + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'" } else { return this.literal(new AliasedExpression(column - && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias)); + && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias)); } }, @@ -1480,9 +1481,9 @@ define(null, { throw new QueryError("No source specified for the query"); } return " " + source.map( - function (s) { - return this.__tableRef(s); - }, this).join(this._static.COMMA_SEPARATOR); + function (s) { + return this.__tableRef(s); + }, this).join(this._static.COMMA_SEPARATOR); }, /** diff --git a/lib/model.js b/lib/model.js index 45f6b5fc..b32ceacd 100644 --- a/lib/model.js +++ b/lib/model.js @@ -229,10 +229,15 @@ var Model = define([QueryPlugin, Middleware], { __setFromDb:function (values, ignore) { values = values || {}; this.__ignore = ignore === true; + var schema = this.schema; Object.keys(values).forEach(function (column) { var value = values[column]; // Typecast value retrieved from db - this.__values[column] = this._typeCastValue(column, value, ignore); + if (schema.hasOwnProperty(column)) { + this.__values[column] = this._typeCastValue(column, value, ignore); + } else { + this[column] = value; + } }, this); this.__ignore = false; diff --git a/package.json b/package.json index 71233b46..e7f6f0b9 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "patio", "description": "Patio query engine and ORM", - "version": "0.1.0", + "version": "0.1.1", "keywords": [ "ORM", "object relation mapper", diff --git a/test/model.test.js b/test/model.test.js index 64f0c149..1cc8166a 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -10,7 +10,7 @@ var it = require('it'), var gender = ["M", "F"]; -it.describe("A model with properites", function (it) { +it.describe("A model with properites",function (it) { var Employee; it.beforeAll(function () { @@ -194,11 +194,13 @@ it.describe("A model with properites", function (it) { }); it.should("support the filtering of models", function (next) { - var id = sql.identifier("id"), ids = emps.map(function(emp){return emp.id;}); + var id = sql.identifier("id"), ids = emps.map(function (emp) { + return emp.id; + }); comb.executeInOrder(Employee, function (Employee) { var ret = {}; - ret.query1 = Employee.filter({id:ids.slice(0,6)}).all(); + ret.query1 = Employee.filter({id:ids.slice(0, 6)}).all(); ret.query2 = Employee.filter(id.gt(ids[0]), id.lt(ids[5])).order("id").last(); ret.query3 = Employee.filter(function () { return this.firstname.like(/first1[1|2]*$/); @@ -231,7 +233,7 @@ it.describe("A model with properites", function (it) { assert.deepEqual(query4.map(function (e) { assert.instanceOf(e, Employee); return e.id; - }), ids.slice(0,6)); + }), ids.slice(0, 6)); assert.deepEqual(query5.map(function (e) { assert.instanceOf(e, Employee); return e.id; @@ -239,7 +241,7 @@ it.describe("A model with properites", function (it) { next(); }, next); }); - + it.should("support custom query methods", function (next) { Employee.findByGender("F").then(function (emps) { emps.forEach(function (emp) { @@ -272,7 +274,9 @@ it.describe("A model with properites", function (it) { it.should("support map", function (next) { - var ids = emps.map(function(emp){return emp.id;}); + var ids = emps.map(function (emp) { + return emp.id; + }); comb.executeInOrder(Employee, function (Employee) { var ret = {}; @@ -315,7 +319,9 @@ it.describe("A model with properites", function (it) { }); it.should("support first", function (next) { - var id = sql.identifier("id"),ids = emps.map(function(emp){return emp.id;}); + var id = sql.identifier("id"), ids = emps.map(function (emp) { + return emp.id; + }); Employee.first(id.gt(ids[5]), id.lt(ids[11])).then(function (emp) { assert.instanceOf(emp, Employee); assert.equal(emp.id, ids[6]) @@ -401,7 +407,12 @@ it.describe("A model with properites", function (it) { }, next); }); + }); + it.should("allow values when initalizing that are not in the schema", function () { + var m = new Employee({otherVal:"otherVal", firstname:"dougie"}, true); + assert.equal(m.otherVal, "otherVal"); + assert.equal(m.firstname, "dougie"); }); it.afterAll(function () { @@ -409,7 +420,6 @@ it.describe("A model with properites", function (it) { }); - }).as(module); diff --git a/test/plugins/columnMapper.test.js b/test/plugins/columnMapper.test.js index f2abfa14..cb77c587 100644 --- a/test/plugins/columnMapper.test.js +++ b/test/plugins/columnMapper.test.js @@ -10,7 +10,6 @@ var it = require('it'), var gender = ["M", "F"]; - it.describe("patio.plugins.ColumnMapper", function (it) { var Works, Employee; it.beforeAll(function () {