From fe94a187e96d9d4eb28f67ec315b268a58ce1851 Mon Sep 17 00:00:00 2001 From: Doug Martin Date: Fri, 20 Dec 2013 12:12:14 -0600 Subject: [PATCH] v0.2.11 --- docs-md/coverage.html | 5187 +++++++++++++++++++++-------------------- docs/coverage.html | 5187 +++++++++++++++++++++-------------------- package.json | 2 +- 3 files changed, 5245 insertions(+), 5131 deletions(-) diff --git a/docs-md/coverage.html b/docs-md/coverage.html index f4295537..fd8d971d 100644 --- a/docs-md/coverage.html +++ b/docs-md/coverage.html @@ -256,10 +256,10 @@
- Coverage88.47 - SLOC22140 - LOC5315 - Missed613 + Coverage88.48 + SLOC22197 + LOC5311 + Missed612
@@ -6921,11 +6921,11 @@
  • * @property {Boolean} hasSelectSource true if this dataset already has a select sources.
  • */
  • constructor:function (db, opts) {
  • -
  • 25725 this._super(arguments);
  • -
  • 25725 this.db = db;
  • -
  • 25725 this.__opts = {};
  • -
  • 25725 this.__rowCb = null;
  • -
  • 25725 if (db) {
  • +
  • 25745 this._super(arguments);
  • +
  • 25745 this.db = db;
  • +
  • 25745 this.__opts = {};
  • +
  • 25745 this.__rowCb = null;
  • +
  • 25745 if (db) {
  • 13274 this.__quoteIdentifiers = db.quoteIdentifiers;
  • 13274 this.__identifierInputMethod = db.identifierInputMethod;
  • 13274 this.__identifierOutputMethod = db.identifierOutputMethod;
  • @@ -6943,22 +6943,22 @@
  • * @return [patio.Dataset] a cloned dataset with the merged options
  • **/
  • mergeOptions:function (opts) {
  • -
  • 13414 opts = isUndefined(opts) ? {} : opts;
  • -
  • 13414 var ds = new this._static(this.db, {});
  • -
  • 13414 ds.rowCb = this.rowCb;
  • -
  • 13414 this._static.FEATURES.forEach(function (f) {
  • -
  • 187796 ds[f] = this[f];
  • +
  • 13418 opts = isUndefined(opts) ? {} : opts;
  • +
  • 13418 var ds = new this._static(this.db, {});
  • +
  • 13418 ds.rowCb = this.rowCb;
  • +
  • 13418 this._static.FEATURES.forEach(function (f) {
  • +
  • 187852 ds[f] = this[f];
  • }, this);
  • -
  • 13414 var dsOpts = ds.__opts = merge({}, this.__opts, opts);
  • -
  • 13414 ds.identifierInputMethod = this.identifierInputMethod;
  • -
  • 13414 ds.identifierOutputMethod = this.identifierOutputMethod;
  • -
  • 13414 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
  • -
  • 13414 if (Object.keys(opts).some(function (o) {
  • -
  • 12145 return columnChangeOpts.indexOf(o) !== -1;
  • +
  • 13418 var dsOpts = ds.__opts = merge({}, this.__opts, opts);
  • +
  • 13418 ds.identifierInputMethod = this.identifierInputMethod;
  • +
  • 13418 ds.identifierOutputMethod = this.identifierOutputMethod;
  • +
  • 13418 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
  • +
  • 13418 if (Object.keys(opts).some(function (o) {
  • +
  • 12149 return columnChangeOpts.indexOf(o) !== -1;
  • })) {
  • 2289 dsOpts.columns = null;
  • }
  • -
  • 13414 return ds;
  • +
  • 13418 return ds;
  • },
  • @@ -6985,17 +6985,17 @@
  • * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
  • */
  • stringToIdentifier:function (name) {
  • -
  • 12879 if (isString(name)) {
  • -
  • 8755 var parts = this._splitString(name);
  • -
  • 8755 var schema = parts[0], table = parts[1], alias = parts[2];
  • -
  • 8755 return (schema && table && alias
  • +
  • 12895 if (isString(name)) {
  • +
  • 8763 var parts = this._splitString(name);
  • +
  • 8763 var schema = parts[0], table = parts[1], alias = parts[2];
  • +
  • 8763 return (schema && table && alias
  • ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
  • : (schema && table
  • ? new QualifiedIdentifier(schema, table)
  • : (table && alias
  • ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
  • } else {
  • -
  • 4124 return name;
  • +
  • 4132 return name;
  • }
  • },
  • @@ -7023,20 +7023,20 @@
  • * </ul>
  • */
  • _splitString:function (s) {
  • -
  • 16920 var ret, m;
  • -
  • 16920 if ((m = s.match(this._static.COLUMN_REF_RE1)) !== null) {
  • +
  • 16928 var ret, m;
  • +
  • 16928 if ((m = s.match(this._static.COLUMN_REF_RE1)) !== null) {
  • 137 ret = m.slice(1);
  • }
  • -
  • 16783 else if ((m = s.match(this._static.COLUMN_REF_RE2)) !== null) {
  • +
  • 16791 else if ((m = s.match(this._static.COLUMN_REF_RE2)) !== null) {
  • 24 ret = [null, m[1], m[2]];
  • }
  • -
  • 16759 else if ((m = s.match(this._static.COLUMN_REF_RE3)) !== null) {
  • +
  • 16767 else if ((m = s.match(this._static.COLUMN_REF_RE3)) !== null) {
  • 1547 ret = [m[1], m[2], null];
  • }
  • else {
  • -
  • 15212 ret = [null, s, null];
  • +
  • 15220 ret = [null, s, null];
  • }
  • -
  • 16920 return ret;
  • +
  • 16928 return ret;
  • },
  • /**
  • @@ -7045,15 +7045,15 @@
  • getters:{
  • rowCb:function () {
  • -
  • 19292 return this.__rowCb;
  • +
  • 19296 return this.__rowCb;
  • },
  • identifierInputMethod:function () {
  • -
  • 13414 return this.__identifierInputMethod;
  • +
  • 13418 return this.__identifierInputMethod;
  • },
  • identifierOutputMethod:function () {
  • -
  • 13414 return this.__identifierOutputMethod;
  • +
  • 13418 return this.__identifierOutputMethod;
  • },
  • firstSourceAlias:function () {
  • @@ -7112,16 +7112,16 @@
  • /**@lends patio.Dataset.prototype*/
  • identifierInputMethod:function (meth) {
  • -
  • 13484 this.__identifierInputMethod = meth;
  • +
  • 13488 this.__identifierInputMethod = meth;
  • },
  • identifierOutputMethod:function (meth) {
  • -
  • 13484 this.__identifierOutputMethod = meth;
  • +
  • 13488 this.__identifierOutputMethod = meth;
  • },
  • rowCb:function (cb) {
  • -
  • 16639 if (isFunction(cb) || isNull(cb)) {
  • -
  • 16634 this.__rowCb = cb;
  • +
  • 16643 if (isFunction(cb) || isNull(cb)) {
  • +
  • 16638 this.__rowCb = cb;
  • } else {
  • 5 throw new DatasetError("rowCb mus be a function");
  • }
  • @@ -7347,14 +7347,14 @@
  • * @borrows patio.Dataset#leftJoin as leftJoin
  • * */
  • constructor:function (options, fromDb) {
  • -
  • 2717 if (this.synced) {
  • -
  • 2717 this.__emitter = new EventEmitter();
  • -
  • 2717 this._super(arguments);
  • -
  • 2717 this.patio = patio || require("./index");
  • -
  • 2717 fromDb = isBoolean(fromDb) ? fromDb : false;
  • -
  • 2717 this.__changed = {};
  • -
  • 2717 this.__values = {};
  • -
  • 2717 if (fromDb) {
  • +
  • 2715 if (this.synced) {
  • +
  • 2715 this.__emitter = new EventEmitter();
  • +
  • 2715 this._super(arguments);
  • +
  • 2715 this.patio = patio || require("./index");
  • +
  • 2715 fromDb = isBoolean(fromDb) ? fromDb : false;
  • +
  • 2715 this.__changed = {};
  • +
  • 2715 this.__values = {};
  • +
  • 2715 if (fromDb) {
  • 1519 this._hook("pre", "load");
  • 1519 this.__isNew = false;
  • 1519 this.__setFromDb(options, true);
  • @@ -7363,8 +7363,8 @@
  • 1519 this._static.emit("load", this);
  • }
  • } else {
  • -
  • 1198 this.__isNew = true;
  • -
  • 1198 this.__set(options);
  • +
  • 1196 this.__isNew = true;
  • +
  • 1196 this.__set(options);
  • }
  • } else {
  • 0 throw new ModelError("Model " + this.tableName + " has not been synced");
  • @@ -7372,15 +7372,15 @@
  • },
  • __set:function (values, ignore) {
  • -
  • 1322 values = values || {};
  • -
  • 1322 this.__ignore = ignore === true;
  • -
  • 1322 Object.keys(values).forEach(function (attribute) {
  • +
  • 1320 values = values || {};
  • +
  • 1320 this.__ignore = ignore === true;
  • +
  • 1320 Object.keys(values).forEach(function (attribute) {
  • 5863 var value = values[attribute];
  • //check if the column is a constrained value and is allowed to be set
  • 5863 !ignore && this._checkIfColumnIsConstrained(attribute);
  • 5863 this[attribute] = value;
  • }, this);
  • -
  • 1322 this.__ignore = false;
  • +
  • 1320 this.__ignore = false;
  • },
  • __setFromDb:function (values, ignore) {
  • @@ -7635,7 +7635,7 @@
  • },
  • synced:function () {
  • -
  • 11255 return this._static.synced;
  • +
  • 11253 return this._static.synced;
  • }
  • }
  • @@ -9564,7 +9564,7 @@
  • * @param {String} message the message to show.
  • */
  • 1patio.QueryError = function(message) {
  • -
  • 122 return new Error("QueryError : " + message);
  • +
  • 118 return new Error("QueryError : " + message);
  • };
  • /**
  • @@ -9662,21 +9662,21 @@
  • 1var virtualRow = function (name) {
  • -
  • 882 var WILDCARD = new LiteralString('*');
  • -
  • 882 var QUESTION_MARK = new LiteralString('?');
  • -
  • 882 var COMMA_SEPARATOR = new LiteralString(', ');
  • -
  • 882 var DOUBLE_UNDERSCORE = '__';
  • -
  • -
  • 882 var parts = name.split(DOUBLE_UNDERSCORE);
  • -
  • 882 var table = parts[0], column = parts[1];
  • -
  • 882 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • -
  • 882 var prox = methodMissing(ident, function (m) {
  • +
  • 876 var WILDCARD = new LiteralString('*');
  • +
  • 876 var QUESTION_MARK = new LiteralString('?');
  • +
  • 876 var COMMA_SEPARATOR = new LiteralString(', ');
  • +
  • 876 var DOUBLE_UNDERSCORE = '__';
  • +
  • +
  • 876 var parts = name.split(DOUBLE_UNDERSCORE);
  • +
  • 876 var table = parts[0], column = parts[1];
  • +
  • 876 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • +
  • 876 var prox = methodMissing(ident, function (m) {
  • 3 return function () {
  • 3 var args = argsToArray(arguments);
  • 3 return SQLFunction.fromArgs([m, name].concat(args));
  • }
  • }, column ? QualifiedIdentifier : Identifier);
  • -
  • 882 var ret = createFunctionWrapper(prox, function (m) {
  • +
  • 876 var ret = createFunctionWrapper(prox, function (m) {
  • 484 var args = argsToArray(arguments);
  • 484 if (args.length) {
  • 478 return SQLFunction.fromArgs([name].concat(args));
  • @@ -9686,8 +9686,8 @@
  • }, function () {
  • 0 return SQLFunction.fromArgs(arguments);
  • });
  • -
  • 882 ret.__proto__ = ident;
  • -
  • 882 return ret;
  • +
  • 876 ret.__proto__ = ident;
  • +
  • 876 return ret;
  • };
  • 1var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
  • @@ -10042,8 +10042,8 @@
  • * @see patio.sql.identifier
  • */
  • stringToIdentifier:function (name) {
  • -
  • 10491 !Dataset && (Dataset = require("./dataset"));
  • -
  • 10491 return new Dataset().stringToIdentifier(name);
  • +
  • 10507 !Dataset && (Dataset = require("./dataset"));
  • +
  • 10507 return new Dataset().stringToIdentifier(name);
  • },
  • /**
  • @@ -10139,7 +10139,7 @@
  • });
  • 1exports.sql = methodMissing(sql, function (name) {
  • -
  • 882 return virtualRow(name);
  • +
  • 876 return virtualRow(name);
  • });
  • 1var OPERTATOR_INVERSIONS = {
  • @@ -10949,13 +10949,13 @@
  • * @return {patio.sql.Expression} an expression.
  • */
  • fromArgs:function (args) {
  • -
  • 1960 var ret;
  • -
  • 1960 try {
  • -
  • 1960 ret = new this();
  • +
  • 1958 var ret;
  • +
  • 1958 try {
  • +
  • 1958 ret = new this();
  • } catch (ignore) {
  • }
  • -
  • 1960 this.apply(ret, args);
  • -
  • 1960 return ret;
  • +
  • 1958 this.apply(ret, args);
  • +
  • 1958 return ret;
  • },
  • /**
  • @@ -10972,8 +10972,8 @@
  • * @return {Boolean} true if the object is a Hash or is an array of two element arrays.
  • */
  • isConditionSpecifier:function (obj) {
  • -
  • 19142 return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
  • -
  • 7932 return isArray(i) && i.length === 2;
  • +
  • 19178 return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
  • +
  • 7944 return isArray(i) && i.length === 2;
  • }));
  • }
  • }
  • @@ -11197,35 +11197,35 @@
  • * </p>
  • */
  • constructor:function (op) {
  • -
  • 6642 if (op) {
  • -
  • 6054 var args = argsToArray(arguments,1 );
  • +
  • 6658 if (op) {
  • +
  • 6066 var args = argsToArray(arguments,1 );
  • //make a copy of the args
  • -
  • 6054 var origArgs = args.slice(0);
  • -
  • 6054 args.forEach(function (a, i) {
  • -
  • 12280 if (Expression.isConditionSpecifier(a)) {
  • +
  • 6066 var origArgs = args.slice(0);
  • +
  • 6066 args.forEach(function (a, i) {
  • +
  • 12304 if (Expression.isConditionSpecifier(a)) {
  • 6 args[i] = BooleanExpression.fromValuePairs(a);
  • }
  • });
  • -
  • 6054 op = op.toUpperCase();
  • +
  • 6066 op = op.toUpperCase();
  • -
  • 6054 if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
  • -
  • 953 if (args.length < 1) {
  • +
  • 6066 if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
  • +
  • 957 if (args.length < 1) {
  • 0 throw new ExpressionError("The " + op + " operator requires at least 1 argument")
  • }
  • -
  • 953 var oldArgs = args.slice(0);
  • -
  • 953 args = [];
  • -
  • 953 oldArgs.forEach(function (a) {
  • -
  • 2141 a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
  • +
  • 957 var oldArgs = args.slice(0);
  • +
  • 957 args = [];
  • +
  • 957 oldArgs.forEach(function (a) {
  • +
  • 2149 a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
  • });
  • -
  • 5101 } else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
  • -
  • 5038 if (args.length != 2) {
  • +
  • 5109 } else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
  • +
  • 5046 if (args.length != 2) {
  • 0 throw new ExpressionError("The " + op + " operator requires precisely 2 arguments");
  • }
  • //With IN/NOT IN, even if the second argument is an array of two element arrays,
  • //don't convert it into a boolean expression, since it's definitely being used
  • //as a value list.
  • -
  • 5038 if (IN_OPERATORS[op]) {
  • +
  • 5046 if (IN_OPERATORS[op]) {
  • 23 args[1] = origArgs[1]
  • }
  • 63 } else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
  • @@ -11235,8 +11235,8 @@
  • } else {
  • 0 throw new ExpressionError("Invalid operator " + op);
  • }
  • -
  • 6054 this.op = op;
  • -
  • 6054 this.args = args;
  • +
  • 6066 this.op = op;
  • +
  • 6066 this.args = args;
  • }
  • },
  • @@ -11249,9 +11249,9 @@
  • * @return String the SQL version of the {@link patio.sql.ComplexExpression}.
  • */
  • toString:function (ds) {
  • -
  • 5673 !Dataset && (Dataset = require("./dataset"));
  • -
  • 5673 ds = ds || new Dataset();
  • -
  • 5673 return ds.complexExpressionSql(this.op, this.args);
  • +
  • 5685 !Dataset && (Dataset = require("./dataset"));
  • +
  • 5685 ds = ds || new Dataset();
  • +
  • 5685 return ds.complexExpressionSql(this.op, this.args);
  • }
  • },
  • @@ -11468,40 +11468,40 @@
  • * @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
  • */
  • fromValuePairs:function (a, op, negate) {
  • -
  • 6758 !Dataset && (Dataset = require("./dataset"));
  • -
  • 6758 op = op || "AND", negate = negate || false;
  • -
  • 6758 var pairArr = [];
  • -
  • 6758 var isArr = isArray(a) && Expression.isConditionSpecifier(a);
  • -
  • 6758 if (isHash(a)) {
  • -
  • 3063 pairArr.push(this.__filterObject(a));
  • +
  • 6774 !Dataset && (Dataset = require("./dataset"));
  • +
  • 6774 op = op || "AND", negate = negate || false;
  • +
  • 6774 var pairArr = [];
  • +
  • 6774 var isArr = isArray(a) && Expression.isConditionSpecifier(a);
  • +
  • 6774 if (isHash(a)) {
  • +
  • 3071 pairArr.push(this.__filterObject(a));
  • } else {
  • -
  • 3695 for (var k in a) {
  • -
  • 4320 var v = isArr ? a[k][1] : a[k], ret;
  • -
  • 4320 k = isArr ? a[k][0] : k;
  • -
  • 4320 if (isArray(v) || isInstanceOf(v, Dataset)) {
  • +
  • 3703 for (var k in a) {
  • +
  • 4328 var v = isArr ? a[k][1] : a[k], ret;
  • +
  • 4328 k = isArr ? a[k][0] : k;
  • +
  • 4328 if (isArray(v) || isInstanceOf(v, Dataset)) {
  • 17 k = isArray(k) ? k.map(function (i) {
  • 12 return isString(i) ? sql.stringToIdentifier(i) : i
  • }) : isString(k) ? sql.stringToIdentifier(k) : k;
  • 17 ret = new BooleanExpression("IN", k, v);
  • -
  • 4303 } else if (isInstanceOf(v, NegativeBooleanConstant)) {
  • +
  • 4311 } else if (isInstanceOf(v, NegativeBooleanConstant)) {
  • 0 ret = new BooleanExpression("ISNOT", k, v.constant);
  • -
  • 4303 } else if (isInstanceOf(v, BooleanConstant)) {
  • +
  • 4311 } else if (isInstanceOf(v, BooleanConstant)) {
  • 0 ret = new BooleanExpression("IS", k, v.constant);
  • -
  • 4303 } else if (isNull(v) || isBoolean(v)) {
  • +
  • 4311 } else if (isNull(v) || isBoolean(v)) {
  • 180 ret = new BooleanExpression("IS", k, v);
  • -
  • 4123 } else if (isHash(v)) {
  • +
  • 4131 } else if (isHash(v)) {
  • 0 ret = BooleanExpression.__filterObject(v, k);
  • -
  • 4123 } else if (isRegExp(v)) {
  • +
  • 4131 } else if (isRegExp(v)) {
  • 61 ret = StringExpression.like(sql.stringToIdentifier(k), v);
  • } else {
  • -
  • 4062 ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
  • +
  • 4070 ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
  • }
  • -
  • 4320 negate && (ret = BooleanExpression.invert(ret));
  • -
  • 4320 pairArr.push(ret);
  • +
  • 4328 negate && (ret = BooleanExpression.invert(ret));
  • +
  • 4328 pairArr.push(ret);
  • }
  • }
  • //if We just have one then return the first otherwise create a new Boolean expression
  • -
  • 6758 return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
  • +
  • 6774 return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
  • },
  • /**
  • @@ -11526,13 +11526,13 @@
  • * @return {patio.sql.Expression} an expression to use in the filter
  • */
  • __filterObject:function (expr, key) {
  • -
  • 3172 var pairs = [], opts, newKey;
  • -
  • 3172 var twoArityOperators = this.TWO_ARITY_OPERATORS;
  • -
  • 3172 for (var k in expr) {
  • -
  • 3196 var v = expr[k];
  • -
  • 3196 if (isHash(v)) { //its a hash too filter it too!
  • +
  • 3180 var pairs = [], opts, newKey;
  • +
  • 3180 var twoArityOperators = this.TWO_ARITY_OPERATORS;
  • +
  • 3180 for (var k in expr) {
  • +
  • 3204 var v = expr[k];
  • +
  • 3204 if (isHash(v)) { //its a hash too filter it too!
  • 107 pairs.push(this.__filterObject(v, k));
  • -
  • 3089 } else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
  • +
  • 3097 } else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
  • //its a two arrity operator (e.g. '=', '>')
  • 110 newKey = isString(key) ? key.split(",") : [key];
  • 110 if (newKey.length > 1) {
  • @@ -11566,19 +11566,19 @@
  • } else {
  • //we're not a twoarity operator
  • //so we create a boolean expression out of it
  • -
  • 2979 newKey = k.split(",");
  • -
  • 2979 if (newKey.length == 1) {
  • -
  • 2973 newKey = sql.stringToIdentifier(newKey[0]);
  • +
  • 2987 newKey = k.split(",");
  • +
  • 2987 if (newKey.length == 1) {
  • +
  • 2981 newKey = sql.stringToIdentifier(newKey[0]);
  • }
  • -
  • 2979 opts = [
  • +
  • 2987 opts = [
  • [newKey, v]
  • ];
  • -
  • 2979 pairs.push(BooleanExpression.fromValuePairs(opts));
  • +
  • 2987 pairs.push(BooleanExpression.fromValuePairs(opts));
  • }
  • }
  • //if the total of pairs is one then we just return the first element
  • //otherwise we join them all with an AND
  • -
  • 3172 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
  • +
  • 3180 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
  • }
  • }
  • }).as(sql, "BooleanExpression");
  • @@ -11758,7 +11758,7 @@
  • * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
  • */
  • constructor:function (value) {
  • -
  • 14766 this.__value = value;
  • +
  • 14762 this.__value = value;
  • },
  • /**
  • @@ -11770,15 +11770,15 @@
  • * @return String the SQL version of the {@link patio.sql.Identifier}.
  • */
  • toString:function (ds) {
  • -
  • 19196 !Dataset && (Dataset = require("./dataset"));
  • -
  • 19196 ds = ds || new Dataset();
  • -
  • 19196 return ds.quoteIdentifier(this);
  • +
  • 19208 !Dataset && (Dataset = require("./dataset"));
  • +
  • 19208 ds = ds || new Dataset();
  • +
  • 19208 return ds.quoteIdentifier(this);
  • },
  • /**@ignore*/
  • getters:{
  • value:function () {
  • -
  • 22095 return this.__value;
  • +
  • 22107 return this.__value;
  • }
  • }
  • }
  • @@ -12335,7 +12335,7 @@
  • 1var addStringMethod = function (op) {
  • 24 return function () {
  • -
  • 3622 return this.__str[op].apply(this.__str, arguments);
  • +
  • 3626 return this.__str[op].apply(this.__str, arguments);
  • }
  • };
  • @@ -12358,7 +12358,7 @@
  • * @param {String} str the literal string.
  • */
  • constructor:function (str) {
  • -
  • 3075 this.__str = str;
  • +
  • 3057 this.__str = str;
  • }
  • }
  • }).as(sql, "LiteralString");
  • @@ -14086,9 +14086,9 @@
  • constructor:function () {
  • //We initialize these here because otherwise
  • //the will be blank because of recursive dependencies.
  • -
  • 25725 !patio && (patio = require("../index"));
  • -
  • 25725 !Dataset && (Dataset = patio.Dataset);
  • -
  • 25725 this._super(arguments);
  • +
  • 25745 !patio && (patio = require("../index"));
  • +
  • 25745 !Dataset && (Dataset = patio.Dataset);
  • +
  • 25745 this._super(arguments);
  • },
  • /**
  • @@ -14373,15 +14373,15 @@
  • * @return {String} a literal representation of the value.
  • */
  • literal:function (v) {
  • -
  • 38459 if (isInstanceOf(v, LiteralString)) {
  • +
  • 38491 if (isInstanceOf(v, LiteralString)) {
  • 194 return "" + v;
  • -
  • 38265 } else if (isString(v)) {
  • +
  • 38297 } else if (isString(v)) {
  • 5397 return this._literalString(v);
  • -
  • 32868 } else if (isNumber(v)) {
  • -
  • 6152 return this._literalNumber(v);
  • +
  • 32900 } else if (isNumber(v)) {
  • +
  • 6160 return this._literalNumber(v);
  • }
  • -
  • 26716 else if (isInstanceOf(v, Expression)) {
  • -
  • 24443 return this._literalExpression(v);
  • +
  • 26740 else if (isInstanceOf(v, Expression)) {
  • +
  • 24467 return this._literalExpression(v);
  • }
  • 2273 else if (isInstanceOf(v, Dataset)) {
  • 104 return this._literalDataset(v);
  • @@ -14572,22 +14572,22 @@
  • //Prepares an SQL statement by calling all clause methods for the given statement type.
  • _clauseSql:function (type) {
  • -
  • 5793 var sql = [("" + type).toUpperCase()];
  • -
  • 5793 try {
  • -
  • 5793 this._static[sql + "_CLAUSE_METHODS"].forEach(function (m) {
  • -
  • 58246 if (m.match("With")) {
  • -
  • 5516 this[m](sql);
  • +
  • 5797 var sql = [("" + type).toUpperCase()];
  • +
  • 5797 try {
  • +
  • 5797 this._static[sql + "_CLAUSE_METHODS"].forEach(function (m) {
  • +
  • 58298 if (m.match("With")) {
  • +
  • 5520 this[m](sql);
  • } else {
  • -
  • 52730 var sqlRet = this[m]();
  • -
  • 52728 if (sqlRet) {
  • -
  • 18662 sql.push(sqlRet);
  • +
  • 52778 var sqlRet = this[m]();
  • +
  • 52776 if (sqlRet) {
  • +
  • 18674 sql.push(sqlRet);
  • }
  • }
  • }, this);
  • } catch (e) {
  • 2 throw e;
  • }
  • -
  • 5791 return sql.join("");
  • +
  • 5795 return sql.join("");
  • },
  • @@ -14701,9 +14701,9 @@
  • * SQL fragment for complex expressions
  • **/
  • complexExpressionSql:function (op, args) {
  • -
  • 5658 var newOp;
  • -
  • 5658 var isOperators = this._static.IS_OPERATORS, isLiterals = this._static.IS_LITERALS;
  • -
  • 5658 if ((newOp = isOperators[op]) != null) {
  • +
  • 5670 var newOp;
  • +
  • 5670 var isOperators = this._static.IS_OPERATORS, isLiterals = this._static.IS_LITERALS;
  • +
  • 5670 if ((newOp = isOperators[op]) != null) {
  • 232 var r = args[1], v = isNull(r) ? isLiterals.NULL : isLiterals[r];
  • 232 if (r == null || this.supportsIsTrue) {
  • 232 if (isUndefined(v)) {
  • @@ -14719,7 +14719,7 @@
  • null)]);
  • }
  • -
  • 5426 } else if (["IN", "NOTIN"].indexOf(op) != -1) {
  • +
  • 5438 } else if (["IN", "NOTIN"].indexOf(op) != -1) {
  • 17 var cols = args[0], vals = args[1], colArray = isArray(cols), valArray = false, emptyValArray = false;
  • 17 if (isArray(vals)) {
  • @@ -14765,12 +14765,12 @@
  • ComplexExpression.IN_OPERATORS[op], this.literal(vals));
  • }
  • }
  • -
  • 5409 } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
  • -
  • 4596 var l = args[0];
  • -
  • 4596 return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
  • +
  • 5421 } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
  • +
  • 4604 var l = args[0];
  • +
  • 4604 return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
  • this.literal(args[1]));
  • -
  • 813 } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
  • -
  • 779 return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
  • +
  • 817 } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
  • +
  • 783 return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
  • 34 } else if (op == "NOT") {
  • 5 return string.format("NOT %s", this.literal(args[0]));
  • 29 } else if (op == "NOOP") {
  • @@ -14899,18 +14899,18 @@
  • * quote the name with {@link patio.dataset._Sql#_quotedIdentifier}.
  • */
  • quoteIdentifier:function (name) {
  • -
  • 29064 if (isInstanceOf(name, LiteralString)) {
  • +
  • 29076 if (isInstanceOf(name, LiteralString)) {
  • 228 return name;
  • } else {
  • -
  • 28836 if (isInstanceOf(name, Identifier)) {
  • -
  • 19530 name = name.value;
  • +
  • 28848 if (isInstanceOf(name, Identifier)) {
  • +
  • 19542 name = name.value;
  • }
  • -
  • 28836 name = this.inputIdentifier(name);
  • -
  • 28836 if (this.quoteIdentifiers) {
  • +
  • 28848 name = this.inputIdentifier(name);
  • +
  • 28848 if (this.quoteIdentifiers) {
  • 21729 name = this._quotedIdentifier(name)
  • }
  • }
  • -
  • 28836 return name;
  • +
  • 28848 return name;
  • },
  • /**
  • @@ -14920,9 +14920,9 @@
  • * identifierOutputMethod.
  • */
  • inputIdentifier:function (v) {
  • -
  • 28970 var i = this.__identifierInputMethod;
  • -
  • 28970 v = v.toString(this);
  • -
  • 28970 return !isUndefinedOrNull(i) ?
  • +
  • 28982 var i = this.__identifierInputMethod;
  • +
  • 28982 v = v.toString(this);
  • +
  • 28982 return !isUndefinedOrNull(i) ?
  • isFunction(v[i]) ?
  • v[i]() :
  • isFunction(comb[i]) ?
  • @@ -15010,7 +15010,7 @@
  • * column names. If the array is empty, a wildcard (*) is returned.
  • */
  • __columnList:function (columns) {
  • -
  • 4776 return (!columns || columns.length == 0) ? this._static.WILDCARD : this.__expressionList(columns);
  • +
  • 4780 return (!columns || columns.length == 0) ? this._static.WILDCARD : this.__expressionList(columns);
  • },
  • /**
  • @@ -15094,11 +15094,11 @@
  • * @return SQL fragment for a number.
  • */
  • _literalNumber:function (num) {
  • -
  • 6152 var ret = "" + num;
  • -
  • 6152 if (isNaN(num) || num == Infinity) {
  • +
  • 6160 var ret = "" + num;
  • +
  • 6160 if (isNaN(num) || num == Infinity) {
  • 0 ret = string.format("'%s'", ret);
  • }
  • -
  • 6152 return ret;
  • +
  • 6160 return ret;
  • },
  • /**
  • @@ -15147,7 +15147,7 @@
  • * @return SQL fragment for SQL::Expression, result depends on the specific type of expression.
  • * */
  • _literalExpression:function (v) {
  • -
  • 24453 return v.toString(this);
  • +
  • 24477 return v.toString(this);
  • },
  • /**
  • @@ -15180,9 +15180,9 @@
  • /*SQL STATEMENT CREATION METHODS*/
  • _selectQualifySql:function () {
  • -
  • 4323 var o = this.__opts;
  • -
  • 4323 var table = this.__opts.alwaysQualify;
  • -
  • 4323 if (table && !o.sql) {
  • +
  • 4327 var o = this.__opts;
  • +
  • 4327 var table = this.__opts.alwaysQualify;
  • +
  • 4327 if (table && !o.sql) {
  • 2 array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) {
  • 2 o[k] = this._qualifiedExpression(o[k], table);
  • }, this);
  • @@ -15200,19 +15200,19 @@
  • * @return the columns selected
  • * */
  • _selectColumnsSql:function () {
  • -
  • 3588 return " " + this.__columnList(this.__opts.select);
  • +
  • 3592 return " " + this.__columnList(this.__opts.select);
  • },
  • /**@return the DISTINCT clause.*/
  • _selectDistinctSql:function () {
  • -
  • 3588 var distinct = this.__opts.distinct, ret = [];
  • -
  • 3588 if (distinct) {
  • +
  • 3592 var distinct = this.__opts.distinct, ret = [];
  • +
  • 3592 if (distinct) {
  • 10 ret.push(" DISTINCT");
  • 10 if (distinct.length) {
  • 4 ret.push(format(" ON (%s)", this.__expressionList(distinct)));
  • }
  • }
  • -
  • 3588 return ret.join("");
  • +
  • 3592 return ret.join("");
  • },
  • /**
  • @@ -15221,30 +15221,30 @@
  • * work on all databases.
  • **/
  • _selectCompoundsSql:function () {
  • -
  • 3588 var opts = this.__opts, compounds = opts.compounds, ret = [];
  • -
  • 3588 if (compounds) {
  • +
  • 3592 var opts = this.__opts, compounds = opts.compounds, ret = [];
  • +
  • 3592 if (compounds) {
  • 49 compounds.forEach(function (c) {
  • 49 var type = c[0], dataset = c[1], all = c[2];
  • 49 ret.push(string.format(" %s%s %s", type.toUpperCase(), all ? " ALL" : "", this._subselectSql(dataset)));
  • }, this);
  • }
  • -
  • 3588 return ret.join("");
  • +
  • 3592 return ret.join("");
  • },
  • /**
  • * @return the sql to add the list of tables to select FROM
  • **/
  • _selectFromSql:function () {
  • -
  • 3606 var from = this.__opts.from;
  • -
  • 3606 return from ? string.format(" %s%s", this._static.FROM, this._sourceList(from)) : "";
  • +
  • 3610 var from = this.__opts.from;
  • +
  • 3610 return from ? string.format(" %s%s", this._static.FROM, this._sourceList(from)) : "";
  • },
  • /**
  • * @return the GROUP BY clause
  • **/
  • _selectGroupSql:function () {
  • -
  • 3588 var group = this.__opts.group;
  • -
  • 3588 return group ? string.format(" GROUP BY %s", this.__expressionList(group)) : "";
  • +
  • 3592 var group = this.__opts.group;
  • +
  • 3592 return group ? string.format(" GROUP BY %s", this.__expressionList(group)) : "";
  • },
  • @@ -15252,62 +15252,62 @@
  • *@return the sql to add the filter criteria in the HAVING clause
  • **/
  • _selectHavingSql:function () {
  • -
  • 3588 var having = this.__opts.having;
  • -
  • 3588 return having ? string.format(" HAVING %s", this.literal(having)) : "";
  • +
  • 3592 var having = this.__opts.having;
  • +
  • 3592 return having ? string.format(" HAVING %s", this.literal(having)) : "";
  • },
  • /**
  • * @return the JOIN clause.
  • **/
  • _selectJoinSql:function () {
  • -
  • 3591 var join = this.__opts.join, ret = [];
  • -
  • 3591 if (join) {
  • +
  • 3595 var join = this.__opts.join, ret = [];
  • +
  • 3595 if (join) {
  • 467 join.forEach(function (j) {
  • 771 ret.push(this.literal(j));
  • }, this);
  • }
  • -
  • 3591 return ret.join("");
  • +
  • 3595 return ret.join("");
  • },
  • /**
  • * @return the LIMIT and OFFSET clauses.
  • * */
  • _selectLimitSql:function () {
  • -
  • 3588 var ret = [], limit = this.__opts.limit, offset = this.__opts.offset;
  • -
  • 3588 !isUndefined(limit) && !isNull(limit) && (ret.push(format(" LIMIT %s", this.literal(limit))));
  • -
  • 3588 !isUndefined(offset) && !isNull(offset) && (ret.push(format(" OFFSET %s", this.literal(offset))));
  • -
  • 3588 return ret.join("");
  • +
  • 3592 var ret = [], limit = this.__opts.limit, offset = this.__opts.offset;
  • +
  • 3592 !isUndefined(limit) && !isNull(limit) && (ret.push(format(" LIMIT %s", this.literal(limit))));
  • +
  • 3592 !isUndefined(offset) && !isNull(offset) && (ret.push(format(" OFFSET %s", this.literal(offset))));
  • +
  • 3592 return ret.join("");
  • },
  • /**
  • * @return SQL for different locking modes.
  • **/
  • _selectLockSql:function () {
  • -
  • 3587 var lock = this.__opts.lock, ret = [];
  • -
  • 3587 if (lock) {
  • +
  • 3591 var lock = this.__opts.lock, ret = [];
  • +
  • 3591 if (lock) {
  • 3 if (lock == "update") {
  • 2 ret.push(this._static.FOR_UPDATE);
  • } else {
  • 1 ret.push(" ", lock);
  • }
  • }
  • -
  • 3587 return ret.join("");
  • +
  • 3591 return ret.join("");
  • },
  • /**
  • * @return the SQL ORDER BY clause fragment.
  • */
  • _selectOrderSql:function () {
  • -
  • 3600 var order = this.__opts.order;
  • -
  • 3600 return order ? string.format(" ORDER BY %s", this.__expressionList(order)) : "";
  • +
  • 3604 var order = this.__opts.order;
  • +
  • 3604 return order ? string.format(" ORDER BY %s", this.__expressionList(order)) : "";
  • },
  • /**
  • * @return the SQL WHERE clause fragment.
  • */
  • _selectWhereSql:function () {
  • -
  • 4554 var where = this.__opts.where;
  • -
  • 4554 return where ? string.format(" WHERE %s", this.literal(where)) : "";
  • +
  • 4558 var where = this.__opts.where;
  • +
  • 4558 return where ? string.format(" WHERE %s", this.literal(where)) : "";
  • },
  • /**
  • @@ -15315,8 +15315,8 @@
  • * @param sql
  • */
  • _selectWithSql:function (sql) {
  • -
  • 5516 var wit = this.__opts["with"];
  • -
  • 5516 if (wit && wit.length) {
  • +
  • 5520 var wit = this.__opts["with"];
  • +
  • 5520 if (wit && wit.length) {
  • //sql.length = 0;
  • 8 var base = sql.join("");
  • 8 sql.length = 0;
  • @@ -15459,15 +15459,15 @@
  • * Converts an array of source names into into a comma separated list.
  • **/
  • _sourceList:function (source) {
  • -
  • 5814 if (!Array.isArray(source)) {
  • +
  • 5818 if (!Array.isArray(source)) {
  • 718 source = [source];
  • }
  • -
  • 5814 if (!source || !source.length) {
  • +
  • 5818 if (!source || !source.length) {
  • 0 throw new QueryError("No source specified for the query");
  • }
  • -
  • 5814 return " " + source.map(
  • +
  • 5818 return " " + source.map(
  • function (s) {
  • -
  • 5996 return this.__tableRef(s);
  • +
  • 6000 return this.__tableRef(s);
  • }, this).join(this._static.COMMA_SEPARATOR);
  • },
  • @@ -15492,7 +15492,7 @@
  • * @returns SQL fragment specifying a table name.
  • **/
  • __tableRef:function (t) {
  • -
  • 6767 return isString(t) ? this._quotedIdentifier(t) : this.literal(t);
  • +
  • 6771 return isString(t) ? this._quotedIdentifier(t) : this.literal(t);
  • },
  • @@ -15522,12 +15522,12 @@
  • getters:{
  • //Same as selectS, not aliased directly to make subclassing simpler.
  • sql:function () {
  • -
  • 648 return this.selectSql;
  • +
  • 652 return this.selectSql;
  • },
  • selectSql:function () {
  • -
  • 3690 if (this.__opts.sql) return this._staticSql(this.__opts.sql);
  • -
  • 3588 else return this._clauseSql("select");
  • +
  • 3694 if (this.__opts.sql) return this._staticSql(this.__opts.sql);
  • +
  • 3592 else return this._clauseSql("select");
  • },
  • deleteSql:function () {
  • @@ -15982,10 +15982,10 @@
  • /**@ignore*/
  • constructor:function () {
  • -
  • 25725 if (!Dataset) {
  • +
  • 25745 if (!Dataset) {
  • 1 Dataset = require("../index").Dataset;
  • }
  • -
  • 25725 this._super(arguments);
  • +
  • 25745 this._super(arguments);
  • },
  • @@ -17202,10 +17202,10 @@
  • *
  • */
  • constructor:function () {
  • -
  • 2717 if (comb.isUndefinedOrNull(this.__associations)) {
  • -
  • 2629 this.__associations = {};
  • +
  • 2715 if (comb.isUndefinedOrNull(this.__associations)) {
  • +
  • 2628 this.__associations = {};
  • }
  • -
  • 2717 this._super(arguments);
  • +
  • 2715 this._super(arguments);
  • },
  • reload:function () {
  • @@ -17597,2865 +17597,2922 @@
  • }});
  • -
    +
    -
    dataset/query.js
    +
    plugins/validation.js
    - Coverage97.62 - SLOC2290 - LOC462 - Missed11 + Coverage97.66 + SLOC535 + LOC128 + Missed3
    -
    1. 1var comb = require("comb"),
    2. +
      1. 1var comb = require("comb"),
      2. array = comb.array,
      3. -
      4. flatten = array.flatten,
      5. compact = array.compact,
      6. -
      7. define = comb.define,
      8. -
      9. argsToArray = comb.argsToArray,
      10. -
      11. isString = comb.isString,
      12. -
      13. isEmpty = comb.isEmpty,
      14. -
      15. isNull = comb.isNull,
      16. -
      17. isBoolean = comb.isBoolean,
      18. -
      19. isNumber = comb.isNumber,
      20. -
      21. merge = comb.merge,
      22. +
      23. flatten = array.flatten,
      24. +
      25. toArray = array.toArray,
      26. +
      27. net = require("net"),
      28. +
      29. isIP = net.isIP,
      30. +
      31. isIPv4 = net.isIPv4,
      32. +
      33. isIPv6 = net.isIPv6,
      34. +
      35. validator = require("validator"),
      36. +
      37. validatorCheck = validator.check,
      38. +
      39. dateCmp = comb.date.compare,
      40. isArray = comb.isArray,
      41. -
      42. isObject = comb.isObject,
      43. +
      44. combDeepEqual = comb.deepEqual,
      45. +
      46. combIsBoolean = comb.isBoolean,
      47. +
      48. isString = comb.isString,
      49. +
      50. combIsDefined = comb.isDefined,
      51. +
      52. combIsNull = comb.isNull,
      53. +
      54. ModelError = require("../errors.js").ModelError,
      55. isFunction = comb.isFunction,
      56. -
      57. isUndefined = comb.isUndefined,
      58. -
      59. isHash = comb.isHash,
      60. -
      61. isInstanceOf = comb.isInstanceOf,
      62. -
      63. sql = require("../sql").sql,
      64. -
      65. LiteralString = sql.LiteralString,
      66. -
      67. Expression = sql.Expression,
      68. -
      69. ComplexExpression = sql.ComplexExpression,
      70. -
      71. BooleanExpression = sql.BooleanExpression,
      72. -
      73. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
      74. -
      75. Identifier = sql.Identifier,
      76. -
      77. QualifiedIdentifier = sql.QualifiedIdentifier,
      78. -
      79. AliasedExpression = sql.AliasedExpression,
      80. -
      81. StringExpression = sql.StringExpression,
      82. -
      83. NumericExpression = sql.NumericExpression,
      84. -
      85. OrderedExpression = sql.OrderedExpression,
      86. -
      87. JoinClause = sql.JoinClause,
      88. -
      89. JoinOnClause = sql.JoinOnClause,
      90. -
      91. JoinUsingClause = sql.JoinUsingClause,
      92. -
      93. ColumnAll = sql.ColumnAll,
      94. -
      95. QueryError = require("../errors").QueryError;
      96. +
      97. format = comb.string.format,
      98. +
      99. Promise = comb.Promise,
      100. +
      101. serial = comb.serial,
      102. +
      103. when = comb.when,
      104. +
      105. merge = comb.merge,
      106. +
      107. define = comb.define;
      108. +
      109. 1var Validator = define(null, {
      110. +
      111. instance:{
      112. -
      113. 1var Dataset;
      114. +
      115. constructor:function validator(col) {
      116. +
      117. 44 this.col = col;
      118. +
      119. 44 this.__actions = [];
      120. +
      121. },
      122. -
      123. 1function conditionedJoin(type) {
      124. -
      125. 539 var args = argsToArray(arguments, 1);
      126. -
      127. 539 return this.joinTable.apply(this, [type].concat(args));
      128. -
      129. }
      130. +
      131. __addAction:function __addAction(action, opts) {
      132. +
      133. 46 this.__actions.push({
      134. +
      135. action:action,
      136. +
      137. opts:merge({onlyDefined:true, onlyNotNull:false}, opts)
      138. +
      139. });
      140. +
      141. 46 return this;
      142. +
      143. },
      144. -
      145. 1function unConditionJoin(type, table) {
      146. -
      147. 6 var args = argsToArray(arguments, 1);
      148. -
      149. 6 return this.joinTable.apply(this, [type, table]);
      150. -
      151. }
      152. +
      153. isAfter:function (date, opts) {
      154. +
      155. 1 opts = opts || {};
      156. +
      157. 1 var cmpDate = combIsBoolean(opts.date) ? opts.date : true;
      158. +
      159. 1 return this.__addAction(function (col) {
      160. +
      161. 3 return dateCmp(col, date, cmpDate ? "date" : "datetime") > 0;
      162. +
      163. }, merge({message:"{col} must be after " + date + " got {val}."}, opts));
      164. +
      165. },
      166. +
      167. isBefore:function (date, opts) {
      168. +
      169. 1 opts = opts || {};
      170. +
      171. 1 var cmpDate = combIsBoolean(opts.date) ? opts.date : true;
      172. +
      173. 1 return this.__addAction(function (col) {
      174. +
      175. 3 return dateCmp(col, date, cmpDate ? "date" : "datetime") === -1;
      176. +
      177. }, merge({message:"{col} must be before " + date + " got {val}."}, opts));
      178. +
      179. },
      180. -
      181. 1define(null, {
      182. -
      183. /**@ignore*/
      184. -
      185. instance: {
      186. +
      187. isDefined:function isDefined(opts) {
      188. +
      189. 1 return this.__addAction(function (col) {
      190. +
      191. 3 return combIsDefined(col);
      192. +
      193. }, merge({message:"{col} must be defined.", onlyDefined:false, onlyNotNull:false}, opts));
      194. +
      195. },
      196. -
      197. /**@lends patio.Dataset.prototype*/
      198. +
      199. isNotDefined:function isNotDefined(opts) {
      200. +
      201. 1 return this.__addAction(function (col) {
      202. +
      203. 3 return !combIsDefined(col);
      204. +
      205. }, merge({message:"{col} cannot be defined.", onlyDefined:false, onlyNotNull:false}, opts));
      206. +
      207. },
      208. -
      209. /**
      210. -
      211. * @ignore
      212. -
      213. */
      214. -
      215. constructor: function () {
      216. -
      217. 25725 !Dataset && (Dataset = require("../index").Dataset);
      218. -
      219. 25725 this._super(arguments);
      220. -
      221. 25725 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
      222. -
      223. 180075 if (!this[type + "Join"]) {
      224. -
      225. 180075 this[type + "Join"] = conditionedJoin.bind(this, type);
      226. -
      227. }
      228. +
      229. isNotNull:function isNotNull(opts) {
      230. +
      231. 3 return this.__addAction(function (col) {
      232. +
      233. 21 return combIsDefined(col) && !combIsNull(col);
      234. +
      235. }, merge({message:"{col} cannot be null.", onlyDefined:false, onlyNotNull:false}, opts));
      236. +
      237. },
      238. -
      239. }, this);
      240. -
      241. 25725 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
      242. -
      243. 128625 if (!this[type + "Join"]) {
      244. -
      245. 128625 this[type + "Join"] = unConditionJoin.bind(this, type);
      246. -
      247. }
      248. +
      249. isNull:function isNull(opts) {
      250. +
      251. 1 return this.__addAction(function (col) {
      252. +
      253. 3 return !combIsDefined(col) || combIsNull(col);
      254. +
      255. }, merge({message:"{col} must be null got {val}.", onlyDefined:false, onlyNotNull:false}, opts));
      256. +
      257. },
      258. -
      259. }, this);
      260. +
      261. isEq:function eq(val, opts) {
      262. +
      263. 4 return this.__addAction(function (col) {
      264. +
      265. 15 return combDeepEqual(col, val);
      266. +
      267. }, merge({message:"{col} must === " + val + " got {val}."}, opts));
      268. },
      269. -
      270. /**
      271. -
      272. * Adds a RETURNING clause, which is not supported by all databases. If returning is
      273. -
      274. * used instead of returning the autogenerated primary key or update/delete returning the number of rows modified.
      275. -
      276. *
      277. -
      278. * @example
      279. -
      280. *
      281. -
      282. * ds.from("items").returning() //"RETURNING *"
      283. -
      284. * ds.from("items").returning(null) //"RETURNING NULL"
      285. -
      286. * ds.from("items").returning("id", "name") //"RETURNING id, name"
      287. -
      288. * ds.from("items").returning(["id", "name"]) //"RETURNING id, name"
      289. -
      290. *
      291. -
      292. * @param values columns to return. If values is an array then the array is assumed to contain the columns to
      293. -
      294. * return. Otherwise the arguments will be used.
      295. -
      296. * @return {patio.Dataset} a new dataset with the retuning option added.
      297. -
      298. */
      299. -
      300. returning: function (values) {
      301. -
      302. 1087 var args;
      303. -
      304. 1087 if (Array.isArray(values)) {
      305. -
      306. 0 args = values;
      307. -
      308. } else {
      309. -
      310. 1087 args = argsToArray(arguments);
      311. -
      312. }
      313. -
      314. 1087 return this.mergeOptions({returning: args.map(function (v) {
      315. -
      316. 1005 return isString(v) ? sql.stringToIdentifier(v) : v;
      317. -
      318. })});
      319. +
      320. isNeq:function neq(val, opts) {
      321. +
      322. 2 return this.__addAction(function (col) {
      323. +
      324. 8 return !combDeepEqual(col, val);
      325. +
      326. }, merge({message:"{col} must !== " + val + "."}, opts));
      327. },
      328. +
      329. isLike:function like(val, opts) {
      330. +
      331. 3 return this.__addAction(function (col) {
      332. +
      333. 14 return !!col.match(val);
      334. +
      335. }, merge({message:"{col} must be like " + val + " got {val}."}, opts));
      336. +
      337. },
      338. -
      339. /**
      340. -
      341. * Adds a futher filter to an existing filter using AND. This method is identical to {@link patio.Dataset#filter}
      342. -
      343. * except it expects an existing filter.
      344. -
      345. *
      346. -
      347. * <p>
      348. -
      349. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      350. -
      351. * </p>
      352. -
      353. *
      354. -
      355. * @example
      356. -
      357. * DB.from("table").filter("a").and("b").sql;
      358. -
      359. * //=>SELECT * FROM table WHERE a AND b
      360. -
      361. *
      362. -
      363. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      364. -
      365. *
      366. -
      367. * @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.
      368. -
      369. */
      370. -
      371. and: function () {
      372. -
      373. 7 var tOpts = this.__opts, clauseObj = tOpts[tOpts.having ? "having" : "where"];
      374. -
      375. 7 if (clauseObj) {
      376. -
      377. 6 return this.filter.apply(this, arguments);
      378. -
      379. } else {
      380. -
      381. 1 throw new QueryError("No existing filter found");
      382. -
      383. }
      384. +
      385. isNotLike:function notLike(val, opts) {
      386. +
      387. 2 return this.__addAction(function (col) {
      388. +
      389. 6 return !(!!col.match(val));
      390. +
      391. }, merge({message:"{col} must not be like " + val + "."}, opts));
      392. },
      393. -
      394. as: function (alias) {
      395. -
      396. 8 return new AliasedExpression(this, alias);
      397. +
      398. isLt:function lt(num, opts) {
      399. +
      400. 1 return this.__addAction(function (col) {
      401. +
      402. 3 return col < num;
      403. +
      404. }, merge({message:"{col} must be < " + num + " got {val}."}, opts));
      405. },
      406. -
      407. /**
      408. -
      409. * Adds an alternate filter to an existing WHERE/HAVING using OR.
      410. -
      411. *
      412. -
      413. * <p>
      414. -
      415. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      416. -
      417. * </p>
      418. -
      419. *
      420. -
      421. * @example
      422. -
      423. *
      424. -
      425. * DB.from("items").filter("a").or("b")
      426. -
      427. * //=> SELECT * FROM items WHERE a OR b
      428. -
      429. *
      430. -
      431. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      432. -
      433. * @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.
      434. -
      435. */
      436. -
      437. or: function () {
      438. -
      439. 12 var tOpts = this.__opts;
      440. -
      441. 12 var clause = (tOpts.having ? "having" : "where"), clauseObj = tOpts[clause];
      442. -
      443. 12 if (clauseObj) {
      444. -
      445. 9 var args = argsToArray(arguments);
      446. -
      447. 9 args = args.length == 1 ? args[0] : args;
      448. -
      449. 9 var opts = {};
      450. -
      451. 9 opts[clause] = new BooleanExpression("OR", clauseObj, this._filterExpr(args));
      452. -
      453. 9 return this.mergeOptions(opts);
      454. -
      455. } else {
      456. -
      457. 3 throw new QueryError("No existing filter found");
      458. +
      459. isGt:function gt(num, opts) {
      460. +
      461. 1 return this.__addAction(function (col) {
      462. +
      463. 3 return col > num;
      464. +
      465. }, merge({message:"{col} must be > " + num + " got {val}."}, opts));
      466. +
      467. },
      468. +
      469. +
      470. isLte:function lte(num, opts) {
      471. +
      472. 1 return this.__addAction(function (col) {
      473. +
      474. 3 return col <= num;
      475. +
      476. }, merge({message:"{col} must be <= " + num + " got {val}."}, opts));
      477. +
      478. },
      479. +
      480. +
      481. isGte:function gte(num, opts) {
      482. +
      483. 1 return this.__addAction(function (col) {
      484. +
      485. 3 return col >= num;
      486. +
      487. }, merge({message:"{col} must be >= " + num + " got {val}."}, opts));
      488. +
      489. },
      490. +
      491. +
      492. isIn:function isIn(arr, opts) {
      493. +
      494. 2 if (!isArray(arr)) {
      495. +
      496. 1 throw new Error("isIn requires an array of values");
      497. }
      498. +
      499. 1 return this.__addAction(function (col) {
      500. +
      501. 3 return arr.indexOf(col) !== -1;
      502. +
      503. }, merge({message:"{col} must be in " + arr.join(",") + " got {val}."}, opts));
      504. },
      505. -
      506. /**
      507. -
      508. * Adds to the where/having clause with an AND a group of ORed conditions wrapped in parens
      509. -
      510. *
      511. -
      512. * <p>
      513. -
      514. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      515. -
      516. * </p>
      517. -
      518. *
      519. -
      520. * @example
      521. -
      522. *
      523. -
      524. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
      525. -
      526. * //=> SELECT
      527. -
      528. * *
      529. -
      530. * FROM
      531. -
      532. * items
      533. -
      534. * WHERE
      535. -
      536. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
      537. -
      538. *
      539. -
      540. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      541. -
      542. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
      543. -
      544. */
      545. -
      546. andGroupedOr: function () {
      547. -
      548. 2 var tOpts = this.__opts,
      549. -
      550. clause = (tOpts.having ? "having" : "where"),
      551. -
      552. clauseObj = tOpts[clause];
      553. -
      554. 2 if (clauseObj) {
      555. -
      556. 1 var args = argsToArray(arguments);
      557. -
      558. 1 args = args.length == 1 ? args[0] : args;
      559. -
      560. 1 var opts = {};
      561. -
      562. 1 opts[clause] = new BooleanExpression("AND", clauseObj, this._filterExpr(args, null, "OR"));
      563. -
      564. 1 return this.mergeOptions(opts);
      565. -
      566. } else {
      567. -
      568. 1 throw new QueryError("No existing filter found");
      569. -
      570. }
      571. -
      572. },
      573. -
      574. -
      575. /**
      576. -
      577. * Adds to the where/having clause with an AND a group of ORed conditions wrapped in parens
      578. -
      579. *
      580. -
      581. * <p>
      582. -
      583. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      584. -
      585. * </p>
      586. -
      587. *
      588. -
      589. * @example
      590. -
      591. *
      592. -
      593. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
      594. -
      595. * //=> SELECT
      596. -
      597. * *
      598. -
      599. * FROM
      600. -
      601. * items
      602. -
      603. * WHERE
      604. -
      605. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
      606. -
      607. *
      608. -
      609. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      610. -
      611. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
      612. -
      613. */
      614. -
      615. andGroupedAnd: function () {
      616. -
      617. 2 var tOpts = this.__opts,
      618. -
      619. clause = (tOpts.having ? "having" : "where"),
      620. -
      621. clauseObj = tOpts[clause];
      622. -
      623. 2 if (clauseObj) {
      624. -
      625. 1 var args = argsToArray(arguments);
      626. -
      627. 1 args = args.length == 1 ? args[0] : args;
      628. -
      629. 1 var opts = {};
      630. -
      631. 1 opts[clause] = new BooleanExpression("AND", clauseObj, this._filterExpr(args, null, "AND"));
      632. -
      633. 1 return this.mergeOptions(opts);
      634. -
      635. } else {
      636. -
      637. 1 throw new QueryError("No existing filter found");
      638. -
      639. }
      640. -
      641. },
      642. -
      643. -
      644. /**
      645. -
      646. * Adds to the where/having clause with an OR a group of ANDed conditions wrapped in parens
      647. -
      648. *
      649. -
      650. * <p>
      651. -
      652. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      653. -
      654. * </p>
      655. -
      656. *
      657. -
      658. * @example
      659. -
      660. *
      661. -
      662. * DB.from("items").filter({id, [1,2,3]}).orGroupedAnd([{price: {lt : 0}}, {price: {gt: 10}]).sql;
      663. -
      664. * //=> SELECT
      665. -
      666. * *
      667. -
      668. * FROM
      669. -
      670. * items
      671. -
      672. * WHERE
      673. -
      674. * ((id IN (1, 2, 3)) OR ((price > 0) AND (price < 10)))
      675. -
      676. *
      677. -
      678. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      679. -
      680. * @return {patio.Dataset} a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.
      681. -
      682. */
      683. -
      684. orGroupedAnd: function () {
      685. -
      686. 3 return this.or.apply(this, arguments);
      687. -
      688. },
      689. -
      690. -
      691. /**
      692. -
      693. * Adds to the where/having clause with an AND a group of ORed conditions wrapped in parens
      694. -
      695. *
      696. -
      697. * <p>
      698. -
      699. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      700. -
      701. * </p>
      702. -
      703. *
      704. -
      705. * @example
      706. -
      707. *
      708. -
      709. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
      710. -
      711. * //=> SELECT
      712. -
      713. * *
      714. -
      715. * FROM
      716. -
      717. * items
      718. -
      719. * WHERE
      720. -
      721. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
      722. -
      723. *
      724. -
      725. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      726. -
      727. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
      728. -
      729. */
      730. -
      731. orGroupedOr: function () {
      732. -
      733. 1 var tOpts = this.__opts,
      734. -
      735. clause = (tOpts.having ? "having" : "where"),
      736. -
      737. clauseObj = tOpts[clause];
      738. -
      739. 1 if (clauseObj) {
      740. -
      741. 1 var args = argsToArray(arguments);
      742. -
      743. 1 args = args.length == 1 ? args[0] : args;
      744. -
      745. 1 var opts = {};
      746. -
      747. 1 opts[clause] = new BooleanExpression("OR", clauseObj, this._filterExpr(args, null, "OR"));
      748. -
      749. 1 return this.mergeOptions(opts);
      750. -
      751. } else {
      752. -
      753. 0 throw new QueryError("No existing filter found");
      754. -
      755. }
      756. -
      757. },
      758. -
      759. -
      760. /**
      761. -
      762. * Returns a copy of the dataset with the SQL DISTINCT clause.
      763. -
      764. * The DISTINCT clause is used to remove duplicate rows from the
      765. -
      766. * output. If arguments are provided, uses a DISTINCT ON clause,
      767. -
      768. * in which case it will only be distinct on those columns, instead
      769. -
      770. * of all returned columns.
      771. -
      772. *
      773. -
      774. * @example
      775. -
      776. *
      777. -
      778. * DB.from("items").distinct().sqll
      779. -
      780. * //=> SELECT DISTINCT * FROM items
      781. -
      782. * DB.from("items").order("id").distinct("id").sql;
      783. -
      784. * //=> SELECT DISTINCT ON (id) * FROM items ORDER BY id
      785. -
      786. *
      787. -
      788. * @throws {patio.QueryError} If arguments are given and DISTINCT ON is not supported.
      789. -
      790. * @param {...String|...patio.sql.Identifier} args variable number of arguments used to create
      791. -
      792. * the DISTINCT ON clause.
      793. -
      794. * @return {patio.Dataset} a cloned dataset with the DISTINCT/DISTINCT ON clause added.
      795. -
      796. */
      797. -
      798. distinct: function (args) {
      799. -
      800. 13 args = argsToArray(arguments);
      801. -
      802. 13 if (args.length && !this.supportsDistinctOn) {
      803. -
      804. 1 throw new QueryError("DISTICT ON is not supported");
      805. -
      806. }
      807. -
      808. 12 args = args.map(function (a) {
      809. -
      810. 6 return isString(a) ? new Identifier(a) : a;
      811. -
      812. });
      813. -
      814. 12 return this.mergeOptions({distinct: args});
      815. -
      816. },
      817. -
      818. -
      819. /**
      820. -
      821. * Adds an EXCEPT clause using a second dataset object.
      822. -
      823. * An EXCEPT compound dataset returns all rows in the current dataset
      824. -
      825. * that are not in the given dataset.
      826. -
      827. *
      828. -
      829. * @example
      830. -
      831. *
      832. -
      833. * DB.from("items").except(DB.from("other_items")).sql;
      834. -
      835. * //=> SELECT * FROM items EXCEPT SELECT * FROM other_items
      836. -
      837. *
      838. -
      839. * DB.from("items").except(DB.from("other_items"),
      840. -
      841. * {all : true, fromSelf : false}).sql;
      842. -
      843. * //=> SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
      844. -
      845. *
      846. -
      847. * DB.from("items").except(DB.from("other_items"),
      848. -
      849. * {alias : "i"}).sql;
      850. -
      851. * //=>SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS i
      852. -
      853. *
      854. -
      855. * @throws {patio.QueryError} if the operation is not supported.
      856. -
      857. * @param {patio.Dataset} dataset the dataset to use to create the EXCEPT clause.
      858. -
      859. * @param {Object} [opts] options to use when creating the EXCEPT clause
      860. -
      861. * @param {String|patio.sql.Identifier} [opt.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias.
      862. -
      863. * @param {Boolean} [opts.all] Set to true to use EXCEPT ALL instead of EXCEPT, so duplicate rows can occur
      864. -
      865. * @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}, use with care.
      866. -
      867. *
      868. -
      869. * @return {patio.Dataset} a cloned dataset with the EXCEPT clause added.
      870. -
      871. */
      872. -
      873. except: function (dataset, opts) {
      874. -
      875. 18 opts = isUndefined(opts) ? {} : opts;
      876. -
      877. 18 if (!isHash(opts)) {
      878. -
      879. 5 opts = {all: true};
      880. -
      881. }
      882. -
      883. 18 if (!this.supportsIntersectExcept) {
      884. -
      885. 2 throw new QueryError("EXCEPT not supoorted");
      886. -
      887. 16 } else if (opts.hasOwnProperty("all") && !this.supportsIntersectExceptAll) {
      888. -
      889. 1 throw new QueryError("EXCEPT ALL not supported");
      890. +
      891. isNotIn:function notIn(arr, opts) {
      892. +
      893. 2 if (!isArray(arr)) {
      894. +
      895. 1 throw new Error("notIn requires an array of values");
      896. }
      897. -
      898. 15 return this.compoundClone("except", dataset, opts);
      899. +
      900. 1 return this.__addAction(function (col) {
      901. +
      902. 3 return arr.indexOf(col) === -1;
      903. +
      904. }, merge({message:"{col} cannot be in " + arr.join(",") + " got {val}."}, opts));
      905. },
      906. -
      907. /**
      908. -
      909. * Performs the inverse of {@link patio.Dataset#filter}. Note that if you have multiple filter
      910. -
      911. * conditions, this is not the same as a negation of all conditions. For argument types see
      912. -
      913. * {@link patio.Dataset#filter}
      914. -
      915. *
      916. -
      917. * @example
      918. -
      919. *
      920. -
      921. * DB.from("items").exclude({category : "software").sql;
      922. -
      923. * //=> SELECT * FROM items WHERE (category != 'software')
      924. -
      925. *
      926. -
      927. * DB.from("items").exclude({category : 'software', id : 3}).sql;
      928. -
      929. * //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
      930. -
      931. * @return {patio.Dataset} a cloned dataset with the excluded conditions applied to the HAVING/WHERE clause.
      932. -
      933. */
      934. -
      935. exclude: function () {
      936. -
      937. 69 var cond = argsToArray(arguments), tOpts = this.__opts;
      938. -
      939. 69 var clause = (tOpts["having"] ? "having" : "where"), clauseObj = tOpts[clause];
      940. -
      941. 69 cond = cond.length > 1 ? cond : cond[0];
      942. -
      943. 69 cond = this._filterExpr.call(this, cond);
      944. -
      945. 69 cond = BooleanExpression.invert(cond);
      946. -
      947. 69 if (clauseObj) {
      948. -
      949. 58 cond = new BooleanExpression("AND", clauseObj, cond)
      950. -
      951. }
      952. -
      953. 69 var opts = {};
      954. -
      955. 69 opts[clause] = cond;
      956. -
      957. 69 return this.mergeOptions(opts);
      958. +
      959. isMacAddress:function isMaxAddress(opts) {
      960. +
      961. 1 return this.__addAction(function (col) {
      962. +
      963. 4 return !!col.match(/^([0-9A-F]{2}[:\-]){5}([0-9A-F]{2})$/);
      964. +
      965. }, merge({message:"{col} must be a valid MAC address got {val}."}, opts));
      966. },
      967. -
      968. /**
      969. -
      970. * Returns a copy of the dataset with the given conditions applied to it.
      971. -
      972. * If the query already has a HAVING clause, then the conditions are applied to the
      973. -
      974. * HAVING clause otherwise they are applied to the WHERE clause.
      975. -
      976. *
      977. -
      978. * @example
      979. -
      980. *
      981. -
      982. * DB.from("items").filter({id : 3}).sql;
      983. -
      984. * //=> SELECT * FROM items WHERE (id = 3)
      985. -
      986. *
      987. -
      988. * DB.from("items").filter('price < ?', 100)
      989. -
      990. * //=> SELECT * FROM items WHERE price < 100
      991. -
      992. *
      993. -
      994. * DB.from("items").filter({id, [1,2,3]}).filter({id : {between : [0, 10]}}).sql;
      995. -
      996. * //=> SELECT
      997. -
      998. * *
      999. -
      1000. * FROM
      1001. -
      1002. * items
      1003. -
      1004. * WHERE
      1005. -
      1006. * ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
      1007. -
      1008. *
      1009. -
      1010. * DB.from("items").filter('price < 100');
      1011. -
      1012. * //=> SELECT * FROM items WHERE price < 100
      1013. -
      1014. *
      1015. -
      1016. * DB.from("items").filter("active").sql;
      1017. -
      1018. * //=> SELECT * FROM items WHERE active
      1019. -
      1020. *
      1021. -
      1022. * DB.from("items").filter(function(){
      1023. -
      1024. * return this.price.lt(100);
      1025. -
      1026. * });
      1027. -
      1028. * //=> SELECT * FROM items WHERE (price < 100)
      1029. -
      1030. *
      1031. -
      1032. * //Multiple filter calls can be chained for scoping:
      1033. -
      1034. * DB.from("items").filter(:category => 'software').filter{price < 100}
      1035. -
      1036. * //=> SELECT * FROM items WHERE ((category = 'software') AND (price < 100))
      1037. -
      1038. *
      1039. -
      1040. *
      1041. -
      1042. * @param {Object|Array|String|patio.sql.Identifier|patio.sql.BooleanExpression} args filters to apply to the
      1043. -
      1044. * WHERE/HAVING clause. Description of each:
      1045. -
      1046. * <ul>
      1047. -
      1048. * <li>Hash - list of equality/inclusion expressions</li>
      1049. -
      1050. * <li>Array - depends:
      1051. -
      1052. * <ul>
      1053. -
      1054. * <li>If first member is a string, assumes the rest of the arguments
      1055. -
      1056. * are parameters and interpolates them into the string.</li>
      1057. -
      1058. * <li>If all members are arrays of length two, treats the same way
      1059. -
      1060. * as a hash, except it allows for duplicate keys to be
      1061. -
      1062. * specified.</li>
      1063. -
      1064. * <li>Otherwise, treats each argument as a separate condition.</li>
      1065. -
      1066. * </ul>
      1067. -
      1068. * </li>
      1069. -
      1070. * <li>String - taken literally</li>
      1071. -
      1072. * <li>{@link patio.sql.Identifier} - taken as a boolean column argument (e.g. WHERE active)</li>
      1073. -
      1074. * <li>{@link patio.sql.BooleanExpression} - an existing condition expression,
      1075. -
      1076. * probably created using the patio.sql methods.
      1077. -
      1078. * </li>
      1079. -
      1080. *
      1081. -
      1082. * @param {Function} [cb] filter also takes a cb, which should return one of the above argument
      1083. -
      1084. * types, and is treated the same way. This block is called with an {@link patio.sql} object which can be used to dynaically create expression. For more details
      1085. -
      1086. * on the sql object see {@link patio.sql}
      1087. -
      1088. *
      1089. -
      1090. * <p>
      1091. -
      1092. * <b>NOTE:</b>If both a cb and regular arguments are provided, they get ANDed together.
      1093. -
      1094. * </p>
      1095. -
      1096. *
      1097. -
      1098. * @return {patio.Dataset} a cloned dataset with the filter arumgents applied to the WHERE/HAVING clause.
      1099. -
      1100. **/
      1101. -
      1102. filter: function (args, cb) {
      1103. -
      1104. 3468 args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));
      1105. -
      1106. 3468 return this._filter.apply(this, args);
      1107. +
      1108. isIPAddress:function isIpAddress(opts) {
      1109. +
      1110. 1 return this.__addAction(function (col) {
      1111. +
      1112. 4 return !!isIP(col);
      1113. +
      1114. }, merge({message:"{col} must be a valid IPv4 or IPv6 address got {val}."}, opts));
      1115. },
      1116. -
      1117. /**
      1118. -
      1119. * @see patio.Dataset#filter
      1120. -
      1121. */
      1122. -
      1123. find: function () {
      1124. -
      1125. 30 var args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));
      1126. -
      1127. 30 return this._filter.apply(this, args);
      1128. +
      1129. isIPv4Address:function isIpAddress(opts) {
      1130. +
      1131. 1 return this.__addAction(function (col) {
      1132. +
      1133. 3 return isIPv4(col);
      1134. +
      1135. }, merge({message:"{col} must be a valid IPv4 address got {val}."}, opts));
      1136. },
      1137. -
      1138. /**
      1139. -
      1140. * @example
      1141. -
      1142. * DB.from("table").forUpdate()
      1143. -
      1144. * //=> SELECT * FROM table FOR UPDATE
      1145. -
      1146. * @return {patio.Dataset} a cloned dataset with a "update" lock style.
      1147. -
      1148. */
      1149. -
      1150. forUpdate: function () {
      1151. -
      1152. 1 return this.lockStyle("update");
      1153. +
      1154. isIPv6Address:function isIpAddress(opts) {
      1155. +
      1156. 1 return this.__addAction(function (col) {
      1157. +
      1158. 3 return isIPv6(col);
      1159. +
      1160. }, merge({message:"{col} must be a valid IPv6 address got {val}."}, opts));
      1161. },
      1162. -
      1163. /**
      1164. -
      1165. * Returns a copy of the dataset with the source changed. If no
      1166. -
      1167. * source is given, removes all tables. If multiple sources
      1168. -
      1169. * are given, it is the same as using a CROSS JOIN (cartesian product) between all tables.
      1170. -
      1171. *
      1172. -
      1173. * @example
      1174. -
      1175. * var dataset = DB.from("items");
      1176. -
      1177. *
      1178. -
      1179. * dataset.from().sql;
      1180. -
      1181. * //=> SELECT *
      1182. -
      1183. *
      1184. -
      1185. * dataset.from("blah").sql
      1186. -
      1187. * //=> SELECT * FROM blah
      1188. -
      1189. *
      1190. -
      1191. * dataset.from("blah", "foo")
      1192. -
      1193. * //=> SELECT * FROM blah, foo
      1194. -
      1195. *
      1196. -
      1197. * dataset.from({a:"b"}).sql;
      1198. -
      1199. * //=> SELECT * FROM a AS b
      1200. -
      1201. *
      1202. -
      1203. * dataset.from(dataset.from("a").group("b").as("c")).sql;
      1204. -
      1205. * //=> "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"
      1206. -
      1207. *
      1208. -
      1209. * @param {...String|...patio.sql.Identifier|...patio.Dataset|...Object} [source] tables to select from
      1210. -
      1211. *
      1212. -
      1213. * @return {patio.Dataset} a cloned dataset with the FROM clause overridden.
      1214. -
      1215. */
      1216. -
      1217. from: function (source) {
      1218. -
      1219. 821 source = argsToArray(arguments);
      1220. -
      1221. 821 var tableAliasNum = 0, sources = [];
      1222. -
      1223. 821 source.forEach(function (s) {
      1224. -
      1225. 1003 if (isInstanceOf(s, Dataset)) {
      1226. -
      1227. 86 sources.push(new AliasedExpression(s, this._datasetAlias(++tableAliasNum)));
      1228. -
      1229. 917 } else if (isHash(s)) {
      1230. -
      1231. 3 for (var i in s) {
      1232. -
      1233. 3 sources.push(new AliasedExpression(new Identifier(i), s[i]));
      1234. -
      1235. }
      1236. -
      1237. 914 } else if (isString(s)) {
      1238. -
      1239. 889 sources.push(this.stringToIdentifier(s))
      1240. -
      1241. } else {
      1242. -
      1243. 25 sources.push(s);
      1244. -
      1245. }
      1246. -
      1247. }, this);
      1248. -
      1249. -
      1250. 821 var o = {from: sources.length ? sources : null}
      1251. -
      1252. 821 if (tableAliasNum) {
      1253. -
      1254. 84 o.numDatasetSources = tableAliasNum;
      1255. -
      1256. }
      1257. -
      1258. 821 return this.mergeOptions(o)
      1259. +
      1260. isUUID:function isUUID(opts) {
      1261. +
      1262. 1 return this.__addAction(function (col) {
      1263. +
      1264. 3 return !!col.match(/^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/);
      1265. +
      1266. }, merge({message:"{col} must be a valid UUID got {val}"}, opts));
      1267. },
      1268. -
      1269. /**
      1270. -
      1271. * Returns a dataset selecting from the current dataset.
      1272. -
      1273. * Supplying the alias option controls the alias of the result.
      1274. -
      1275. *
      1276. -
      1277. * @example
      1278. -
      1279. *
      1280. -
      1281. * ds = DB.from("items").order("name").select("id", "name")
      1282. -
      1283. * //=> SELECT id,name FROM items ORDER BY name
      1284. -
      1285. *
      1286. -
      1287. * ds.fromSelf().sql;
      1288. -
      1289. * //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1
      1290. -
      1291. *
      1292. -
      1293. * ds.fromSelf({alias : "foo"}).sql;
      1294. -
      1295. * //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo
      1296. -
      1297. *
      1298. -
      1299. * @param {Object} [opts] options
      1300. -
      1301. * @param {String|patio.sql.Identifier} [opts.alias] alias to use
      1302. -
      1303. *
      1304. -
      1305. * @return {patio.Dataset} a cloned dataset with the FROM clause set as the current dataset.
      1306. -
      1307. */
      1308. -
      1309. fromSelf: function (opts) {
      1310. -
      1311. 84 opts = isUndefined(opts) ? {} : opts;
      1312. -
      1313. 84 var fs = {};
      1314. -
      1315. 84 var nonSqlOptions = this._static.NON_SQL_OPTIONS;
      1316. -
      1317. 84 Object.keys(this.__opts).forEach(function (k) {
      1318. -
      1319. 302 if (nonSqlOptions.indexOf(k) == -1) {
      1320. -
      1321. 302 fs[k] = null;
      1322. -
      1323. }
      1324. -
      1325. });
      1326. -
      1327. 84 return this.mergeOptions(fs).from(opts["alias"] ? this.as(opts["alias"]) : this);
      1328. +
      1329. isEmail:function isEmail(opts) {
      1330. +
      1331. 1 return this.__addAction(function (col) {
      1332. +
      1333. 3 return validatorCheck(col).isEmail();
      1334. +
      1335. }, merge({message:"{col} must be a valid Email Address got {val}"}, opts));
      1336. },
      1337. -
      1338. /**
      1339. -
      1340. * Match any of the columns to any of the patterns. The terms can be
      1341. -
      1342. * strings (which use LIKE) or regular expressions (which are only
      1343. -
      1344. * supported on MySQL and PostgreSQL). Note that the total number of
      1345. -
      1346. * pattern matches will be columns[].length * terms[].length,
      1347. -
      1348. * which could cause performance issues.
      1349. -
      1350. *
      1351. -
      1352. * @example
      1353. -
      1354. *
      1355. -
      1356. * DB.from("items").grep("a", "%test%").sql;
      1357. -
      1358. * //=> SELECT * FROM items WHERE (a LIKE '%test%');
      1359. -
      1360. *
      1361. -
      1362. * DB.from("items").grep(["a", "b"], ["%test%" "foo"]).sql;
      1363. -
      1364. * //=> SELECT * FROM items WHERE ((a LIKE '%test%') OR (a LIKE 'foo') OR (b LIKE '%test%') OR (b LIKE 'foo'))
      1365. -
      1366. *
      1367. -
      1368. * DB.from("items").grep(['a', 'b'], ["%foo%", "%bar%"], {allPatterns : true}).sql;
      1369. -
      1370. * //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (b LIKE '%foo%')) AND ((a LIKE '%bar%') OR (b LIKE '%bar%')))
      1371. -
      1372. *
      1373. -
      1374. * DB.from("items").grep(["a", "b"], ['%foo%", "%bar%", {allColumns : true})sql;
      1375. -
      1376. * //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (a LIKE '%bar%')) AND ((b LIKE '%foo%') OR (b LIKE '%bar%')))
      1377. -
      1378. *
      1379. -
      1380. * DB.from("items").grep(["a", "b"], ["%foo%", "%bar%"], {allPatterns : true, allColumns : true}).sql;
      1381. -
      1382. * //=> SELECT * FROM a WHERE ((a LIKE '%foo%') AND (b LIKE '%foo%') AND (a LIKE '%bar%') AND (b LIKE '%bar%'))
      1383. -
      1384. *
      1385. -
      1386. * @param {String[]|patio.sql.Identifier[]} columns columns to search
      1387. -
      1388. * @param {String|RegExp} patterns patters to search with
      1389. -
      1390. * @param {Object} [opts] options to use when searching. NOTE If both allColumns and allPatterns are true, all columns must match all patterns
      1391. -
      1392. * @param {Boolean} [opts.allColumns] All columns must be matched to any of the given patterns.
      1393. -
      1394. * @param {Boolean} [opts.allPatterns] All patterns must match at least one of the columns.
      1395. -
      1396. * @param {Boolean} [opts.caseInsensitive] Use a case insensitive pattern match (the default is
      1397. -
      1398. * case sensitive if the database supports it).
      1399. -
      1400. * @return {patio.Dataset} a dataset with the LIKE clauses added
      1401. -
      1402. */
      1403. -
      1404. -
      1405. grep: function (columns, patterns, opts) {
      1406. -
      1407. 15 opts = isUndefined(opts) ? {} : opts;
      1408. -
      1409. 15 var conds;
      1410. -
      1411. 15 if (opts.hasOwnProperty("allPatterns")) {
      1412. -
      1413. 4 conds = array.toArray(patterns).map(function (pat) {
      1414. -
      1415. 8 return BooleanExpression.fromArgs(
      1416. -
      1417. [(opts.allColumns ? "AND" : "OR")]
      1418. -
      1419. .concat(array.toArray(columns)
      1420. -
      1421. .map(function (c) {
      1422. -
      1423. 16 return StringExpression.like(c, pat, opts);
      1424. -
      1425. })));
      1426. -
      1427. });
      1428. -
      1429. 4 return this.filter(BooleanExpression.fromArgs([opts.allPatterns ? "AND" : "OR"].concat(conds)));
      1430. -
      1431. } else {
      1432. -
      1433. 11 conds = array.toArray(columns)
      1434. -
      1435. .map(function (c) {
      1436. -
      1437. 16 return BooleanExpression.fromArgs(["OR"].concat(array.toArray(patterns).map(function (pat) {
      1438. -
      1439. 26 return StringExpression.like(c, pat, opts);
      1440. -
      1441. })));
      1442. -
      1443. });
      1444. -
      1445. 11 return this.filter(BooleanExpression.fromArgs([opts.allColumns ? "AND" : "OR"].concat(conds)));
      1446. -
      1447. }
      1448. +
      1449. isUrl:function isUrl(opts) {
      1450. +
      1451. 1 return this.__addAction(function (col) {
      1452. +
      1453. 3 return validatorCheck(col).isUrl();
      1454. +
      1455. }, merge({message:"{col} must be a valid url got {val}"}, opts));
      1456. },
      1457. -
      1458. /**
      1459. -
      1460. * @see patio.Dataset#grep
      1461. -
      1462. */
      1463. -
      1464. like: function () {
      1465. -
      1466. 1 return this.grep.apply(this, arguments);
      1467. +
      1468. isAlpha:function isAlpha(opts) {
      1469. +
      1470. 2 return this.__addAction(function (col) {
      1471. +
      1472. 11 return validatorCheck(col).isAlpha();
      1473. +
      1474. }, merge({message:"{col} must be a only letters got {val}"}, opts));
      1475. },
      1476. -
      1477. -
      1478. /**
      1479. -
      1480. * Returns a copy of the dataset with the results grouped by the value of
      1481. -
      1482. * the given columns.
      1483. -
      1484. * @example
      1485. -
      1486. * DB.from("items").group("id")
      1487. -
      1488. * //=>SELECT * FROM items GROUP BY id
      1489. -
      1490. * DB.from("items").group("id", "name")
      1491. -
      1492. * //=> SELECT * FROM items GROUP BY id, name
      1493. -
      1494. * @param {String...|patio.sql.Identifier...} columns columns to group by.
      1495. -
      1496. *
      1497. -
      1498. * @return {patio.Dataset} a cloned dataset with the GROUP BY clause added.
      1499. -
      1500. **/
      1501. -
      1502. group: function (columns) {
      1503. -
      1504. 32 columns = argsToArray(arguments);
      1505. -
      1506. 32 var stringToIdentifier = this.stringToIdentifier.bind(this)
      1507. -
      1508. 32 return this.mergeOptions({group: (array.compact(columns).length == 0 ? null : columns.map(function (c) {
      1509. -
      1510. 32 return isString(c) ? stringToIdentifier(c) : c;
      1511. -
      1512. }))});
      1513. +
      1514. isAlphaNumeric:function isAlphaNumeric(opts) {
      1515. +
      1516. 1 return this.__addAction(function (col) {
      1517. +
      1518. 3 return validatorCheck(col).isAlphanumeric();
      1519. +
      1520. }, merge({message:"{col} must be a alphanumeric got {val}"}, opts));
      1521. },
      1522. -
      1523. /**
      1524. -
      1525. * @see patio.Dataset#group
      1526. -
      1527. */
      1528. -
      1529. groupBy: function () {
      1530. -
      1531. 10 return this.group.apply(this, arguments);
      1532. +
      1533. hasLength:function hasLength(min, max, opts) {
      1534. +
      1535. 2 return this.__addAction(function (col) {
      1536. +
      1537. 6 return validatorCheck(col).len(min, max);
      1538. +
      1539. }, merge({message:"{col} must have a length between " + min + (max ? " and " + max : "") + "."}, opts));
      1540. },
      1541. +
      1542. isLowercase:function isLowercase(opts) {
      1543. +
      1544. 1 return this.__addAction(function (col) {
      1545. +
      1546. 3 return validatorCheck(col).isLowercase();
      1547. +
      1548. }, merge({message:"{col} must be lowercase got {val}."}, opts));
      1549. +
      1550. },
      1551. -
      1552. /**
      1553. -
      1554. * Returns a dataset grouped by the given column with count by group.
      1555. -
      1556. * Column aliases may be supplied, and will be included in the select clause.
      1557. -
      1558. *
      1559. -
      1560. * @example
      1561. -
      1562. *
      1563. -
      1564. * DB.from("items").groupAndCount("name").all()
      1565. -
      1566. * //=> SELECT name, count(*) AS count FROM items GROUP BY name
      1567. -
      1568. * //=> [{name : 'a', count : 1}, ...]
      1569. -
      1570. *
      1571. -
      1572. * DB.from("items").groupAndCount("first_name", "last_name").all()
      1573. -
      1574. * //SELECT first_name, last_name, count(*) AS count FROM items GROUP BY first_name, last_name
      1575. -
      1576. * //=> [{first_name : 'a', last_name : 'b', count : 1}, ...]
      1577. -
      1578. *
      1579. -
      1580. * DB.from("items").groupAndCount("first_name___name").all()
      1581. -
      1582. * //=> SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
      1583. -
      1584. * //=> [{name : 'a', count:1}, ...]
      1585. -
      1586. * @param {String...|patio.sql.Identifier...} columns columns to croup and count on.
      1587. -
      1588. *
      1589. -
      1590. * @return {patio.Dataset} a cloned dataset with the GROUP clause and count added.
      1591. -
      1592. */
      1593. -
      1594. groupAndCount: function (columns) {
      1595. -
      1596. 8 columns = argsToArray(arguments);
      1597. -
      1598. 8 var group = this.group.apply(this, columns.map(function (c) {
      1599. -
      1600. 9 return this._unaliasedIdentifier(c);
      1601. -
      1602. }, this));
      1603. -
      1604. 8 return group.select.apply(group, columns.concat([this._static.COUNT_OF_ALL_AS_COUNT]));
      1605. -
      1606. +
      1607. isUppercase:function isUppercase(opts) {
      1608. +
      1609. 1 return this.__addAction(function (col) {
      1610. +
      1611. 3 return validatorCheck(col).isUppercase();
      1612. +
      1613. }, merge({message:"{col} must be uppercase got {val}."}, opts));
      1614. },
      1615. -
      1616. /**
      1617. -
      1618. * Returns a copy of the dataset with the HAVING conditions changed. See {@link patio.Dataset#filter} for argument types.
      1619. -
      1620. *
      1621. -
      1622. * @example
      1623. -
      1624. * DB.from("items").group("sum").having({sum : 10}).sql;
      1625. -
      1626. * //=> SELECT * FROM items GROUP BY sum HAVING (sum = 10)
      1627. -
      1628. *
      1629. -
      1630. * @return {patio.Dataset} a cloned dataset with HAVING clause changed or added.
      1631. -
      1632. **/
      1633. -
      1634. having: function () {
      1635. -
      1636. 12 var cond = argsToArray(arguments).map(function (s) {
      1637. -
      1638. 12 return isString(s) && s !== '' ? this.stringToIdentifier(s) : s
      1639. -
      1640. }, this);
      1641. -
      1642. 12 return this._filter.apply(this, ["having"].concat(cond));
      1643. +
      1644. isEmpty:function isEmpty(opts) {
      1645. +
      1646. 1 return this.__addAction(function (col) {
      1647. +
      1648. 3 try {
      1649. +
      1650. 3 validatorCheck(col).notEmpty();
      1651. +
      1652. 2 return false;
      1653. +
      1654. } catch (e) {
      1655. +
      1656. 1 return true;
      1657. +
      1658. }
      1659. +
      1660. }, merge({message:"{col} must be empty got {val}."}, opts));
      1661. },
      1662. -
      1663. /**
      1664. -
      1665. * Adds an INTERSECT clause using a second dataset object.
      1666. -
      1667. * An INTERSECT compound dataset returns all rows in both the current dataset
      1668. -
      1669. * and the given dataset.
      1670. -
      1671. *
      1672. -
      1673. * @example
      1674. -
      1675. *
      1676. -
      1677. * DB.from("items").intersect(DB.from("other_items")).sql;
      1678. -
      1679. * //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS t1
      1680. -
      1681. *
      1682. -
      1683. * DB.from("items").intersect(DB.from("other_items"), {all : true, fromSelf : false}).sql;
      1684. -
      1685. * //=> SELECT * FROM items INTERSECT ALL SELECT * FROM other_items
      1686. -
      1687. *
      1688. -
      1689. * DB.from("items").intersect(DB.from("other_items"), {alias : "i"}).sql;
      1690. -
      1691. * //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS i
      1692. -
      1693. *
      1694. -
      1695. * @throws {patio.QueryError} if the operation is not supported.
      1696. -
      1697. * @param {patio.Dataset} dataset the dataset to intersect
      1698. -
      1699. * @param {Object} [opts] options
      1700. -
      1701. * @param {String|patio.sql.Identifier} [opts.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias
      1702. -
      1703. * @param {Boolean} [opts.all] Set to true to use INTERSECT ALL instead of INTERSECT, so duplicate rows can occur
      1704. -
      1705. * @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}.
      1706. -
      1707. *
      1708. -
      1709. * @return {patio.Dataset} a cloned dataset with the INTERSECT clause.
      1710. -
      1711. **/
      1712. -
      1713. intersect: function (dataset, opts) {
      1714. -
      1715. 18 opts = isUndefined(opts) ? {} : opts;
      1716. -
      1717. 18 if (!isHash(opts)) {
      1718. -
      1719. 5 opts = {all: opts};
      1720. -
      1721. }
      1722. -
      1723. 18 if (!this.supportsIntersectExcept) {
      1724. -
      1725. 2 throw new QueryError("INTERSECT not supported");
      1726. -
      1727. 16 } else if (opts.all && !this.supportsIntersectExceptAll) {
      1728. -
      1729. 1 throw new QueryError("INTERSECT ALL not supported");
      1730. -
      1731. }
      1732. -
      1733. 15 return this.compoundClone("intersect", dataset, opts);
      1734. +
      1735. isNotEmpty:function isNotEmpty(opts) {
      1736. +
      1737. 2 return this.__addAction(function (col) {
      1738. +
      1739. 11 return validatorCheck(col).notEmpty();
      1740. +
      1741. }, merge({message:"{col} must not be empty."}, opts));
      1742. },
      1743. -
      1744. /**
      1745. -
      1746. * Inverts the current filter.
      1747. -
      1748. *
      1749. -
      1750. * @example
      1751. -
      1752. * DB.from("items").filter({category : 'software'}).invert()
      1753. -
      1754. * //=> SELECT * FROM items WHERE (category != 'software')
      1755. -
      1756. *
      1757. -
      1758. * @example
      1759. -
      1760. * DB.from("items").filter({category : 'software', id : 3}).invert()
      1761. -
      1762. * //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
      1763. -
      1764. *
      1765. -
      1766. * @return {patio.Dataset} a cloned dataset with the filter inverted.
      1767. -
      1768. **/
      1769. -
      1770. invert: function () {
      1771. -
      1772. 3 var having = this.__opts.having, where = this.__opts.where;
      1773. -
      1774. 3 if (!(having || where)) {
      1775. -
      1776. 1 throw new QueryError("No current filter");
      1777. -
      1778. }
      1779. -
      1780. 2 var o = {}
      1781. -
      1782. 2 if (having) {
      1783. -
      1784. 1 o.having = BooleanExpression.invert(having);
      1785. -
      1786. }
      1787. -
      1788. 2 if (where) {
      1789. -
      1790. 2 o.where = BooleanExpression.invert(where);
      1791. -
      1792. }
      1793. -
      1794. 2 return this.mergeOptions(o);
      1795. +
      1796. isCreditCard:function isCreditCard(opts) {
      1797. +
      1798. 0 return this.__addAction(function (col) {
      1799. +
      1800. 0 return validatorCheck(col).isCreditCard();
      1801. +
      1802. }, merge({message:"{col} is an invalid credit card"}, opts));
      1803. },
      1804. -
      1805. /**
      1806. -
      1807. * Returns a cloned dataset with an inner join applied.
      1808. -
      1809. *
      1810. -
      1811. * @see patio.Dataset#joinTable
      1812. -
      1813. */
      1814. -
      1815. join: function () {
      1816. -
      1817. 212 return this.innerJoin.apply(this, arguments);
      1818. +
      1819. check:function (fun, opts) {
      1820. +
      1821. 4 return this.__addAction(fun, opts);
      1822. },
      1823. +
      1824. validate:function validate(value) {
      1825. +
      1826. 218 var errOpts = {col:this.col, val:value};
      1827. +
      1828. 218 return compact(this.__actions.map(function (action) {
      1829. +
      1830. 248 var actionOpts = action.opts;
      1831. +
      1832. 248 if (!actionOpts.onlyDefined || (combIsDefined(value) &&
      1833. +
      1834. (!actionOpts.onlyNotNull || !combIsNull(value)) )) {
      1835. +
      1836. 186 var ret = null;
      1837. +
      1838. 186 try {
      1839. +
      1840. 186 if (!action.action(value)) {
      1841. +
      1842. 69 ret = format(actionOpts.message, errOpts);
      1843. +
      1844. }
      1845. +
      1846. } catch (e) {
      1847. +
      1848. 28 ret = format(actionOpts.message, errOpts);
      1849. +
      1850. }
      1851. +
      1852. 186 return ret;
      1853. +
      1854. }
      1855. +
      1856. }, this));
      1857. +
      1858. }
      1859. +
      1860. +
      1861. }
      1862. +
      1863. });
      1864. +
      1865. +
      1866. 1function shouldValidate(opts, def) {
      1867. +
      1868. 115 opts = opts || {};
      1869. +
      1870. 115 return combIsBoolean(opts.validate) ? opts.validate : def;
      1871. +
      1872. }
      1873. +
      1874. +
      1875. 1function validateHook(prop, next, opts) {
      1876. +
      1877. 115 if (shouldValidate(opts, prop) && !this.isValid()) {
      1878. +
      1879. 45 next(flatten(toArray(this.errors).map(function (entry) {
      1880. +
      1881. 64 return entry[1].map(function (err) {
      1882. +
      1883. 48 return new Error(err);
      1884. +
      1885. });
      1886. +
      1887. })));
      1888. +
      1889. } else {
      1890. +
      1891. 70 next();
      1892. +
      1893. }
      1894. +
      1895. }
      1896. +
      1897. +
      1898. 1define(null, {
      1899. +
      1900. +
      1901. instance:{
      1902. +
      1903. /**
      1904. -
      1905. * Returns a joined dataset. Uses the following arguments:
      1906. +
      1907. * A validation plugin for patio models. This plugin adds a <code>validate</code> method to each {@link patio.Model}
      1908. +
      1909. * class that adds it as a plugin. This plugin does not include most typecast checks as <code>patio</code> already checks
      1910. +
      1911. * types upon column assignment.
      1912. *
      1913. -
      1914. * @example
      1915. +
      1916. * To do single col validation
      1917. +
      1918. * {@code
      1919. *
      1920. -
      1921. * DB.from("items").joinTable("leftOuter", "categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;
      1922. -
      1923. * //=>'SELECT
      1924. -
      1925. * *
      1926. -
      1927. * FROM
      1928. -
      1929. * `items`
      1930. -
      1931. * LEFT OUTER JOIN
      1932. -
      1933. * `categories` ON (
      1934. -
      1935. * (`categories`.`categoryId` = `items`.`id`)
      1936. -
      1937. * AND
      1938. -
      1939. * (`categories`.`categoryId` IN (1,2, 3))
      1940. -
      1941. * )
      1942. -
      1943. * DB.from("items").leftOuter("categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;
      1944. -
      1945. * //=>'SELECT
      1946. -
      1947. * *
      1948. -
      1949. * FROM
      1950. -
      1951. * `items`
      1952. -
      1953. * LEFT OUTER JOIN
      1954. -
      1955. * `categories` ON (
      1956. -
      1957. * (`categories`.`categoryId` = `items`.`id`)
      1958. -
      1959. * AND
      1960. -
      1961. * (`categories`.`categoryId` IN (1,2, 3))
      1962. -
      1963. * )
      1964. +
      1965. * var Model = patio.addModel("validator", {
      1966. +
      1967. * plugins:[ValidatorPlugin]
      1968. +
      1969. * });
      1970. +
      1971. * //this ensures column assignment
      1972. +
      1973. * Model.validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);
      1974. +
      1975. * //col2 does not have to be assigned but if it is it must match /hello/ig.
      1976. +
      1977. * Model.validate("col2").like(/hello/ig);
      1978. +
      1979. * //Ensures that the emailAddress column is a valid email address.
      1980. +
      1981. * Model.validate("emailAddress").isEmailAddress();
      1982. +
      1983. * }
      1984. +
      1985. *
      1986. +
      1987. * Or you can do a mass validation through a callback.
      1988. +
      1989. * {@code
      1990. *
      1991. -
      1992. * DB.from("items").leftOuterJoin("categories", {categoryId:"id"}).sql
      1993. -
      1994. * //=> SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      1995. +
      1996. * var Model = patio.addModel("validator", {
      1997. +
      1998. * plugins:[ValidatorPlugin]
      1999. +
      2000. * });
      2001. +
      2002. * Model.validate(function(validate){
      2003. +
      2004. * //this ensures column assignment
      2005. +
      2006. * validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);
      2007. +
      2008. * //col2 does not have to be assigned but if it is it must match /hello/ig.
      2009. +
      2010. * validate("col2").isLike(/hello/ig);
      2011. +
      2012. * //Ensures that the emailAddress column is a valid email address.
      2013. +
      2014. * validate("emailAddress").isEmail();
      2015. +
      2016. * });
      2017. +
      2018. * }
      2019. *
      2020. -
      2021. * DB.from("items").rightOuterJoin("categories", {categoryId:"id"}).sql
      2022. -
      2023. * //=> SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2024. +
      2025. * To check if a {@link patio.Model} is valid you can run the <code>isValid</code> method.
      2026. *
      2027. -
      2028. * DB.from("items").fullOuterJoin("categories", {categoryId:"id"}).sql
      2029. -
      2030. * //=> SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2031. +
      2032. * {@code
      2033. +
      2034. * var model1 = new Model({col2 : 'grape', emailAddress : "test"}),
      2035. +
      2036. * model2 = new Model({col1 : "grape", col2 : "hello", emailAddress : "test@test.com"});
      2037. *
      2038. -
      2039. * DB.from("items").innerJoin("categories", {categoryId:"id"}).sql
      2040. -
      2041. * //=> SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2042. +
      2043. * model1.isValid() //false
      2044. +
      2045. * model2.isValid() //true
      2046. +
      2047. * }
      2048. *
      2049. -
      2050. * DB.from("items").leftJoin("categories", {categoryId:"id"}).sql
      2051. -
      2052. * //=> SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2053. +
      2054. * To get the errors associated with an invalid model you can access the errors property
      2055. *
      2056. -
      2057. * DB.from("items").rightJoin("categories", {categoryId:"id"}).sql
      2058. -
      2059. * //=> SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2060. +
      2061. * {@code
      2062. +
      2063. * model1.errors; //{ col1: [ 'col1 must be defined.' ],
      2064. +
      2065. * // col2: [ 'col2 must be like /hello/gi got grape.' ],
      2066. +
      2067. * // emailAddress: [ 'emailAddress must be a valid Email Address got test' ] }
      2068. +
      2069. * }
      2070. *
      2071. -
      2072. * DB.from("items").fullJoin("categories", {categoryId:"id"}).sql
      2073. -
      2074. * //=> SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2075. +
      2076. * Validation is also run pre save and pre update. To prevent this you can specify the <code>validate</code> option
      2077. *
      2078. -
      2079. * DB.from("items").naturalJoin("categories").sql
      2080. -
      2081. * //=> SELECT * FROM "items" NATURAL JOIN "categories"
      2082. +
      2083. * {@code
      2084. +
      2085. * model1.save(null, {validate : false});
      2086. +
      2087. * model2.save(null, {validate : false});
      2088. +
      2089. * }
      2090. *
      2091. -
      2092. * DB.from("items").naturalLeftJoin("categories").sql
      2093. -
      2094. * //=> SELECT * FROM "items" NATURAL LEFT JOIN "categories"
      2095. +
      2096. * Or you can specify the class level properties <code>validateOnSave</code> and <code>validateOnUpdate</code>
      2097. +
      2098. * to false respectively
      2099. +
      2100. * {@code
      2101. +
      2102. * Model.validateOnSave = false;
      2103. +
      2104. * Model.validateOnUpdate = false;
      2105. +
      2106. * }
      2107. *
      2108. -
      2109. * DB.from("items").naturalRightJoin("categories").sql
      2110. -
      2111. * //=> SELECT * FROM "items" NATURAL RIGHT JOIN "categories"
      2112. +
      2113. * Avaiable validation methods are.
      2114. *
      2115. -
      2116. * DB.from("items").naturalFullJoin("categories").sql
      2117. -
      2118. * //=> SELECT * FROM "items" NATURAL FULL JOIN "categories"'
      2119. +
      2120. * <ul>
      2121. +
      2122. * <li><code>isAfter</code> : check that a date is after a specified date</li>
      2123. +
      2124. * <li><code>isBefore</code> : check that a date is after before a specified date </li>
      2125. +
      2126. * <li><code>isDefined</code> : ensure that a column is defined</li>
      2127. +
      2128. * <li><code>isNotDefined</code> : ensure that a column is not defined</li>
      2129. +
      2130. * <li><code>isNotNull</code> : ensure that a column is defined and not null</li>
      2131. +
      2132. * <li><code>isNull</code> : ensure that a column is not defined or null</li>
      2133. +
      2134. * <li><code>isEq</code> : ensure that a column equals a value <b>this uses deep equal</b></li>
      2135. +
      2136. * <li><code>isNeq</code> : ensure that a column does not equal a value <b>this uses deep equal</b></li>
      2137. +
      2138. * <li><code>isLike</code> : ensure that a column is like a value, can be a regexp or string</li>
      2139. +
      2140. * <li><code>isNotLike</code> : ensure that a column is not like a value, can be a regexp or string</li>
      2141. +
      2142. * <li><code>isLt</code> : ensure that a column is less than a value</li>
      2143. +
      2144. * <li><code>isGt</code> : ensure that a column is greater than a value</li>
      2145. +
      2146. * <li><code>isLte</code> : ensure that a column is less than or equal to a value</li>
      2147. +
      2148. * <li><code>isGte</code> : ensure that a column is greater than or equal to a value</li>
      2149. +
      2150. * <li><code>isIn</code> : ensure that a column is contained in an array of values</li>
      2151. +
      2152. * <li><code>isNotIn</code> : ensure that a column is not contained in an array of values</li>
      2153. +
      2154. * <li><code>isMacAddress</code> : ensure that a column is a valid MAC address</li>
      2155. +
      2156. * <li><code>isIPAddress</code> : ensure that a column is a valid IPv4 or IPv6 address</li>
      2157. +
      2158. * <li><code>isIPv4Address</code> : ensure that a column is a valid IPv4 address</li>
      2159. +
      2160. * <li><code>isIPv6Address</code> : ensure that a column is a valid IPv6 address</li>
      2161. +
      2162. * <li><code>isUUID</code> : ensure that a column is a valid UUID</li>
      2163. +
      2164. * <li><code>isEmail</code> : ensure that a column is a valid email address</li>
      2165. +
      2166. * <li><code>isUrl</code> : ensure than a column is a valid URL</li>
      2167. +
      2168. * <li><code>isAlpha</code> : ensure than a column is all letters</li>
      2169. +
      2170. * <li><code>isAlphaNumeric</code> : ensure than a column is all letters or numbers</li>
      2171. +
      2172. * <li><code>hasLength</code> : ensure than a column is fits within the specified length accepts a min and optional max value</li>
      2173. +
      2174. * <li><code>isLowercase</code> : ensure than a column is lowercase</li>
      2175. +
      2176. * <li><code>isUppercase</code> : ensure than a column is uppercase</li>
      2177. +
      2178. * <li><code>isEmpty</code> : ensure than a column empty (i.e. a blank string)</li>
      2179. +
      2180. * <li><code>isNotEmpty</code> : ensure than a column not empty (i.e. not a blank string)</li>
      2181. +
      2182. * <li><code>isCreditCard</code> : ensure than a is a valid credit card number</li>
      2183. +
      2184. * <li><code>check</code> : accepts a function to perform validation</li>
      2185. +
      2186. * </ul>
      2187. *
      2188. -
      2189. * DB.from("items").crossJoin("categories").sql
      2190. -
      2191. * //=> SELECT * FROM "items" CROSS JOIN "categories"
      2192. +
      2193. * All of the validation methods are chainable, and accept an options argument.
      2194. *
      2195. -
      2196. * @param {String} type the type of join to do.
      2197. -
      2198. * @param {String|patio.sql.Identifier|patio.Dataset|Object} table depends on the type.
      2199. -
      2200. * <ul>
      2201. -
      2202. * <li>{@link patio.Dataset} - a subselect is performed with an alias</li>
      2203. -
      2204. * <li>Object - an object that has a tableName property.</li>
      2205. -
      2206. * <li>String|{@link patio.sql.Identifier} - the name of the table</li>
      2207. -
      2208. * </ul>
      2209. -
      2210. * @param [expr] - depends on type
      2211. +
      2212. * The options include
      2213. * <ul>
      2214. -
      2215. * <li>Object|Array of two element arrays - Assumes key (1st arg) is column of joined table (unless already
      2216. -
      2217. * qualified), and value (2nd arg) is column of the last joined or primary table (or the
      2218. -
      2219. * implicitQualifier option</li>.
      2220. -
      2221. * <li>Array - If all members of the array are string or {@link patio.sql.Identifier}, considers
      2222. -
      2223. * them as columns and uses a JOIN with a USING clause. Most databases will remove duplicate columns from
      2224. -
      2225. * the result set if this is used.</li>
      2226. -
      2227. * <li>null|undefined(not passed in) - If a cb is not given, doesn't use ON or USING, so the JOIN should be a NATURAL
      2228. -
      2229. * or CROSS join. If a block is given, uses an ON clause based on the block, see below.</li>
      2230. -
      2231. * <li>Everything else - pretty much the same as a using the argument in a call to {@link patio.Dataset#filter},
      2232. -
      2233. * so strings are considered literal, {@link patio.sql.Identifiers} specify boolean columns, and patio.sql
      2234. -
      2235. * expressions can be used. Uses a JOIN with an ON clause.</li>
      2236. +
      2237. * <li><code>message</code> : a message to return if a column fails validation. The message can include <code>{val}</code> and <code>{col}</code>
      2238. +
      2239. * replacements which will insert the invalid value and the column name.
      2240. +
      2241. * </li>
      2242. +
      2243. * <li><code>[onlyDefined=true]</code> : set to false to run the method even if the column value is not defined.</li>
      2244. +
      2245. * <li><code>[onlyNotNull=true]</code> : set to false to run the method even if the column value is null.</li>
      2246. * </ul>
      2247. -
      2248. * @param {Object} options an object of options.
      2249. -
      2250. * @param {String|patio.sql.Identifier} [options.tableAlias=undefined] the name of the table's alias when joining, necessary for joining
      2251. -
      2252. * to the same table more than once. No alias is used by default.
      2253. -
      2254. * @param {String|patio.sql.Identifier} [options.implicitQualifier=undefined] The name to use for qualifying implicit conditions. By default,
      2255. -
      2256. * the last joined or primary table is used.
      2257. -
      2258. * @param {Function} [cb] cb - The cb argument should only be given if a JOIN with an ON clause is used,
      2259. -
      2260. * in which case it is called with
      2261. -
      2262. * <ul>
      2263. -
      2264. * <li>table alias/name for the table currently being joined</li>
      2265. -
      2266. * <li> the table alias/name for the last joined (or first table)
      2267. -
      2268. * <li>array of previous</li>
      2269. -
      2270. * </ul>
      2271. -
      2272. * the cb should return an expression to be used in the ON clause.
      2273. *
      2274. -
      2275. * @return {patio.Dataset} a cloned dataset joined using the arguments.
      2276. +
      2277. *
      2278. +
      2279. * @constructs
      2280. +
      2281. * @name ValidatorPlugin
      2282. +
      2283. * @memberOf patio.plugins
      2284. +
      2285. * @property {Object} [errors={}] the validation errors for this model.
      2286. +
      2287. *
      2288. */
      2289. +
      2290. constructor:function () {
      2291. +
      2292. 114 this._super(arguments);
      2293. +
      2294. 114 this.errors = {};
      2295. +
      2296. },
      2297. -
      2298. joinTable: function (type, table, expr, options, cb) {
      2299. -
      2300. 634 var args = argsToArray(arguments);
      2301. -
      2302. 634 if (isFunction(args[args.length - 1])) {
      2303. -
      2304. 12 cb = args[args.length - 1];
      2305. -
      2306. 12 args.pop();
      2307. -
      2308. } else {
      2309. -
      2310. 622 cb = null;
      2311. -
      2312. }
      2313. -
      2314. 634 type = args.shift(), table = args.shift(), expr = args.shift(), options = args.shift();
      2315. -
      2316. 634 expr = isUndefined(expr) ? null : expr, options = isUndefined(options) ? {} : options;
      2317. +
      2318. /**
      2319. +
      2320. * Validates a model, returning an array of error messages for each invalid property.
      2321. +
      2322. * @return {String[]} an array of error messages for each invalid property.
      2323. +
      2324. */
      2325. +
      2326. validate:function () {
      2327. +
      2328. 159 this.errors = {};
      2329. +
      2330. 159 return flatten(this._static.validators.map(function runValidator(validator) {
      2331. +
      2332. 218 var col = validator.col, val = this.__values[validator.col], ret = validator.validate(val);
      2333. +
      2334. 218 this.errors[col] = ret;
      2335. +
      2336. 218 return ret;
      2337. +
      2338. }, this));
      2339. +
      2340. },
      2341. -
      2342. 634 var h;
      2343. -
      2344. 634 var usingJoin = isArray(expr) && expr.length && expr.every(function (x) {
      2345. -
      2346. 223 return isString(x) || isInstanceOf(x, Identifier)
      2347. -
      2348. });
      2349. -
      2350. 634 if (usingJoin && !this.supportsJoinUsing) {
      2351. -
      2352. 1 h = {};
      2353. -
      2354. 1 expr.forEach(function (s) {
      2355. -
      2356. 1 h[s] = s;
      2357. -
      2358. });
      2359. -
      2360. 1 return this.joinTable(type, table, h, options);
      2361. -
      2362. }
      2363. -
      2364. 633 var tableAlias, lastAlias;
      2365. -
      2366. 633 if (isHash(options)) {
      2367. -
      2368. 623 tableAlias = options.tableAlias;
      2369. -
      2370. 623 lastAlias = options.implicitQualifier;
      2371. -
      2372. 10 } else if (isString(options) || isInstanceOf(options, Identifier)) {
      2373. -
      2374. 9 tableAlias = options;
      2375. -
      2376. 9 lastAlias = null;
      2377. -
      2378. } else {
      2379. -
      2380. 1 throw new QueryError("Invalid options format for joinTable %j4", [options]);
      2381. -
      2382. }
      2383. -
      2384. 632 var tableAliasNum, tableName;
      2385. -
      2386. 632 if (isInstanceOf(table, Dataset)) {
      2387. -
      2388. 11 if (!tableAlias) {
      2389. -
      2390. 6 tableAliasNum = (this.__opts.numDatasetSources || 0) + 1;
      2391. -
      2392. 6 tableAlias = this._datasetAlias(tableAliasNum);
      2393. -
      2394. }
      2395. -
      2396. 11 tableName = tableAlias;
      2397. -
      2398. } else {
      2399. -
      2400. 621 if (!isUndefined(table.tableName)) {
      2401. -
      2402. 1 table = table.tableName;
      2403. -
      2404. }
      2405. -
      2406. 621 if (isArray(table)) {
      2407. -
      2408. 2 table = table.map(this.stringToIdentifier, this);
      2409. -
      2410. } else {
      2411. -
      2412. 619 table = isString(table) ? this.stringToIdentifier(table) : table;
      2413. -
      2414. 619 var parts = this._splitAlias(table), implicitTableAlias = parts[1];
      2415. -
      2416. 619 table = parts[0]
      2417. -
      2418. 619 tableAlias = tableAlias || implicitTableAlias;
      2419. -
      2420. 619 tableName = tableAlias || table;
      2421. -
      2422. }
      2423. -
      2424. }
      2425. -
      2426. 632 var join;
      2427. -
      2428. 632 if (!expr && !cb) {
      2429. -
      2430. 22 join = new JoinClause(type, table, tableAlias);
      2431. -
      2432. 610 } else if (usingJoin) {
      2433. -
      2434. 9 if (cb) {
      2435. -
      2436. 1 throw new QueryError("cant use a cb if an array is given");
      2437. -
      2438. }
      2439. -
      2440. 8 join = new JoinUsingClause(expr, type, table, tableAlias);
      2441. -
      2442. } else {
      2443. -
      2444. 601 lastAlias = lastAlias || this.__opts["lastJoinedTable"] || this.firstSourceAlias;
      2445. -
      2446. 600 if (Expression.isConditionSpecifier(expr)) {
      2447. -
      2448. 588 var newExpr = [];
      2449. -
      2450. 588 for (var i in expr) {
      2451. -
      2452. 909 var val = expr[i];
      2453. -
      2454. 909 if (isArray(val) && val.length == 2) {
      2455. -
      2456. 418 i = val[0], val = val[1];
      2457. -
      2458. }
      2459. -
      2460. 909 var k = this.qualifiedColumnName(i, tableName), v;
      2461. -
      2462. 909 if (isInstanceOf(val, Identifier)) {
      2463. -
      2464. 405 v = val.qualify(lastAlias);
      2465. -
      2466. } else {
      2467. -
      2468. 504 v = val;
      2469. -
      2470. }
      2471. -
      2472. 909 newExpr.push([k, v]);
      2473. -
      2474. }
      2475. -
      2476. 588 expr = newExpr;
      2477. -
      2478. }
      2479. -
      2480. 600 if (isFunction(cb)) {
      2481. -
      2482. 11 var expr2 = cb.apply(sql, [tableName, lastAlias, this.__opts.join || []]);
      2483. -
      2484. 11 expr = expr ? new BooleanExpression("AND", expr, expr2) : expr2;
      2485. -
      2486. }
      2487. -
      2488. 600 join = new JoinOnClause(expr, type, table, tableAlias);
      2489. -
      2490. }
      2491. -
      2492. 630 var opts = {join: (this.__opts.join || []).concat([join]), lastJoinedTable: tableName};
      2493. -
      2494. 630 if (tableAliasNum) {
      2495. -
      2496. 6 opts.numDatasetSources = tableAliasNum;
      2497. +
      2498. /**
      2499. +
      2500. * Returns if this model passes validation.
      2501. +
      2502. *
      2503. +
      2504. * @return {Boolean}
      2505. +
      2506. */
      2507. +
      2508. isValid:function () {
      2509. +
      2510. 159 return this.validate().length === 0;
      2511. +
      2512. }
      2513. +
      2514. },
      2515. +
      2516. +
      2517. "static":{
      2518. +
      2519. /**@lends patio.plugins.ValidatorPlugin*/
      2520. +
      2521. +
      2522. /**
      2523. +
      2524. * Set to false to prevent model validation when saving.
      2525. +
      2526. * @default true
      2527. +
      2528. */
      2529. +
      2530. validateOnSave:true,
      2531. +
      2532. +
      2533. /**
      2534. +
      2535. * Set to false to prevent model validation when updating.
      2536. +
      2537. * @default true
      2538. +
      2539. */
      2540. +
      2541. validateOnUpdate:true,
      2542. +
      2543. +
      2544. init:function () {
      2545. +
      2546. 35 this._super(arguments);
      2547. +
      2548. },
      2549. +
      2550. +
      2551. __initValidation:function () {
      2552. +
      2553. 43 if (!this.__isValidationInited) {
      2554. +
      2555. 34 this.validators = [];
      2556. +
      2557. 34 this.pre("save", function preSaveValidate(next, opts) {
      2558. +
      2559. 114 validateHook.call(this, this._static.validateOnSave, next, opts);
      2560. +
      2561. });
      2562. +
      2563. 34 this.pre("update", function preUpdateValidate(next, opts) {
      2564. +
      2565. 1 validateHook.call(this, this._static.validateOnSave, next, opts);
      2566. +
      2567. });
      2568. +
      2569. 34 this.__isValidationInited = true;
      2570. }
      2571. -
      2572. 630 return this.mergeOptions(opts);
      2573. +
      2574. },
      2575. +
      2576. __getValidator:function validator(name) {
      2577. +
      2578. 44 var ret = new Validator(name);
      2579. +
      2580. 44 this.validators.push(ret);
      2581. +
      2582. 44 return ret;
      2583. },
      2584. /**
      2585. -
      2586. * If given an integer, the dataset will contain only the first l results.
      2587. -
      2588. If a second argument is given, it is used as an offset. To use
      2589. -
      2590. * an offset without a limit, pass null as the first argument.
      2591. +
      2592. * Sets up validation for a model.
      2593. *
      2594. -
      2595. * @example
      2596. +
      2597. * To do single col validation
      2598. +
      2599. * {@code
      2600. *
      2601. -
      2602. * DB.from("items").limit(10)
      2603. -
      2604. * //=> SELECT * FROM items LIMIT 10
      2605. -
      2606. * DB.from("items").limit(10, 20)
      2607. -
      2608. * //=> SELECT * FROM items LIMIT 10 OFFSET 20
      2609. -
      2610. * DB.from("items").limit([3, 7]).sql
      2611. -
      2612. * //=> SELECT * FROM items LIMIT 5 OFFSET 3');
      2613. -
      2614. * DB.from("items").limit(null, 20)
      2615. -
      2616. * //=> SELECT * FROM items OFFSET 20
      2617. +
      2618. * var Model = patio.addModel("validator", {
      2619. +
      2620. * plugins:[ValidatorPlugin]
      2621. +
      2622. * });
      2623. +
      2624. * //this ensures column assignment
      2625. +
      2626. * Model.validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);
      2627. +
      2628. * //col2 does not have to be assigned but if it is it must match /hello/ig.
      2629. +
      2630. * Model.validate("col2").like(/hello/ig);
      2631. +
      2632. * //Ensures that the emailAddress column is a valid email address.
      2633. +
      2634. * Model.validate("emailAddress").isEmailAddress();
      2635. +
      2636. * }
      2637. *
      2638. -
      2639. * DB.from("items").limit('6', sql['a() - 1']).sql
      2640. -
      2641. * => 'SELECT * FROM items LIMIT 6 OFFSET a() - 1');
      2642. +
      2643. * Or you can do a mass validation through a callback.
      2644. +
      2645. * {@code
      2646. *
      2647. -
      2648. * @param {Number|String|Number[]} limit the limit to apply
      2649. -
      2650. * @param {Number|String|patio.sql.LiteralString} offset the offset to apply
      2651. +
      2652. * var Model = patio.addModel("validator", {
      2653. +
      2654. * plugins:[ValidatorPlugin]
      2655. +
      2656. * });
      2657. +
      2658. * Model.validate(function(validate){
      2659. +
      2660. * //this ensures column assignment
      2661. +
      2662. * validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);
      2663. +
      2664. * //col2 does not have to be assigned but if it is it must match /hello/ig.
      2665. +
      2666. * validate("col2").isLike(/hello/ig);
      2667. +
      2668. * //Ensures that the emailAddress column is a valid email address.
      2669. +
      2670. * validate("emailAddress").isEmail();
      2671. +
      2672. * });
      2673. +
      2674. * }
      2675. *
      2676. -
      2677. * @return {patio.Dataset} a cloned dataset witht the LIMIT and OFFSET applied.
      2678. -
      2679. **/
      2680. -
      2681. limit: function (limit, offset) {
      2682. -
      2683. 46 if (this.__opts.sql) {
      2684. -
      2685. 7 return this.fromSelf().limit(limit, offset);
      2686. -
      2687. }
      2688. -
      2689. 39 if (isArray(limit) && limit.length == 2) {
      2690. -
      2691. 1 offset = limit[0];
      2692. -
      2693. 1 limit = limit[1] - limit[0] + 1;
      2694. -
      2695. }
      2696. -
      2697. 39 if (isString(limit) || isInstanceOf(limit, LiteralString)) {
      2698. -
      2699. 2 limit = parseInt("" + limit, 10);
      2700. -
      2701. }
      2702. -
      2703. 39 if (isNumber(limit) && limit < 1) {
      2704. -
      2705. 2 throw new QueryError("Limit must be >= 1");
      2706. +
      2707. *
      2708. +
      2709. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
      2710. +
      2711. *
      2712. +
      2713. * @throws {patio.ModelError} if name is not a function or string.
      2714. +
      2715. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
      2716. +
      2717. */
      2718. +
      2719. validate:function (name) {
      2720. +
      2721. 43 this.__initValidation();
      2722. +
      2723. 43 var ret;
      2724. +
      2725. 43 if (isFunction(name)) {
      2726. +
      2727. 1 name.apply(this, [this.__getValidator.bind(this)]);
      2728. +
      2729. 1 ret = this;
      2730. +
      2731. 42 } else if (isString(name)) {
      2732. +
      2733. 42 ret = this.__getValidator(name);
      2734. +
      2735. } else {
      2736. +
      2737. 0 throw new ModelError("name is must be a string or function when validating");
      2738. }
      2739. -
      2740. 37 var opts = {limit: limit};
      2741. -
      2742. 37 if (offset) {
      2743. -
      2744. 9 if (isString(offset) || isInstanceOf(offset, LiteralString)) {
      2745. -
      2746. 1 offset = parseInt("" + offset, 10);
      2747. -
      2748. 1 isNaN(offset) && (offset = 0);
      2749. +
      2750. 43 return ret;
      2751. +
      2752. }
      2753. +
      2754. }
      2755. +
      2756. +
      2757. }).as(module);
      2758. +
      +
    + +
    + + + + + +
    +
    dataset/query.js
    +
    +
    + Coverage97.82 + SLOC2347 + LOC458 + Missed10 +
    +
    +
    1. 1var comb = require("comb"),
    2. +
    3. array = comb.array,
    4. +
    5. flatten = array.flatten,
    6. +
    7. compact = array.compact,
    8. +
    9. define = comb.define,
    10. +
    11. argsToArray = comb.argsToArray,
    12. +
    13. isString = comb.isString,
    14. +
    15. isEmpty = comb.isEmpty,
    16. +
    17. isNull = comb.isNull,
    18. +
    19. isBoolean = comb.isBoolean,
    20. +
    21. isNumber = comb.isNumber,
    22. +
    23. merge = comb.merge,
    24. +
    25. isArray = comb.isArray,
    26. +
    27. isObject = comb.isObject,
    28. +
    29. isFunction = comb.isFunction,
    30. +
    31. isUndefined = comb.isUndefined,
    32. +
    33. isHash = comb.isHash,
    34. +
    35. isInstanceOf = comb.isInstanceOf,
    36. +
    37. sql = require("../sql").sql,
    38. +
    39. LiteralString = sql.LiteralString,
    40. +
    41. Expression = sql.Expression,
    42. +
    43. ComplexExpression = sql.ComplexExpression,
    44. +
    45. BooleanExpression = sql.BooleanExpression,
    46. +
    47. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    48. +
    49. Identifier = sql.Identifier,
    50. +
    51. QualifiedIdentifier = sql.QualifiedIdentifier,
    52. +
    53. AliasedExpression = sql.AliasedExpression,
    54. +
    55. StringExpression = sql.StringExpression,
    56. +
    57. NumericExpression = sql.NumericExpression,
    58. +
    59. OrderedExpression = sql.OrderedExpression,
    60. +
    61. JoinClause = sql.JoinClause,
    62. +
    63. JoinOnClause = sql.JoinOnClause,
    64. +
    65. JoinUsingClause = sql.JoinUsingClause,
    66. +
    67. ColumnAll = sql.ColumnAll,
    68. +
    69. QueryError = require("../errors").QueryError;
    70. +
    71. +
    72. +
    73. 1var Dataset;
    74. +
    75. +
    76. 1function conditionedJoin(type) {
    77. +
    78. 539 var args = argsToArray(arguments, 1);
    79. +
    80. 539 return this.joinTable.apply(this, [type].concat(args));
    81. +
    82. }
    83. +
    84. +
    85. 1function unConditionJoin(type, table) {
    86. +
    87. 6 var args = argsToArray(arguments, 1);
    88. +
    89. 6 return this.joinTable.apply(this, [type, table]);
    90. +
    91. }
    92. +
    93. +
    94. +
    95. 1define(null, {
    96. +
    97. /**@ignore*/
    98. +
    99. instance: {
    100. +
    101. +
    102. /**@lends patio.Dataset.prototype*/
    103. +
    104. +
    105. /**
    106. +
    107. * @ignore
    108. +
    109. */
    110. +
    111. constructor: function () {
    112. +
    113. 25745 !Dataset && (Dataset = require("../index").Dataset);
    114. +
    115. 25745 this._super(arguments);
    116. +
    117. 25745 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
    118. +
    119. 180215 if (!this[type + "Join"]) {
    120. +
    121. 180215 this[type + "Join"] = conditionedJoin.bind(this, type);
    122. }
    123. -
    124. 9 if (isNumber(offset) && offset < 0) {
    125. -
    126. 1 throw new QueryError("Offset must be >= 0");
    127. +
    128. +
    129. }, this);
    130. +
    131. 25745 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
    132. +
    133. 128725 if (!this[type + "Join"]) {
    134. +
    135. 128725 this[type + "Join"] = unConditionJoin.bind(this, type);
    136. }
    137. -
    138. 8 opts.offset = offset;
    139. +
    140. +
    141. }, this);
    142. +
    143. },
    144. +
    145. +
    146. /**
    147. +
    148. * Adds a RETURNING clause, which is not supported by all databases. If returning is
    149. +
    150. * used instead of returning the autogenerated primary key or update/delete returning the number of rows modified.
    151. +
    152. *
    153. +
    154. * @example
    155. +
    156. *
    157. +
    158. * ds.from("items").returning() //"RETURNING *"
    159. +
    160. * ds.from("items").returning(null) //"RETURNING NULL"
    161. +
    162. * ds.from("items").returning("id", "name") //"RETURNING id, name"
    163. +
    164. * ds.from("items").returning(["id", "name"]) //"RETURNING id, name"
    165. +
    166. *
    167. +
    168. * @param values columns to return. If values is an array then the array is assumed to contain the columns to
    169. +
    170. * return. Otherwise the arguments will be used.
    171. +
    172. * @return {patio.Dataset} a new dataset with the retuning option added.
    173. +
    174. */
    175. +
    176. returning: function (values) {
    177. +
    178. 1087 var args;
    179. +
    180. 1087 if (Array.isArray(values)) {
    181. +
    182. 0 args = values;
    183. +
    184. } else {
    185. +
    186. 1087 args = argsToArray(arguments);
    187. }
    188. -
    189. 36 return this.mergeOptions(opts);
    190. +
    191. 1087 return this.mergeOptions({returning: args.map(function (v) {
    192. +
    193. 1005 return isString(v) ? sql.stringToIdentifier(v) : v;
    194. +
    195. })});
    196. },
    197. +
    198. /**
    199. +
    200. * Adds a further filter to an existing filter using AND. This method is identical to {@link patio.Dataset#filter}
    201. +
    202. * except it expects an existing filter.
    203. *
    204. -
    205. * Returns a cloned dataset with a not equal expression added to the WHERE
    206. -
    207. * clause.
    208. +
    209. * <p>
    210. +
    211. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    212. +
    213. * </p>
    214. *
    215. * @example
    216. -
    217. * DB.from("test").neq({x : 1});
    218. -
    219. * //=> SELECT * FROM test WHERE (x != 1)
    220. -
    221. * DB.from("test").neq({x : 1, y : 10});
    222. -
    223. * //=> SELECT * FROM test WHERE ((x != 1) AND (y != 10))
    224. +
    225. * DB.from("table").filter("a").and("b").sql;
    226. +
    227. * //=>SELECT * FROM table WHERE a AND b
    228. *
    229. -
    230. * @param {Object} obj object used to create the not equal expression
    231. +
    232. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
    233. *
    234. -
    235. * @return {patio.Dataset} a cloned dataset with the not equal expression added to the WHERE clause.
    236. +
    237. * @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.
    238. */
    239. -
    240. neq: function (obj) {
    241. -
    242. 2 return this.filter(this.__createBoolExpression("neq", obj));
    243. +
    244. and: function () {
    245. +
    246. 7 var tOpts = this.__opts, clauseObj = tOpts[tOpts.having ? "having" : "where"];
    247. +
    248. 7 if (clauseObj) {
    249. +
    250. 6 return this.filter.apply(this, arguments);
    251. +
    252. } else {
    253. +
    254. 1 throw new QueryError("No existing filter found");
    255. +
    256. }
    257. +
    258. },
    259. +
    260. +
    261. as: function (alias) {
    262. +
    263. 8 return new AliasedExpression(this, alias);
    264. },
    265. /**
    266. +
    267. * Adds an alternate filter to an existing WHERE/HAVING using OR.
    268. *
    269. -
    270. * Returns a cloned dataset with an equal expression added to the WHERE
    271. -
    272. * clause.
    273. +
    274. * <p>
    275. +
    276. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    277. +
    278. * </p>
    279. *
    280. * @example
    281. -
    282. * DB.from("test").eq({x : 1});
    283. -
    284. * //=> SELECT * FROM test WHERE (x = 1)
    285. -
    286. * DB.from("test").eq({x : 1, y : 10});
    287. -
    288. * //=> SELECT * FROM test WHERE ((x = 1) AND (y = 10))
    289. *
    290. -
    291. * @param {Object} obj object used to create the equal expression
    292. +
    293. * DB.from("items").filter("a").or("b")
    294. +
    295. * //=> SELECT * FROM items WHERE a OR b
    296. *
    297. -
    298. * @return {patio.Dataset} a cloned dataset with the equal expression added to the WHERE clause.
    299. +
    300. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
    301. +
    302. * @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.
    303. */
    304. -
    305. eq: function (obj) {
    306. -
    307. 2 return this.filter(this.__createBoolExpression("eq", obj));
    308. +
    309. or: function () {
    310. +
    311. 10 var tOpts = this.__opts;
    312. +
    313. 10 var clause = (tOpts.having ? "having" : "where"), clauseObj = tOpts[clause];
    314. +
    315. 10 if (clauseObj) {
    316. +
    317. 9 var args = argsToArray(arguments);
    318. +
    319. 9 args = args.length == 1 ? args[0] : args;
    320. +
    321. 9 var opts = {};
    322. +
    323. 9 opts[clause] = new BooleanExpression("OR", clauseObj, this._filterExpr(args));
    324. +
    325. 9 return this.mergeOptions(opts);
    326. +
    327. } else {
    328. +
    329. 1 throw new QueryError("No existing filter found");
    330. +
    331. }
    332. },
    333. /**
    334. +
    335. * Adds a group of ORed conditions wrapped in parens, connected to an existing where/having clause by an AND.
    336. +
    337. * If the where/having clause doesn't yet exist, a where clause is created with the ORed group.
    338. *
    339. -
    340. * Returns a cloned dataset with a greater than expression added to the WHERE
    341. -
    342. * clause.
    343. +
    344. * <p>
    345. +
    346. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    347. +
    348. * </p>
    349. *
    350. * @example
    351. -
    352. * DB.from("test").gt({x : 1});
    353. -
    354. * //=> SELECT * FROM test WHERE (x > 1)
    355. -
    356. * DB.from("test").gt({x : 1, y : 10});
    357. -
    358. * //=> SELECT * FROM test WHERE ((x > 1) AND (y > 10))
    359. *
    360. -
    361. * @param {Object} obj object used to create the greater than expression.
    362. +
    363. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    364. +
    365. * //=> SELECT
    366. +
    367. * *
    368. +
    369. * FROM
    370. +
    371. * items
    372. +
    373. * WHERE
    374. +
    375. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
    376. *
    377. -
    378. * @return {patio.Dataset} a cloned dataset with the greater than expression added to the WHERE clause.
    379. +
    380. * DB.from("items").andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    381. +
    382. * //=> SELECT
    383. +
    384. * *
    385. +
    386. * FROM
    387. +
    388. * items
    389. +
    390. * WHERE
    391. +
    392. * ((price < 0) OR (price > 10))
    393. +
    394. *
    395. +
    396. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
    397. */
    398. -
    399. gt: function (obj) {
    400. -
    401. 2 return this.filter(this.__createBoolExpression("gt", obj));
    402. +
    403. andGroupedOr: function (filterExp) {
    404. +
    405. 2 return this._addGroupedCondition("AND", "OR", filterExp);
    406. },
    407. /**
    408. +
    409. * Adds a group of ANDed conditions wrapped in parens to an existing where/having clause by an AND. If there isn't
    410. +
    411. * yet a clause, a where clause is created with the ANDed conditions
    412. *
    413. -
    414. * Returns a cloned dataset with a less than expression added to the WHERE
    415. -
    416. * clause.
    417. +
    418. * <p>
    419. +
    420. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    421. +
    422. * </p>
    423. *
    424. * @example
    425. -
    426. * DB.from("test").lt({x : 1});
    427. -
    428. * //=> SELECT * FROM test WHERE (x < 1)
    429. -
    430. * DB.from("test").lt({x : 1, y : 10});
    431. -
    432. * //=> SELECT * FROM test WHERE ((x < 1) AND (y < 10))
    433. *
    434. -
    435. * @param {Object} obj object used to create the less than expression.
    436. +
    437. * DB.from("items").filter({id, [1,2,3]}).andGroupedAnd([{price: {gt : 0}}, {price: {lt: 10}]).sql;
    438. +
    439. * //=> SELECT
    440. +
    441. * *
    442. +
    443. * FROM
    444. +
    445. * items
    446. +
    447. * WHERE
    448. +
    449. * ((id IN (1, 2, 3)) AND ((price > 0) AND (price < 10)))
    450. *
    451. -
    452. * @return {patio.Dataset} a cloned dataset with the less than expression added to the WHERE clause.
    453. +
    454. * DB.from("items").andGroupedAnd([{price: {gt : 0}}, {price: {lt: 10}]).sql;
    455. +
    456. * //=> SELECT
    457. +
    458. * *
    459. +
    460. * FROM
    461. +
    462. * items
    463. +
    464. * WHERE
    465. +
    466. * ((price > 0) AND (price < 10))
    467. +
    468. *
    469. +
    470. * @return {patio.Dataset} a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.
    471. */
    472. -
    473. lt: function (obj) {
    474. -
    475. 2 return this.filter(this.__createBoolExpression("lt", obj));
    476. +
    477. andGroupedAnd: function (filterExp) {
    478. +
    479. 2 return this._addGroupedCondition("AND", "AND", filterExp);
    480. },
    481. /**
    482. -
    483. * Returnes a cloned dataset with the IS NOT expression added to the WHERE
    484. -
    485. * clause.
    486. +
    487. * Adds a group of ANDed conditions wrapped in parens to an existing where/having clause by an OR. If there isn't
    488. +
    489. * a where/having clause, a where clause is created with the ANDed conditions.
    490. *
    491. -
    492. * @example
    493. +
    494. * <p>
    495. +
    496. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    497. +
    498. * </p>
    499. *
    500. -
    501. * DB.from("test").isNot({boolFlag : null});
    502. -
    503. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL);
    504. -
    505. * DB.from("test").isNot({boolFlag : false, otherFlag : true, name : null});
    506. -
    507. * => SELECT * FROM test WHERE ((boolFlag IS NOT FALSE) AND (otherFlag IS NOT TRUE) AND (name IS NOT NULL));
    508. +
    509. * @example
    510. *
    511. -
    512. * @param {Object} obj object used to create the IS NOT expression for.
    513. +
    514. * DB.from("items").filter({id, [1,2,3]}).orGroupedAnd([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    515. +
    516. * //=> SELECT
    517. +
    518. * *
    519. +
    520. * FROM
    521. +
    522. * items
    523. +
    524. * WHERE
    525. +
    526. * ((id IN (1, 2, 3)) OR ((price > 0) AND (price < 10)))
    527. *
    528. -
    529. * @return {patio.Dataset} a cloned dataset with the IS NOT expression added to the WHERE clause.
    530. +
    531. * DB.from("items").orGroupedAnd([{price: {gt : 0}}, {price: {gt: 10}]).sql;
    532. +
    533. * //=> SELECT
    534. +
    535. * *
    536. +
    537. * FROM
    538. +
    539. * items
    540. +
    541. * WHERE
    542. +
    543. * ((price > 0) AND (price < 10))
    544. *
    545. +
    546. * @return {patio.Dataset} a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.
    547. */
    548. -
    549. isNot: function (obj) {
    550. -
    551. 4 return this.filter(this.__createBoolExpression("isNot", obj));
    552. +
    553. 2 orGroupedAnd: function () {var tOpts = this.__opts,
    554. +
    555. clause = (tOpts.having ? "having" : "where"),
    556. +
    557. clauseObj = tOpts[clause];
    558. +
    559. 2 if (clauseObj) {
    560. +
    561. 1 return this.or.apply(this, arguments);
    562. +
    563. } else {
    564. +
    565. 1 var args = argsToArray(arguments);
    566. +
    567. 1 args = args.length == 1 ? args[0] : args;
    568. +
    569. 1 var opts = {};
    570. +
    571. 1 opts[clause] = this._filterExpr(args, null, "AND");
    572. +
    573. 1 return this.mergeOptions(opts);
    574. +
    575. }
    576. },
    577. /**
    578. -
    579. * Returnes a cloned dataset with the IS expression added to the WHERE
    580. -
    581. * clause.
    582. +
    583. * Adds a group of ORed conditions wrapped in parens to an existing having/where clause with an OR. If there isn't
    584. +
    585. * already a clause, a where clause is created with the ORed group.
    586. *
    587. -
    588. * @example
    589. +
    590. * <p>
    591. +
    592. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    593. +
    594. * </p>
    595. *
    596. -
    597. * DB.from("test").is({boolFlag : null});
    598. -
    599. * => SELECT * FROM test WHERE (boolFlag IS NULL);
    600. -
    601. * DB.from("test").is({boolFlag : false, otherFlag : true, name : null});
    602. -
    603. * => SELECT * FROM test WHERE ((boolFlag IS FALSE) AND (otherFlag IS TRUE) AND (name IS NULL));
    604. +
    605. * @example
    606. *
    607. -
    608. * @param {Object} obj object used to create the IS expression for.
    609. +
    610. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    611. +
    612. * //=> SELECT
    613. +
    614. * *
    615. +
    616. * FROM
    617. +
    618. * items
    619. +
    620. * WHERE
    621. +
    622. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
    623. *
    624. -
    625. * @return {patio.Dataset} a cloned dataset with the IS expression added to the WHERE clause.
    626. +
    627. * DB.from("items").orGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    628. +
    629. * //=> SELECT
    630. +
    631. * *
    632. +
    633. * FROM
    634. +
    635. * items
    636. +
    637. * WHERE
    638. +
    639. * ((price < 0) OR (price > 10))
    640. *
    641. +
    642. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
    643. */
    644. -
    645. is: function (obj) {
    646. -
    647. 4 return this.filter(this.__createBoolExpression("is", obj));
    648. +
    649. orGroupedOr: function (filterExp) {
    650. +
    651. 2 return this._addGroupedCondition("OR", "OR", filterExp);
    652. },
    653. /**
    654. -
    655. * Returnes a cloned dataset with the IS NOT NULL boolean expression added to the WHERE
    656. -
    657. * clause.
    658. +
    659. * Returns a copy of the dataset with the SQL DISTINCT clause.
    660. +
    661. * The DISTINCT clause is used to remove duplicate rows from the
    662. +
    663. * output. If arguments are provided, uses a DISTINCT ON clause,
    664. +
    665. * in which case it will only be distinct on those columns, instead
    666. +
    667. * of all returned columns.
    668. *
    669. * @example
    670. *
    671. -
    672. * DB.from("test").isNotNull("boolFlag");
    673. -
    674. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL);
    675. -
    676. * DB.from("test").isNotNull("boolFlag", "otherFlag");
    677. -
    678. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL AND otherFlag IS NOT NULL);
    679. -
    680. *
    681. -
    682. * @param {String...} arr variable number of arguments to create an IS NOT NULL expression for.
    683. -
    684. *
    685. -
    686. * @return {patio.Dataset} a cloned dataset with the IS NOT NULL expression added to the WHERE clause.
    687. +
    688. * DB.from("items").distinct().sqll
    689. +
    690. * //=> SELECT DISTINCT * FROM items
    691. +
    692. * DB.from("items").order("id").distinct("id").sql;
    693. +
    694. * //=> SELECT DISTINCT ON (id) * FROM items ORDER BY id
    695. *
    696. +
    697. * @throws {patio.QueryError} If arguments are given and DISTINCT ON is not supported.
    698. +
    699. * @param {...String|...patio.sql.Identifier} args variable number of arguments used to create
    700. +
    701. * the DISTINCT ON clause.
    702. +
    703. * @return {patio.Dataset} a cloned dataset with the DISTINCT/DISTINCT ON clause added.
    704. */
    705. -
    706. isNotNull: function (arr) {
    707. -
    708. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);
    709. -
    710. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    711. +
    712. distinct: function (args) {
    713. +
    714. 13 args = argsToArray(arguments);
    715. +
    716. 13 if (args.length && !this.supportsDistinctOn) {
    717. +
    718. 1 throw new QueryError("DISTICT ON is not supported");
    719. +
    720. }
    721. +
    722. 12 args = args.map(function (a) {
    723. +
    724. 6 return isString(a) ? new Identifier(a) : a;
    725. +
    726. });
    727. +
    728. 12 return this.mergeOptions({distinct: args});
    729. },
    730. /**
    731. -
    732. * Returnes a cloned dataset with the IS NULL boolean expression added to the WHERE
    733. -
    734. * clause.
    735. +
    736. * Adds an EXCEPT clause using a second dataset object.
    737. +
    738. * An EXCEPT compound dataset returns all rows in the current dataset
    739. +
    740. * that are not in the given dataset.
    741. *
    742. * @example
    743. *
    744. -
    745. * DB.from("test").isNull("boolFlag");
    746. -
    747. * => SELECT * FROM test WHERE (boolFlag IS NULL);
    748. -
    749. * DB.from("test").isNull("boolFlag", "otherFlag");
    750. -
    751. * => SELECT * FROM test WHERE (boolFlag IS NULL AND otherFlag IS NULL);
    752. +
    753. * DB.from("items").except(DB.from("other_items")).sql;
    754. +
    755. * //=> SELECT * FROM items EXCEPT SELECT * FROM other_items
    756. *
    757. -
    758. * @param {String...} arr variable number of arguments to create an IS NULL expression for.
    759. +
    760. * DB.from("items").except(DB.from("other_items"),
    761. +
    762. * {all : true, fromSelf : false}).sql;
    763. +
    764. * //=> SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
    765. *
    766. -
    767. * @return {patio.Dataset} a cloned dataset with the IS NULL expression added to the WHERE clause.
    768. +
    769. * DB.from("items").except(DB.from("other_items"),
    770. +
    771. * {alias : "i"}).sql;
    772. +
    773. * //=>SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS i
    774. +
    775. *
    776. +
    777. * @throws {patio.QueryError} if the operation is not supported.
    778. +
    779. * @param {patio.Dataset} dataset the dataset to use to create the EXCEPT clause.
    780. +
    781. * @param {Object} [opts] options to use when creating the EXCEPT clause
    782. +
    783. * @param {String|patio.sql.Identifier} [opt.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias.
    784. +
    785. * @param {Boolean} [opts.all] Set to true to use EXCEPT ALL instead of EXCEPT, so duplicate rows can occur
    786. +
    787. * @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}, use with care.
    788. *
    789. +
    790. * @return {patio.Dataset} a cloned dataset with the EXCEPT clause added.
    791. */
    792. -
    793. isNull: function (arr) {
    794. -
    795. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);
    796. -
    797. 2 return this.filter(this.__createBoolExpression("is", arr));
    798. +
    799. except: function (dataset, opts) {
    800. +
    801. 18 opts = isUndefined(opts) ? {} : opts;
    802. +
    803. 18 if (!isHash(opts)) {
    804. +
    805. 5 opts = {all: true};
    806. +
    807. }
    808. +
    809. 18 if (!this.supportsIntersectExcept) {
    810. +
    811. 2 throw new QueryError("EXCEPT not supoorted");
    812. +
    813. 16 } else if (opts.hasOwnProperty("all") && !this.supportsIntersectExceptAll) {
    814. +
    815. 1 throw new QueryError("EXCEPT ALL not supported");
    816. +
    817. }
    818. +
    819. 15 return this.compoundClone("except", dataset, opts);
    820. },
    821. /**
    822. -
    823. * Returnes a cloned dataset with the IS NOT TRUE boolean expression added to the WHERE
    824. -
    825. * clause.
    826. +
    827. * Performs the inverse of {@link patio.Dataset#filter}. Note that if you have multiple filter
    828. +
    829. * conditions, this is not the same as a negation of all conditions. For argument types see
    830. +
    831. * {@link patio.Dataset#filter}
    832. *
    833. * @example
    834. *
    835. -
    836. * DB.from("test").isNotTrue("boolFlag");
    837. -
    838. * => SELECT * FROM test WHERE (boolFlag IS NOT TRUE);
    839. -
    840. * DB.from("test").isNotTrue("boolFlag", "otherFlag");
    841. -
    842. * => SELECT * FROM test WHERE (boolFlag IS NOT TRUE AND otherFlag IS NOT TRUE);
    843. -
    844. *
    845. -
    846. * @param {String...} arr variable number of arguments to create an IS NOT TRUE expression for.
    847. -
    848. *
    849. -
    850. * @return {patio.Dataset} a cloned dataset with the IS NOT TRUE expression added to the WHERE clause.
    851. +
    852. * DB.from("items").exclude({category : "software").sql;
    853. +
    854. * //=> SELECT * FROM items WHERE (category != 'software')
    855. *
    856. +
    857. * DB.from("items").exclude({category : 'software', id : 3}).sql;
    858. +
    859. * //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
    860. +
    861. * @return {patio.Dataset} a cloned dataset with the excluded conditions applied to the HAVING/WHERE clause.
    862. */
    863. -
    864. isNotTrue: function (arr) {
    865. -
    866. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);
    867. -
    868. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    869. +
    870. exclude: function () {
    871. +
    872. 69 var cond = argsToArray(arguments), tOpts = this.__opts;
    873. +
    874. 69 var clause = (tOpts["having"] ? "having" : "where"), clauseObj = tOpts[clause];
    875. +
    876. 69 cond = cond.length > 1 ? cond : cond[0];
    877. +
    878. 69 cond = this._filterExpr.call(this, cond);
    879. +
    880. 69 cond = BooleanExpression.invert(cond);
    881. +
    882. 69 if (clauseObj) {
    883. +
    884. 58 cond = new BooleanExpression("AND", clauseObj, cond)
    885. +
    886. }
    887. +
    888. 69 var opts = {};
    889. +
    890. 69 opts[clause] = cond;
    891. +
    892. 69 return this.mergeOptions(opts);
    893. },
    894. /**
    895. -
    896. * Returnes a cloned dataset with the IS TRUE boolean expression added to the WHERE
    897. -
    898. * clause.
    899. +
    900. * Returns a copy of the dataset with the given conditions applied to it.
    901. +
    902. * If the query already has a HAVING clause, then the conditions are applied to the
    903. +
    904. * HAVING clause otherwise they are applied to the WHERE clause.
    905. *
    906. * @example
    907. *
    908. -
    909. * DB.from("test").isTrue("boolFlag");
    910. -
    911. * => SELECT * FROM test WHERE (boolFlag IS TRUE);
    912. -
    913. * DB.from("test").isTrue("boolFlag", "otherFlag");
    914. -
    915. * => SELECT * FROM test WHERE (boolFlag IS TRUE AND otherFlag IS TRUE);
    916. +
    917. * DB.from("items").filter({id : 3}).sql;
    918. +
    919. * //=> SELECT * FROM items WHERE (id = 3)
    920. *
    921. -
    922. * @param {String...} arr variable number of arguments to create an IS TRUE expression for.
    923. +
    924. * DB.from("items").filter('price < ?', 100)
    925. +
    926. * //=> SELECT * FROM items WHERE price < 100
    927. *
    928. -
    929. * @return {patio.Dataset} a cloned dataset with the IS TRUE expression added to the WHERE clause.
    930. +
    931. * DB.from("items").filter({id, [1,2,3]}).filter({id : {between : [0, 10]}}).sql;
    932. +
    933. * //=> SELECT
    934. +
    935. * *
    936. +
    937. * FROM
    938. +
    939. * items
    940. +
    941. * WHERE
    942. +
    943. * ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
    944. *
    945. -
    946. */
    947. -
    948. isTrue: function (arr) {
    949. -
    950. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);
    951. -
    952. 2 return this.filter(this.__createBoolExpression("is", arr));
    953. -
    954. },
    955. -
    956. -
    957. /**
    958. -
    959. * Returnes a cloned dataset with the IS NOT FALSE boolean expression added to the WHERE
    960. -
    961. * clause.
    962. +
    963. * DB.from("items").filter('price < 100');
    964. +
    965. * //=> SELECT * FROM items WHERE price < 100
    966. *
    967. -
    968. * @example
    969. +
    970. * DB.from("items").filter("active").sql;
    971. +
    972. * //=> SELECT * FROM items WHERE active
    973. *
    974. -
    975. * DB.from("test").isNotFalse("boolFlag");
    976. -
    977. * => SELECT * FROM test WHERE (boolFlag IS NOT FALSE);
    978. -
    979. * DB.from("test").isNotFalse("boolFlag", "otherFlag");
    980. -
    981. * => SELECT * FROM test WHERE (boolFlag IS NOT FALSE AND otherFlag IS NOT FALSE);
    982. -
    983. * @param {String...} arr variable number of arguments to create an IS NOT FALSE expression for.
    984. +
    985. * DB.from("items").filter(function(){
    986. +
    987. * return this.price.lt(100);
    988. +
    989. * });
    990. +
    991. * //=> SELECT * FROM items WHERE (price < 100)
    992. *
    993. -
    994. * @return {patio.Dataset} a cloned dataset with the IS NOT FALSE expression added to the WHERE clause.
    995. +
    996. * //Multiple filter calls can be chained for scoping:
    997. +
    998. * DB.from("items").filter(:category => 'software').filter{price < 100}
    999. +
    1000. * //=> SELECT * FROM items WHERE ((category = 'software') AND (price < 100))
    1001. *
    1002. -
    1003. */
    1004. -
    1005. isNotFalse: function (arr) {
    1006. -
    1007. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);
    1008. -
    1009. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    1010. -
    1011. },
    1012. -
    1013. -
    1014. /**
    1015. -
    1016. * Returnes a cloned dataset with the IS FALSE boolean expression added to the WHERE
    1017. -
    1018. * clause.
    1019. *
    1020. -
    1021. * @example
    1022. +
    1023. * @param {Object|Array|String|patio.sql.Identifier|patio.sql.BooleanExpression} args filters to apply to the
    1024. +
    1025. * WHERE/HAVING clause. Description of each:
    1026. +
    1027. * <ul>
    1028. +
    1029. * <li>Hash - list of equality/inclusion expressions</li>
    1030. +
    1031. * <li>Array - depends:
    1032. +
    1033. * <ul>
    1034. +
    1035. * <li>If first member is a string, assumes the rest of the arguments
    1036. +
    1037. * are parameters and interpolates them into the string.</li>
    1038. +
    1039. * <li>If all members are arrays of length two, treats the same way
    1040. +
    1041. * as a hash, except it allows for duplicate keys to be
    1042. +
    1043. * specified.</li>
    1044. +
    1045. * <li>Otherwise, treats each argument as a separate condition.</li>
    1046. +
    1047. * </ul>
    1048. +
    1049. * </li>
    1050. +
    1051. * <li>String - taken literally</li>
    1052. +
    1053. * <li>{@link patio.sql.Identifier} - taken as a boolean column argument (e.g. WHERE active)</li>
    1054. +
    1055. * <li>{@link patio.sql.BooleanExpression} - an existing condition expression,
    1056. +
    1057. * probably created using the patio.sql methods.
    1058. +
    1059. * </li>
    1060. *
    1061. -
    1062. * DB.from("test").isFalse("boolFlag");
    1063. -
    1064. * => SELECT * FROM test WHERE (boolFlag IS FALSE);
    1065. -
    1066. * DB.from("test").isFalse("boolFlag", "otherFlag");
    1067. -
    1068. * => SELECT * FROM test WHERE (boolFlag IS FALSE AND otherFlag IS FALSE);
    1069. -
    1070. * @param {String...} arr variable number of arguments to create an IS FALSE expression for.
    1071. +
    1072. * @param {Function} [cb] filter also takes a cb, which should return one of the above argument
    1073. +
    1074. * types, and is treated the same way. This block is called with an {@link patio.sql} object which can be used to dynaically create expression. For more details
    1075. +
    1076. * on the sql object see {@link patio.sql}
    1077. *
    1078. -
    1079. * @return {patio.Dataset} a cloned dataset with the IS FALSE expression added to the WHERE clause.
    1080. +
    1081. * <p>
    1082. +
    1083. * <b>NOTE:</b>If both a cb and regular arguments are provided, they get ANDed together.
    1084. +
    1085. * </p>
    1086. *
    1087. -
    1088. */
    1089. -
    1090. isFalse: function (arr) {
    1091. -
    1092. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);
    1093. -
    1094. 2 return this.filter(this.__createBoolExpression("is", arr));
    1095. +
    1096. * @return {patio.Dataset} a cloned dataset with the filter arumgents applied to the WHERE/HAVING clause.
    1097. +
    1098. **/
    1099. +
    1100. filter: function (args, cb) {
    1101. +
    1102. 3468 args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));
    1103. +
    1104. 3468 return this._filter.apply(this, args);
    1105. },
    1106. /**
    1107. -
    1108. *
    1109. -
    1110. * Returns a cloned dataset with a greater than or equal to expression added to the WHERE
    1111. -
    1112. * clause.
    1113. -
    1114. *
    1115. -
    1116. * @example
    1117. -
    1118. * DB.from("test").gte({x : 1});
    1119. -
    1120. * //=> SELECT * FROM test WHERE (x >= 1)
    1121. -
    1122. * DB.from("test").gte({x : 1, y : 10});
    1123. -
    1124. * //=> SELECT * FROM test WHERE ((x >= 1) AND (y >= 10))
    1125. -
    1126. *
    1127. -
    1128. * @param {Object} obj object used to create the greater than or equal to expression.
    1129. -
    1130. *
    1131. -
    1132. * @return {patio.Dataset} a cloned dataset with the greater than or equal to expression added to the WHERE clause.
    1133. +
    1134. * @see patio.Dataset#filter
    1135. */
    1136. -
    1137. gte: function (arr) {
    1138. -
    1139. 2 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "gte");
    1140. -
    1141. 2 return this.filter(this.__createBoolExpression("gte", arr));
    1142. +
    1143. find: function () {
    1144. +
    1145. 30 var args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));
    1146. +
    1147. 30 return this._filter.apply(this, args);
    1148. },
    1149. /**
    1150. -
    1151. *
    1152. -
    1153. * Returns a cloned dataset with a less than or equal to expression added to the WHERE
    1154. -
    1155. * clause.
    1156. -
    1157. *
    1158. * @example
    1159. -
    1160. * DB.from("test").gte({x : 1});
    1161. -
    1162. * //=> SELECT * FROM test WHERE (x <= 1)
    1163. -
    1164. * DB.from("test").gte({x : 1, y : 10});
    1165. -
    1166. * //=> SELECT * FROM test WHERE ((x <= 1) AND (y <= 10))
    1167. -
    1168. *
    1169. -
    1170. * @param {Object} obj object used to create the less than or equal to expression.
    1171. -
    1172. *
    1173. -
    1174. * @return {patio.Dataset} a cloned dataset with the less than or equal to expression added to the WHERE clause.
    1175. +
    1176. * DB.from("table").forUpdate()
    1177. +
    1178. * //=> SELECT * FROM table FOR UPDATE
    1179. +
    1180. * @return {patio.Dataset} a cloned dataset with a "update" lock style.
    1181. */
    1182. -
    1183. lte: function (obj) {
    1184. -
    1185. 2 var arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "lte");
    1186. -
    1187. 2 return this.filter(this.__createBoolExpression("lte", obj));
    1188. +
    1189. forUpdate: function () {
    1190. +
    1191. 1 return this.lockStyle("update");
    1192. },
    1193. /**
    1194. -
    1195. * Returns a cloned dataset with a between clause added
    1196. -
    1197. * to the where clause.
    1198. +
    1199. * Returns a copy of the dataset with the source changed. If no
    1200. +
    1201. * source is given, removes all tables. If multiple sources
    1202. +
    1203. * are given, it is the same as using a CROSS JOIN (cartesian product) between all tables.
    1204. *
    1205. * @example
    1206. -
    1207. * ds.notBetween({x:[1, 2]}).sql;
    1208. -
    1209. * //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))
    1210. +
    1211. * var dataset = DB.from("items");
    1212. *
    1213. -
    1214. * ds.find({x:{notBetween:[1, 2]}}).sql;
    1215. -
    1216. * //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))
    1217. -
    1218. * @param {Object} obj object where the key is the column and the value is an array where the first element
    1219. -
    1220. * is the item to be greater than or equal to than and the second item is less than or equal to than.
    1221. +
    1222. * dataset.from().sql;
    1223. +
    1224. * //=> SELECT *
    1225. *
    1226. -
    1227. * @return {patio.Dataset} a cloned dataset with a between clause added
    1228. -
    1229. * to the where clause.
    1230. -
    1231. */
    1232. -
    1233. between: function (obj) {
    1234. -
    1235. 2 return this.filter(this.__createBetweenExpression(obj));
    1236. -
    1237. },
    1238. -
    1239. -
    1240. /**
    1241. -
    1242. * Returns a cloned dataset with a not between clause added
    1243. -
    1244. * to the where clause.
    1245. +
    1246. * dataset.from("blah").sql
    1247. +
    1248. * //=> SELECT * FROM blah
    1249. *
    1250. -
    1251. * @example
    1252. -
    1253. * ds.notBetween({x:[1, 2]}).sql;
    1254. -
    1255. * //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))
    1256. +
    1257. * dataset.from("blah", "foo")
    1258. +
    1259. * //=> SELECT * FROM blah, foo
    1260. *
    1261. -
    1262. * ds.find({x:{notBetween:[1, 2]}}).sql;
    1263. -
    1264. * //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))
    1265. -
    1266. * @param {Object} obj object where the key is the column and the value is an array where the first element
    1267. -
    1268. * is the item to be less than and the second item is greater than.
    1269. +
    1270. * dataset.from({a:"b"}).sql;
    1271. +
    1272. * //=> SELECT * FROM a AS b
    1273. *
    1274. -
    1275. * @return {patio.Dataset} a cloned dataset with a not between clause added
    1276. -
    1277. * to the where clause.
    1278. +
    1279. * dataset.from(dataset.from("a").group("b").as("c")).sql;
    1280. +
    1281. * //=> "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"
    1282. +
    1283. *
    1284. +
    1285. * @param {...String|...patio.sql.Identifier|...patio.Dataset|...Object} [source] tables to select from
    1286. +
    1287. *
    1288. +
    1289. * @return {patio.Dataset} a cloned dataset with the FROM clause overridden.
    1290. */
    1291. -
    1292. notBetween: function (obj) {
    1293. -
    1294. 2 return this.filter(this.__createBetweenExpression(obj, true));
    1295. +
    1296. from: function (source) {
    1297. +
    1298. 821 source = argsToArray(arguments);
    1299. +
    1300. 821 var tableAliasNum = 0, sources = [];
    1301. +
    1302. 821 source.forEach(function (s) {
    1303. +
    1304. 1003 if (isInstanceOf(s, Dataset)) {
    1305. +
    1306. 86 sources.push(new AliasedExpression(s, this._datasetAlias(++tableAliasNum)));
    1307. +
    1308. 917 } else if (isHash(s)) {
    1309. +
    1310. 3 for (var i in s) {
    1311. +
    1312. 3 sources.push(new AliasedExpression(new Identifier(i), s[i]));
    1313. +
    1314. }
    1315. +
    1316. 914 } else if (isString(s)) {
    1317. +
    1318. 889 sources.push(this.stringToIdentifier(s))
    1319. +
    1320. } else {
    1321. +
    1322. 25 sources.push(s);
    1323. +
    1324. }
    1325. +
    1326. }, this);
    1327. +
    1328. +
    1329. 821 var o = {from: sources.length ? sources : null}
    1330. +
    1331. 821 if (tableAliasNum) {
    1332. +
    1333. 84 o.numDatasetSources = tableAliasNum;
    1334. +
    1335. }
    1336. +
    1337. 821 return this.mergeOptions(o)
    1338. },
    1339. /**
    1340. -
    1341. * Returns a cloned dataset with the given lock style. If style is a
    1342. -
    1343. * string, it will be used directly.Currently "update" is respected
    1344. -
    1345. * by most databases, and "share" is supported by some.
    1346. +
    1347. * Returns a dataset selecting from the current dataset.
    1348. +
    1349. * Supplying the alias option controls the alias of the result.
    1350. *
    1351. * @example
    1352. -
    1353. * DB.from("items").lockStyle('FOR SHARE') # SELECT * FROM items FOR SHARE
    1354. *
    1355. -
    1356. * @param {String} style the lock style to use.
    1357. +
    1358. * ds = DB.from("items").order("name").select("id", "name")
    1359. +
    1360. * //=> SELECT id,name FROM items ORDER BY name
    1361. *
    1362. -
    1363. * @return {patio.Dataset} a cloned datase with the given lock style.
    1364. -
    1365. **/
    1366. -
    1367. lockStyle: function (style) {
    1368. -
    1369. 4 return this.mergeOptions({lock: style});
    1370. -
    1371. },
    1372. -
    1373. -
    1374. /**
    1375. -
    1376. * Returns a copy of the dataset with the order changed. If the dataset has an
    1377. -
    1378. * existing order, it is ignored and overwritten with this order. If null is given
    1379. -
    1380. * the returned dataset has no order. This can accept multiple arguments
    1381. -
    1382. * of varying kinds, such as SQL functions. This also takes a function similar
    1383. -
    1384. * to {@link patio.Dataset#filter}
    1385. +
    1386. * ds.fromSelf().sql;
    1387. +
    1388. * //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1
    1389. *
    1390. -
    1391. * @example
    1392. +
    1393. * ds.fromSelf({alias : "foo"}).sql;
    1394. +
    1395. * //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo
    1396. *
    1397. -
    1398. * DB.from("items").order("name")
    1399. -
    1400. * //=> SELECT * FROM items ORDER BY name
    1401. +
    1402. * @param {Object} [opts] options
    1403. +
    1404. * @param {String|patio.sql.Identifier} [opts.alias] alias to use
    1405. *
    1406. -
    1407. * DB.from("items").order("a", "b")
    1408. -
    1409. * //=> SELECT * FROM items ORDER BY a, b
    1410. +
    1411. * @return {patio.Dataset} a cloned dataset with the FROM clause set as the current dataset.
    1412. +
    1413. */
    1414. +
    1415. fromSelf: function (opts) {
    1416. +
    1417. 84 opts = isUndefined(opts) ? {} : opts;
    1418. +
    1419. 84 var fs = {};
    1420. +
    1421. 84 var nonSqlOptions = this._static.NON_SQL_OPTIONS;
    1422. +
    1423. 84 Object.keys(this.__opts).forEach(function (k) {
    1424. +
    1425. 302 if (nonSqlOptions.indexOf(k) == -1) {
    1426. +
    1427. 302 fs[k] = null;
    1428. +
    1429. }
    1430. +
    1431. });
    1432. +
    1433. 84 return this.mergeOptions(fs).from(opts["alias"] ? this.as(opts["alias"]) : this);
    1434. +
    1435. },
    1436. +
    1437. +
    1438. /**
    1439. +
    1440. * Match any of the columns to any of the patterns. The terms can be
    1441. +
    1442. * strings (which use LIKE) or regular expressions (which are only
    1443. +
    1444. * supported on MySQL and PostgreSQL). Note that the total number of
    1445. +
    1446. * pattern matches will be columns[].length * terms[].length,
    1447. +
    1448. * which could cause performance issues.
    1449. *
    1450. -
    1451. * DB.from("items").order(sql.literal('a + b'))
    1452. -
    1453. * //=> SELECT * FROM items ORDER BY a + b
    1454. +
    1455. * @example
    1456. *
    1457. -
    1458. * DB.from("items").order(sql.identifier("a").plus("b"))
    1459. -
    1460. * //=> SELECT * FROM items ORDER BY (a + b)
    1461. +
    1462. * DB.from("items").grep("a", "%test%").sql;
    1463. +
    1464. * //=> SELECT * FROM items WHERE (a LIKE '%test%');
    1465. *
    1466. -
    1467. * DB.from("items").order(sql.identifier("name").desc())
    1468. -
    1469. * //=> SELECT * FROM items ORDER BY name DESC
    1470. +
    1471. * DB.from("items").grep(["a", "b"], ["%test%" "foo"]).sql;
    1472. +
    1473. * //=> SELECT * FROM items WHERE ((a LIKE '%test%') OR (a LIKE 'foo') OR (b LIKE '%test%') OR (b LIKE 'foo'))
    1474. *
    1475. -
    1476. * DB.from("items").order(sql.identifier("name").asc({nulls : "last"))
    1477. -
    1478. * //=> SELECT * FROM items ORDER BY name ASC NULLS LAST
    1479. +
    1480. * DB.from("items").grep(['a', 'b'], ["%foo%", "%bar%"], {allPatterns : true}).sql;
    1481. +
    1482. * //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (b LIKE '%foo%')) AND ((a LIKE '%bar%') OR (b LIKE '%bar%')))
    1483. *
    1484. -
    1485. * DB.from("items").order(function(){
    1486. -
    1487. * return this.sum("name").desc();
    1488. -
    1489. * }); //=> SELECT * FROM items ORDER BY sum(name) DESC
    1490. +
    1491. * DB.from("items").grep(["a", "b"], ['%foo%", "%bar%", {allColumns : true})sql;
    1492. +
    1493. * //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (a LIKE '%bar%')) AND ((b LIKE '%foo%') OR (b LIKE '%bar%')))
    1494. *
    1495. -
    1496. * DB.from("items").order(null)
    1497. -
    1498. * //=>SELECT * FROM items
    1499. -
    1500. * @param arg variable number of arguments similar to {@link patio.Dataset#filter}
    1501. +
    1502. * DB.from("items").grep(["a", "b"], ["%foo%", "%bar%"], {allPatterns : true, allColumns : true}).sql;
    1503. +
    1504. * //=> SELECT * FROM a WHERE ((a LIKE '%foo%') AND (b LIKE '%foo%') AND (a LIKE '%bar%') AND (b LIKE '%bar%'))
    1505. *
    1506. -
    1507. * @return {patio.Dataset} a cloned dataset with the order changed.
    1508. -
    1509. * */
    1510. -
    1511. order: function (args) {
    1512. -
    1513. 381 args = argsToArray(arguments);
    1514. -
    1515. 381 var order = [];
    1516. -
    1517. 381 args = compact(args).length ? args : null;
    1518. -
    1519. 381 if (args) {
    1520. -
    1521. 263 args.forEach(function (a) {
    1522. -
    1523. 326 if (isString(a)) {
    1524. -
    1525. 204 order.push(this.stringToIdentifier(a));
    1526. -
    1527. 122 } else if (isFunction(a)) {
    1528. -
    1529. 16 var res = a.apply(sql, [sql]);
    1530. -
    1531. 16 order = order.concat(isArray(res) ? res : [res]);
    1532. -
    1533. } else {
    1534. -
    1535. 106 order.push(a);
    1536. -
    1537. }
    1538. -
    1539. }, this);
    1540. +
    1541. * @param {String[]|patio.sql.Identifier[]} columns columns to search
    1542. +
    1543. * @param {String|RegExp} patterns patters to search with
    1544. +
    1545. * @param {Object} [opts] options to use when searching. NOTE If both allColumns and allPatterns are true, all columns must match all patterns
    1546. +
    1547. * @param {Boolean} [opts.allColumns] All columns must be matched to any of the given patterns.
    1548. +
    1549. * @param {Boolean} [opts.allPatterns] All patterns must match at least one of the columns.
    1550. +
    1551. * @param {Boolean} [opts.caseInsensitive] Use a case insensitive pattern match (the default is
    1552. +
    1553. * case sensitive if the database supports it).
    1554. +
    1555. * @return {patio.Dataset} a dataset with the LIKE clauses added
    1556. +
    1557. */
    1558. +
    1559. +
    1560. grep: function (columns, patterns, opts) {
    1561. +
    1562. 15 opts = isUndefined(opts) ? {} : opts;
    1563. +
    1564. 15 var conds;
    1565. +
    1566. 15 if (opts.hasOwnProperty("allPatterns")) {
    1567. +
    1568. 4 conds = array.toArray(patterns).map(function (pat) {
    1569. +
    1570. 8 return BooleanExpression.fromArgs(
    1571. +
    1572. [(opts.allColumns ? "AND" : "OR")]
    1573. +
    1574. .concat(array.toArray(columns)
    1575. +
    1576. .map(function (c) {
    1577. +
    1578. 16 return StringExpression.like(c, pat, opts);
    1579. +
    1580. })));
    1581. +
    1582. });
    1583. +
    1584. 4 return this.filter(BooleanExpression.fromArgs([opts.allPatterns ? "AND" : "OR"].concat(conds)));
    1585. } else {
    1586. -
    1587. 118 order = null;
    1588. +
    1589. 11 conds = array.toArray(columns)
    1590. +
    1591. .map(function (c) {
    1592. +
    1593. 16 return BooleanExpression.fromArgs(["OR"].concat(array.toArray(patterns).map(function (pat) {
    1594. +
    1595. 26 return StringExpression.like(c, pat, opts);
    1596. +
    1597. })));
    1598. +
    1599. });
    1600. +
    1601. 11 return this.filter(BooleanExpression.fromArgs([opts.allColumns ? "AND" : "OR"].concat(conds)));
    1602. }
    1603. -
    1604. 381 return this.mergeOptions({order: order});
    1605. },
    1606. /**
    1607. -
    1608. * Alias of {@link patio.Dataset#orderMore};
    1609. +
    1610. * @see patio.Dataset#grep
    1611. */
    1612. -
    1613. orderAppend: function () {
    1614. -
    1615. 4 return this.orderMore.apply(this, arguments);
    1616. +
    1617. like: function () {
    1618. +
    1619. 1 return this.grep.apply(this, arguments);
    1620. },
    1621. -
    1622. /**
    1623. -
    1624. * @see patio.Dataset#order
    1625. -
    1626. */
    1627. -
    1628. orderBy: function () {
    1629. -
    1630. 6 return this.order.apply(this, arguments);
    1631. -
    1632. },
    1633. /**
    1634. -
    1635. * Returns a copy of the dataset with the order columns added
    1636. -
    1637. * to the end of the existing order. For more detail
    1638. -
    1639. * @see patio.Dataset#order
    1640. -
    1641. *
    1642. +
    1643. * Returns a copy of the dataset with the results grouped by the value of
    1644. +
    1645. * the given columns.
    1646. * @example
    1647. +
    1648. * DB.from("items").group("id")
    1649. +
    1650. * //=>SELECT * FROM items GROUP BY id
    1651. +
    1652. * DB.from("items").group("id", "name")
    1653. +
    1654. * //=> SELECT * FROM items GROUP BY id, name
    1655. +
    1656. * @param {String...|patio.sql.Identifier...} columns columns to group by.
    1657. *
    1658. -
    1659. * DB.from("items").order("a").order("b");
    1660. -
    1661. * //=> SELECT * FROM items ORDER BY b
    1662. -
    1663. *
    1664. -
    1665. * DB.from("items").order("a").orderMore("b");
    1666. -
    1667. * //=>SELECT * FROM items ORDER BY a, b
    1668. -
    1669. */
    1670. -
    1671. orderMore: function () {
    1672. -
    1673. 11 var args = argsToArray(arguments);
    1674. -
    1675. 11 if (this.__opts.order) {
    1676. -
    1677. 9 args = this.__opts.order.concat(args);
    1678. -
    1679. }
    1680. -
    1681. 11 return this.order.apply(this, args);
    1682. +
    1683. * @return {patio.Dataset} a cloned dataset with the GROUP BY clause added.
    1684. +
    1685. **/
    1686. +
    1687. group: function (columns) {
    1688. +
    1689. 32 columns = argsToArray(arguments);
    1690. +
    1691. 32 var stringToIdentifier = this.stringToIdentifier.bind(this)
    1692. +
    1693. 32 return this.mergeOptions({group: (array.compact(columns).length == 0 ? null : columns.map(function (c) {
    1694. +
    1695. 32 return isString(c) ? stringToIdentifier(c) : c;
    1696. +
    1697. }))});
    1698. },
    1699. /**
    1700. -
    1701. * Returns a copy of the dataset with the order columns added
    1702. -
    1703. * to the beginning of the existing order. For more detail
    1704. -
    1705. * @see patio.Dataset#order
    1706. -
    1707. *
    1708. -
    1709. * @example
    1710. -
    1711. * DB.from("items").order("a").order("b");
    1712. -
    1713. * //=> SELECT * FROM items ORDER BY b
    1714. -
    1715. *
    1716. -
    1717. * DB.from("items").order("a").orderPrepend("b");
    1718. -
    1719. * //=>SELECT * FROM items ORDER BY b, a
    1720. -
    1721. *
    1722. -
    1723. *
    1724. -
    1725. **/
    1726. -
    1727. orderPrepend: function () {
    1728. -
    1729. 4 var ds = this.order.apply(this, arguments);
    1730. -
    1731. 4 return this.__opts.order ? ds.orderMore.apply(ds, this.__opts.order) : ds;
    1732. +
    1733. * @see patio.Dataset#group
    1734. +
    1735. */
    1736. +
    1737. groupBy: function () {
    1738. +
    1739. 10 return this.group.apply(this, arguments);
    1740. },
    1741. +
    1742. /**
    1743. -
    1744. * Qualify to the given table, or {@link patio.Dataset#firstSourceAlias} if not table is given.
    1745. +
    1746. * Returns a dataset grouped by the given column with count by group.
    1747. +
    1748. * Column aliases may be supplied, and will be included in the select clause.
    1749. *
    1750. * @example
    1751. -
    1752. * DB.from("items").filter({id : 1}).qualify();
    1753. -
    1754. * //=> SELECT items.* FROM items WHERE (items.id = 1)
    1755. *
    1756. -
    1757. * DB.from("items").filter({id : 1}).qualify("i");
    1758. -
    1759. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    1760. -
    1761. *
    1762. -
    1763. * @param {String|patio.sql.Identifier} [table={@link patio.Dataset#firstSourceAlias}] the table name to qualify to.
    1764. -
    1765. *
    1766. -
    1767. * @return {patio.Dataset} a cloned dataset qualified to the table or {@link patio.Dataset#firstSourceAlias}
    1768. -
    1769. **/
    1770. -
    1771. qualify: function (table) {
    1772. -
    1773. 19 table = table || this.firstSourceAlias;
    1774. -
    1775. 19 return this.qualifyTo(table);
    1776. -
    1777. },
    1778. -
    1779. -
    1780. /**
    1781. -
    1782. * Qualify the dataset to its current first source(first from clause). This is useful
    1783. -
    1784. * if you have unqualified identifiers in the query that all refer to
    1785. -
    1786. * the first source, and you want to join to another table which
    1787. -
    1788. * has columns with the same name as columns in the current dataset.
    1789. -
    1790. * See {@link patio.Dataset#qualifyTo}
    1791. +
    1792. * DB.from("items").groupAndCount("name").all()
    1793. +
    1794. * //=> SELECT name, count(*) AS count FROM items GROUP BY name
    1795. +
    1796. * //=> [{name : 'a', count : 1}, ...]
    1797. *
    1798. -
    1799. * @example
    1800. +
    1801. * DB.from("items").groupAndCount("first_name", "last_name").all()
    1802. +
    1803. * //SELECT first_name, last_name, count(*) AS count FROM items GROUP BY first_name, last_name
    1804. +
    1805. * //=> [{first_name : 'a', last_name : 'b', count : 1}, ...]
    1806. *
    1807. -
    1808. * DB.from("items").filter({id : 1}).qualifyToFirstSource();
    1809. -
    1810. * //=> SELECT items.* FROM items WHERE (items.id = 1)
    1811. +
    1812. * DB.from("items").groupAndCount("first_name___name").all()
    1813. +
    1814. * //=> SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
    1815. +
    1816. * //=> [{name : 'a', count:1}, ...]
    1817. +
    1818. * @param {String...|patio.sql.Identifier...} columns columns to croup and count on.
    1819. *
    1820. -
    1821. * @return {patio.Dataset} a cloned dataset that is qualified with the first source.
    1822. -
    1823. * */
    1824. -
    1825. qualifyToFirstSource: function () {
    1826. -
    1827. 18 return this.qualifyTo(this.firstSourceAlias);
    1828. +
    1829. * @return {patio.Dataset} a cloned dataset with the GROUP clause and count added.
    1830. +
    1831. */
    1832. +
    1833. groupAndCount: function (columns) {
    1834. +
    1835. 8 columns = argsToArray(arguments);
    1836. +
    1837. 8 var group = this.group.apply(this, columns.map(function (c) {
    1838. +
    1839. 9 return this._unaliasedIdentifier(c);
    1840. +
    1841. }, this));
    1842. +
    1843. 8 return group.select.apply(group, columns.concat([this._static.COUNT_OF_ALL_AS_COUNT]));
    1844. +
    1845. },
    1846. /**
    1847. -
    1848. * Return a copy of the dataset with unqualified identifiers in the
    1849. -
    1850. * SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the
    1851. -
    1852. * given table. If no columns are currently selected, select all
    1853. -
    1854. * columns of the given table.
    1855. +
    1856. * Returns a copy of the dataset with the HAVING conditions changed. See {@link patio.Dataset#filter} for argument types.
    1857. *
    1858. * @example
    1859. -
    1860. * DB.from("items").filter({id : 1}).qualifyTo("i");
    1861. -
    1862. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    1863. -
    1864. *
    1865. -
    1866. * @param {String} table the name to qualify identifier to.
    1867. +
    1868. * DB.from("items").group("sum").having({sum : 10}).sql;
    1869. +
    1870. * //=> SELECT * FROM items GROUP BY sum HAVING (sum = 10)
    1871. *
    1872. -
    1873. * @return {patio.Dataset} a cloned dataset with unqualified identifiers qualified.
    1874. -
    1875. */
    1876. -
    1877. qualifyTo: function (table) {
    1878. -
    1879. 40 var o = this.__opts;
    1880. -
    1881. 40 if (o.sql) {
    1882. -
    1883. 2 return this.mergeOptions();
    1884. -
    1885. }
    1886. -
    1887. 38 var h = {};
    1888. -
    1889. 38 array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) {
    1890. -
    1891. 65 h[k] = this._qualifiedExpression(o[k], table);
    1892. +
    1893. * @return {patio.Dataset} a cloned dataset with HAVING clause changed or added.
    1894. +
    1895. **/
    1896. +
    1897. having: function () {
    1898. +
    1899. 12 var cond = argsToArray(arguments).map(function (s) {
    1900. +
    1901. 12 return isString(s) && s !== '' ? this.stringToIdentifier(s) : s
    1902. }, this);
    1903. -
    1904. 38 if (!o.select || isEmpty(o.select)) {
    1905. -
    1906. 14 h.select = [new ColumnAll(table)];
    1907. -
    1908. }
    1909. -
    1910. 38 return this.mergeOptions(h);
    1911. +
    1912. 12 return this._filter.apply(this, ["having"].concat(cond));
    1913. },
    1914. /**
    1915. -
    1916. * Same as {@link patio.Dataset@qualifyTo} except that it forces qualification on methods called
    1917. -
    1918. * after it has been called.
    1919. +
    1920. * Adds an INTERSECT clause using a second dataset object.
    1921. +
    1922. * An INTERSECT compound dataset returns all rows in both the current dataset
    1923. +
    1924. * and the given dataset.
    1925. *
    1926. * @example
    1927. *
    1928. -
    1929. * //qualfyTo would generate
    1930. -
    1931. * DB.from("items").qualifyTo("i").filter({id : 1});
    1932. -
    1933. * //=> SELECT i.* FROM items WHERE (id = 1)
    1934. +
    1935. * DB.from("items").intersect(DB.from("other_items")).sql;
    1936. +
    1937. * //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS t1
    1938. *
    1939. -
    1940. * //alwaysQualify qualifies filter also.
    1941. -
    1942. * DB.from("items").alwaysQualify("i").filter({id : 1});
    1943. -
    1944. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    1945. +
    1946. * DB.from("items").intersect(DB.from("other_items"), {all : true, fromSelf : false}).sql;
    1947. +
    1948. * //=> SELECT * FROM items INTERSECT ALL SELECT * FROM other_items
    1949. *
    1950. +
    1951. * DB.from("items").intersect(DB.from("other_items"), {alias : "i"}).sql;
    1952. +
    1953. * //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS i
    1954. *
    1955. -
    1956. * @param {String|patio.sql.Identifier} [table=this.firstSourceAlias] the table to qualify to.
    1957. -
    1958. * @return {patio.Dataset} a cloned dataset that will always qualify.
    1959. -
    1960. */
    1961. -
    1962. alwaysQualify: function (table) {
    1963. -
    1964. 3 return this.mergeOptions({alwaysQualify: table || this.firstSourceAlias});
    1965. +
    1966. * @throws {patio.QueryError} if the operation is not supported.
    1967. +
    1968. * @param {patio.Dataset} dataset the dataset to intersect
    1969. +
    1970. * @param {Object} [opts] options
    1971. +
    1972. * @param {String|patio.sql.Identifier} [opts.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias
    1973. +
    1974. * @param {Boolean} [opts.all] Set to true to use INTERSECT ALL instead of INTERSECT, so duplicate rows can occur
    1975. +
    1976. * @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}.
    1977. +
    1978. *
    1979. +
    1980. * @return {patio.Dataset} a cloned dataset with the INTERSECT clause.
    1981. +
    1982. **/
    1983. +
    1984. intersect: function (dataset, opts) {
    1985. +
    1986. 18 opts = isUndefined(opts) ? {} : opts;
    1987. +
    1988. 18 if (!isHash(opts)) {
    1989. +
    1990. 5 opts = {all: opts};
    1991. +
    1992. }
    1993. +
    1994. 18 if (!this.supportsIntersectExcept) {
    1995. +
    1996. 2 throw new QueryError("INTERSECT not supported");
    1997. +
    1998. 16 } else if (opts.all && !this.supportsIntersectExceptAll) {
    1999. +
    2000. 1 throw new QueryError("INTERSECT ALL not supported");
    2001. +
    2002. }
    2003. +
    2004. 15 return this.compoundClone("intersect", dataset, opts);
    2005. },
    2006. -
    2007. /**
    2008. -
    2009. * Returns a copy of the dataset with the order reversed. If no order is
    2010. -
    2011. * given, the existing order is inverted.
    2012. +
    2013. * Inverts the current filter.
    2014. *
    2015. * @example
    2016. -
    2017. * DB.from("items").reverse("id");
    2018. -
    2019. * //=> SELECT * FROM items ORDER BY id DESC
    2020. -
    2021. *
    2022. -
    2023. * DB.from("items").order("id").reverse();
    2024. -
    2025. * //=> SELECT * FROM items ORDER BY id DESC
    2026. -
    2027. *
    2028. -
    2029. * DB.from("items").order("id").reverse(sql.identifier("name").asc);
    2030. -
    2031. * //=> SELECT * FROM items ORDER BY name ASC
    2032. -
    2033. *
    2034. -
    2035. * @param {String|patio.sql.Identifier|Function} args variable number of columns add to order before reversing.
    2036. +
    2037. * DB.from("items").filter({category : 'software'}).invert()
    2038. +
    2039. * //=> SELECT * FROM items WHERE (category != 'software')
    2040. *
    2041. -
    2042. * @return {patio.Dataset} a cloned dataset with the order reversed.
    2043. +
    2044. * @example
    2045. +
    2046. * DB.from("items").filter({category : 'software', id : 3}).invert()
    2047. +
    2048. * //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
    2049. *
    2050. +
    2051. * @return {patio.Dataset} a cloned dataset with the filter inverted.
    2052. **/
    2053. -
    2054. reverse: function (args) {
    2055. -
    2056. 46 args = argsToArray(arguments);
    2057. -
    2058. 46 return this.order.apply(this, this._invertOrder(args.length ? args : this.__opts.order));
    2059. +
    2060. invert: function () {
    2061. +
    2062. 3 var having = this.__opts.having, where = this.__opts.where;
    2063. +
    2064. 3 if (!(having || where)) {
    2065. +
    2066. 1 throw new QueryError("No current filter");
    2067. +
    2068. }
    2069. +
    2070. 2 var o = {}
    2071. +
    2072. 2 if (having) {
    2073. +
    2074. 1 o.having = BooleanExpression.invert(having);
    2075. +
    2076. }
    2077. +
    2078. 2 if (where) {
    2079. +
    2080. 2 o.where = BooleanExpression.invert(where);
    2081. +
    2082. }
    2083. +
    2084. 2 return this.mergeOptions(o);
    2085. },
    2086. /**
    2087. -
    2088. * @see patio.Dataset#reverse
    2089. +
    2090. * Returns a cloned dataset with an inner join applied.
    2091. +
    2092. *
    2093. +
    2094. * @see patio.Dataset#joinTable
    2095. */
    2096. -
    2097. reverseOrder: function () {
    2098. -
    2099. 16 return this.reverse.apply(this, arguments);
    2100. +
    2101. join: function () {
    2102. +
    2103. 212 return this.innerJoin.apply(this, arguments);
    2104. },
    2105. /**
    2106. -
    2107. * Returns a copy of the dataset with the columns selected changed
    2108. -
    2109. * to the given columns. This also takes a function similar to {@link patio.Dataset#filter}
    2110. +
    2111. * Returns a joined dataset. Uses the following arguments:
    2112. *
    2113. * @example
    2114. -
    2115. * DB.from("items").select("a");
    2116. -
    2117. * //=> SELECT a FROM items
    2118. *
    2119. -
    2120. * DB.from("items").select("a", "b");
    2121. -
    2122. * //=> SELECT a, b FROM items
    2123. +
    2124. * DB.from("items").joinTable("leftOuter", "categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;
    2125. +
    2126. * //=>'SELECT
    2127. +
    2128. * *
    2129. +
    2130. * FROM
    2131. +
    2132. * `items`
    2133. +
    2134. * LEFT OUTER JOIN
    2135. +
    2136. * `categories` ON (
    2137. +
    2138. * (`categories`.`categoryId` = `items`.`id`)
    2139. +
    2140. * AND
    2141. +
    2142. * (`categories`.`categoryId` IN (1,2, 3))
    2143. +
    2144. * )
    2145. +
    2146. * DB.from("items").leftOuter("categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;
    2147. +
    2148. * //=>'SELECT
    2149. +
    2150. * *
    2151. +
    2152. * FROM
    2153. +
    2154. * `items`
    2155. +
    2156. * LEFT OUTER JOIN
    2157. +
    2158. * `categories` ON (
    2159. +
    2160. * (`categories`.`categoryId` = `items`.`id`)
    2161. +
    2162. * AND
    2163. +
    2164. * (`categories`.`categoryId` IN (1,2, 3))
    2165. +
    2166. * )
    2167. *
    2168. -
    2169. * DB.from("items").select("a", function(){
    2170. -
    2171. * return this.sum("b")
    2172. -
    2173. * }).sql; //=> SELECT a, sum(b) FROM items
    2174. +
    2175. * DB.from("items").leftOuterJoin("categories", {categoryId:"id"}).sql
    2176. +
    2177. * //=> SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2178. *
    2179. -
    2180. * @param {String|patio.sql.Identifier|Function} args variable number of colums to select
    2181. -
    2182. * @return {patio.Dataset} a cloned dataset with the columns selected changed.
    2183. -
    2184. */
    2185. -
    2186. select: function (args) {
    2187. -
    2188. 663 args = flatten(argsToArray(arguments));
    2189. -
    2190. 663 var columns = [];
    2191. -
    2192. 663 args.forEach(function (c) {
    2193. -
    2194. 1043 if (isFunction(c)) {
    2195. -
    2196. 23 var res = c.apply(sql, [sql]);
    2197. -
    2198. 23 columns = columns.concat(isArray(res) ? res : [res]);
    2199. -
    2200. } else {
    2201. -
    2202. 1020 columns.push(c);
    2203. -
    2204. }
    2205. -
    2206. });
    2207. -
    2208. 663 var select = [];
    2209. -
    2210. 663 columns.forEach(function (c) {
    2211. -
    2212. 1046 if (isHash(c)) {
    2213. -
    2214. 3 for (var i in c) {
    2215. -
    2216. 4 select.push(new AliasedExpression(new Identifier(i), c[i]));
    2217. -
    2218. }
    2219. -
    2220. 1043 } else if (isString(c)) {
    2221. -
    2222. 344 select.push(this.stringToIdentifier(c));
    2223. -
    2224. } else {
    2225. -
    2226. 699 select.push(c);
    2227. -
    2228. }
    2229. -
    2230. }, this);
    2231. -
    2232. 663 return this.mergeOptions({select: select});
    2233. -
    2234. -
    2235. },
    2236. -
    2237. -
    2238. /**
    2239. -
    2240. * Returns a cloned dataset that selects *.
    2241. +
    2242. * DB.from("items").rightOuterJoin("categories", {categoryId:"id"}).sql
    2243. +
    2244. * //=> SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2245. *
    2246. -
    2247. * @return {patio.Dataset} a cloned dataset that selects *.
    2248. -
    2249. */
    2250. -
    2251. selectAll: function () {
    2252. -
    2253. 6 return this.mergeOptions({select: null});
    2254. -
    2255. },
    2256. -
    2257. -
    2258. /**
    2259. -
    2260. * Selects the columns if only if there is not already select sources.
    2261. +
    2262. * DB.from("items").fullOuterJoin("categories", {categoryId:"id"}).sql
    2263. +
    2264. * //=> SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2265. *
    2266. -
    2267. * @example
    2268. +
    2269. * DB.from("items").innerJoin("categories", {categoryId:"id"}).sql
    2270. +
    2271. * //=> SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2272. *
    2273. -
    2274. * var ds = DB.from("items"); //SELECT * FROM items
    2275. +
    2276. * DB.from("items").leftJoin("categories", {categoryId:"id"}).sql
    2277. +
    2278. * //=> SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2279. *
    2280. -
    2281. * ds.select("a"); //SELECT a FROM items;
    2282. -
    2283. * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
    2284. -
    2285. * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
    2286. +
    2287. * DB.from("items").rightJoin("categories", {categoryId:"id"}).sql
    2288. +
    2289. * //=> SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2290. *
    2291. -
    2292. * @param cols columns to select if there is not already select sources.
    2293. -
    2294. * @return {patio.Dataset} a cloned dataset with the appropriate select sources.
    2295. +
    2296. * DB.from("items").fullJoin("categories", {categoryId:"id"}).sql
    2297. +
    2298. * //=> SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2299. +
    2300. *
    2301. +
    2302. * DB.from("items").naturalJoin("categories").sql
    2303. +
    2304. * //=> SELECT * FROM "items" NATURAL JOIN "categories"
    2305. +
    2306. *
    2307. +
    2308. * DB.from("items").naturalLeftJoin("categories").sql
    2309. +
    2310. * //=> SELECT * FROM "items" NATURAL LEFT JOIN "categories"
    2311. +
    2312. *
    2313. +
    2314. * DB.from("items").naturalRightJoin("categories").sql
    2315. +
    2316. * //=> SELECT * FROM "items" NATURAL RIGHT JOIN "categories"
    2317. +
    2318. *
    2319. +
    2320. * DB.from("items").naturalFullJoin("categories").sql
    2321. +
    2322. * //=> SELECT * FROM "items" NATURAL FULL JOIN "categories"'
    2323. +
    2324. *
    2325. +
    2326. * DB.from("items").crossJoin("categories").sql
    2327. +
    2328. * //=> SELECT * FROM "items" CROSS JOIN "categories"
    2329. +
    2330. *
    2331. +
    2332. * @param {String} type the type of join to do.
    2333. +
    2334. * @param {String|patio.sql.Identifier|patio.Dataset|Object} table depends on the type.
    2335. +
    2336. * <ul>
    2337. +
    2338. * <li>{@link patio.Dataset} - a subselect is performed with an alias</li>
    2339. +
    2340. * <li>Object - an object that has a tableName property.</li>
    2341. +
    2342. * <li>String|{@link patio.sql.Identifier} - the name of the table</li>
    2343. +
    2344. * </ul>
    2345. +
    2346. * @param [expr] - depends on type
    2347. +
    2348. * <ul>
    2349. +
    2350. * <li>Object|Array of two element arrays - Assumes key (1st arg) is column of joined table (unless already
    2351. +
    2352. * qualified), and value (2nd arg) is column of the last joined or primary table (or the
    2353. +
    2354. * implicitQualifier option</li>.
    2355. +
    2356. * <li>Array - If all members of the array are string or {@link patio.sql.Identifier}, considers
    2357. +
    2358. * them as columns and uses a JOIN with a USING clause. Most databases will remove duplicate columns from
    2359. +
    2360. * the result set if this is used.</li>
    2361. +
    2362. * <li>null|undefined(not passed in) - If a cb is not given, doesn't use ON or USING, so the JOIN should be a NATURAL
    2363. +
    2364. * or CROSS join. If a block is given, uses an ON clause based on the block, see below.</li>
    2365. +
    2366. * <li>Everything else - pretty much the same as a using the argument in a call to {@link patio.Dataset#filter},
    2367. +
    2368. * so strings are considered literal, {@link patio.sql.Identifiers} specify boolean columns, and patio.sql
    2369. +
    2370. * expressions can be used. Uses a JOIN with an ON clause.</li>
    2371. +
    2372. * </ul>
    2373. +
    2374. * @param {Object} options an object of options.
    2375. +
    2376. * @param {String|patio.sql.Identifier} [options.tableAlias=undefined] the name of the table's alias when joining, necessary for joining
    2377. +
    2378. * to the same table more than once. No alias is used by default.
    2379. +
    2380. * @param {String|patio.sql.Identifier} [options.implicitQualifier=undefined] The name to use for qualifying implicit conditions. By default,
    2381. +
    2382. * the last joined or primary table is used.
    2383. +
    2384. * @param {Function} [cb] cb - The cb argument should only be given if a JOIN with an ON clause is used,
    2385. +
    2386. * in which case it is called with
    2387. +
    2388. * <ul>
    2389. +
    2390. * <li>table alias/name for the table currently being joined</li>
    2391. +
    2392. * <li> the table alias/name for the last joined (or first table)
    2393. +
    2394. * <li>array of previous</li>
    2395. +
    2396. * </ul>
    2397. +
    2398. * the cb should return an expression to be used in the ON clause.
    2399. +
    2400. *
    2401. +
    2402. * @return {patio.Dataset} a cloned dataset joined using the arguments.
    2403. */
    2404. -
    2405. selectIfNoSource: function (cols) {
    2406. -
    2407. 0 var ret;
    2408. -
    2409. 0 if (!this.hasSelectSource) {
    2410. -
    2411. 0 ret = this.select.apply(this, arguments);
    2412. +
    2413. +
    2414. joinTable: function (type, table, expr, options, cb) {
    2415. +
    2416. 634 var args = argsToArray(arguments);
    2417. +
    2418. 634 if (isFunction(args[args.length - 1])) {
    2419. +
    2420. 12 cb = args[args.length - 1];
    2421. +
    2422. 12 args.pop();
    2423. +
    2424. } else {
    2425. +
    2426. 622 cb = null;
    2427. +
    2428. }
    2429. +
    2430. 634 type = args.shift(), table = args.shift(), expr = args.shift(), options = args.shift();
    2431. +
    2432. 634 expr = isUndefined(expr) ? null : expr, options = isUndefined(options) ? {} : options;
    2433. +
    2434. +
    2435. 634 var h;
    2436. +
    2437. 634 var usingJoin = isArray(expr) && expr.length && expr.every(function (x) {
    2438. +
    2439. 223 return isString(x) || isInstanceOf(x, Identifier)
    2440. +
    2441. });
    2442. +
    2443. 634 if (usingJoin && !this.supportsJoinUsing) {
    2444. +
    2445. 1 h = {};
    2446. +
    2447. 1 expr.forEach(function (s) {
    2448. +
    2449. 1 h[s] = s;
    2450. +
    2451. });
    2452. +
    2453. 1 return this.joinTable(type, table, h, options);
    2454. +
    2455. }
    2456. +
    2457. 633 var tableAlias, lastAlias;
    2458. +
    2459. 633 if (isHash(options)) {
    2460. +
    2461. 623 tableAlias = options.tableAlias;
    2462. +
    2463. 623 lastAlias = options.implicitQualifier;
    2464. +
    2465. 10 } else if (isString(options) || isInstanceOf(options, Identifier)) {
    2466. +
    2467. 9 tableAlias = options;
    2468. +
    2469. 9 lastAlias = null;
    2470. +
    2471. } else {
    2472. +
    2473. 1 throw new QueryError("Invalid options format for joinTable %j4", [options]);
    2474. +
    2475. }
    2476. +
    2477. 632 var tableAliasNum, tableName;
    2478. +
    2479. 632 if (isInstanceOf(table, Dataset)) {
    2480. +
    2481. 11 if (!tableAlias) {
    2482. +
    2483. 6 tableAliasNum = (this.__opts.numDatasetSources || 0) + 1;
    2484. +
    2485. 6 tableAlias = this._datasetAlias(tableAliasNum);
    2486. +
    2487. }
    2488. +
    2489. 11 tableName = tableAlias;
    2490. +
    2491. } else {
    2492. +
    2493. 621 if (!isUndefined(table.tableName)) {
    2494. +
    2495. 1 table = table.tableName;
    2496. +
    2497. }
    2498. +
    2499. 621 if (isArray(table)) {
    2500. +
    2501. 2 table = table.map(this.stringToIdentifier, this);
    2502. +
    2503. } else {
    2504. +
    2505. 619 table = isString(table) ? this.stringToIdentifier(table) : table;
    2506. +
    2507. 619 var parts = this._splitAlias(table), implicitTableAlias = parts[1];
    2508. +
    2509. 619 table = parts[0]
    2510. +
    2511. 619 tableAlias = tableAlias || implicitTableAlias;
    2512. +
    2513. 619 tableName = tableAlias || table;
    2514. +
    2515. }
    2516. +
    2517. }
    2518. +
    2519. 632 var join;
    2520. +
    2521. 632 if (!expr && !cb) {
    2522. +
    2523. 22 join = new JoinClause(type, table, tableAlias);
    2524. +
    2525. 610 } else if (usingJoin) {
    2526. +
    2527. 9 if (cb) {
    2528. +
    2529. 1 throw new QueryError("cant use a cb if an array is given");
    2530. +
    2531. }
    2532. +
    2533. 8 join = new JoinUsingClause(expr, type, table, tableAlias);
    2534. } else {
    2535. -
    2536. 0 ret = this.mergeOptions();
    2537. +
    2538. 601 lastAlias = lastAlias || this.__opts["lastJoinedTable"] || this.firstSourceAlias;
    2539. +
    2540. 600 if (Expression.isConditionSpecifier(expr)) {
    2541. +
    2542. 588 var newExpr = [];
    2543. +
    2544. 588 for (var i in expr) {
    2545. +
    2546. 909 var val = expr[i];
    2547. +
    2548. 909 if (isArray(val) && val.length == 2) {
    2549. +
    2550. 418 i = val[0], val = val[1];
    2551. +
    2552. }
    2553. +
    2554. 909 var k = this.qualifiedColumnName(i, tableName), v;
    2555. +
    2556. 909 if (isInstanceOf(val, Identifier)) {
    2557. +
    2558. 405 v = val.qualify(lastAlias);
    2559. +
    2560. } else {
    2561. +
    2562. 504 v = val;
    2563. +
    2564. }
    2565. +
    2566. 909 newExpr.push([k, v]);
    2567. +
    2568. }
    2569. +
    2570. 588 expr = newExpr;
    2571. +
    2572. }
    2573. +
    2574. 600 if (isFunction(cb)) {
    2575. +
    2576. 11 var expr2 = cb.apply(sql, [tableName, lastAlias, this.__opts.join || []]);
    2577. +
    2578. 11 expr = expr ? new BooleanExpression("AND", expr, expr2) : expr2;
    2579. +
    2580. }
    2581. +
    2582. 600 join = new JoinOnClause(expr, type, table, tableAlias);
    2583. }
    2584. -
    2585. 0 return ret;
    2586. +
    2587. 630 var opts = {join: (this.__opts.join || []).concat([join]), lastJoinedTable: tableName};
    2588. +
    2589. 630 if (tableAliasNum) {
    2590. +
    2591. 6 opts.numDatasetSources = tableAliasNum;
    2592. +
    2593. }
    2594. +
    2595. 630 return this.mergeOptions(opts);
    2596. +
    2597. },
    2598. /**
    2599. -
    2600. * Returns a copy of the dataset with the given columns added
    2601. -
    2602. * to the existing selected columns. If no columns are currently selected,
    2603. -
    2604. * it will select the columns given in addition to *.
    2605. +
    2606. * If given an integer, the dataset will contain only the first l results.
    2607. +
    2608. If a second argument is given, it is used as an offset. To use
    2609. +
    2610. * an offset without a limit, pass null as the first argument.
    2611. *
    2612. * @example
    2613. -
    2614. * DB.from("items").select("a").selectAppend("b").sql;
    2615. -
    2616. * //=> SELECT b FROM items
    2617. *
    2618. -
    2619. * DB.from("items").select("a").selectAppend("b", "c", "d").sql
    2620. -
    2621. * //=> SELECT a, b, c, d FROM items
    2622. +
    2623. * DB.from("items").limit(10)
    2624. +
    2625. * //=> SELECT * FROM items LIMIT 10
    2626. +
    2627. * DB.from("items").limit(10, 20)
    2628. +
    2629. * //=> SELECT * FROM items LIMIT 10 OFFSET 20
    2630. +
    2631. * DB.from("items").limit([3, 7]).sql
    2632. +
    2633. * //=> SELECT * FROM items LIMIT 5 OFFSET 3');
    2634. +
    2635. * DB.from("items").limit(null, 20)
    2636. +
    2637. * //=> SELECT * FROM items OFFSET 20
    2638. *
    2639. -
    2640. * DB.from("items").selectAppend("b").sql
    2641. -
    2642. * //=> SELECT *, b FROM items
    2643. +
    2644. * DB.from("items").limit('6', sql['a() - 1']).sql
    2645. +
    2646. * => 'SELECT * FROM items LIMIT 6 OFFSET a() - 1');
    2647. *
    2648. -
    2649. * @param [...] cols variable number of columns to add to the select statement
    2650. +
    2651. * @param {Number|String|Number[]} limit the limit to apply
    2652. +
    2653. * @param {Number|String|patio.sql.LiteralString} offset the offset to apply
    2654. *
    2655. -
    2656. * @return {patio.Dataset} returns a cloned dataset with the new select columns appended.
    2657. -
    2658. */
    2659. -
    2660. selectAppend: function (cols) {
    2661. -
    2662. 7 cols = argsToArray(arguments);
    2663. -
    2664. 7 var currentSelect = this.__opts.select;
    2665. -
    2666. 7 if (!currentSelect || !currentSelect.length) {
    2667. -
    2668. 3 currentSelect = [this._static.WILDCARD];
    2669. +
    2670. * @return {patio.Dataset} a cloned dataset witht the LIMIT and OFFSET applied.
    2671. +
    2672. **/
    2673. +
    2674. limit: function (limit, offset) {
    2675. +
    2676. 46 if (this.__opts.sql) {
    2677. +
    2678. 7 return this.fromSelf().limit(limit, offset);
    2679. }
    2680. -
    2681. 7 return this.select.apply(this, currentSelect.concat(cols));
    2682. +
    2683. 39 if (isArray(limit) && limit.length == 2) {
    2684. +
    2685. 1 offset = limit[0];
    2686. +
    2687. 1 limit = limit[1] - limit[0] + 1;
    2688. +
    2689. }
    2690. +
    2691. 39 if (isString(limit) || isInstanceOf(limit, LiteralString)) {
    2692. +
    2693. 2 limit = parseInt("" + limit, 10);
    2694. +
    2695. }
    2696. +
    2697. 39 if (isNumber(limit) && limit < 1) {
    2698. +
    2699. 2 throw new QueryError("Limit must be >= 1");
    2700. +
    2701. }
    2702. +
    2703. 37 var opts = {limit: limit};
    2704. +
    2705. 37 if (offset) {
    2706. +
    2707. 9 if (isString(offset) || isInstanceOf(offset, LiteralString)) {
    2708. +
    2709. 1 offset = parseInt("" + offset, 10);
    2710. +
    2711. 1 isNaN(offset) && (offset = 0);
    2712. +
    2713. }
    2714. +
    2715. 9 if (isNumber(offset) && offset < 0) {
    2716. +
    2717. 1 throw new QueryError("Offset must be >= 0");
    2718. +
    2719. }
    2720. +
    2721. 8 opts.offset = offset;
    2722. +
    2723. }
    2724. +
    2725. 36 return this.mergeOptions(opts);
    2726. },
    2727. /**
    2728. -
    2729. * Returns a copy of the dataset with the given columns added
    2730. -
    2731. * to the existing selected columns. If no columns are currently selected
    2732. -
    2733. * it will just select the columns given.
    2734. -
    2735. *
    2736. -
    2737. * @example
    2738. -
    2739. * DB.from("items").select("a").select("b").sql;
    2740. -
    2741. * //=> SELECT b FROM items
    2742. *
    2743. -
    2744. * DB.from("items").select("a").selectMore("b", "c", "d").sql
    2745. -
    2746. * //=> SELECT a, b, c, d FROM items
    2747. +
    2748. * Returns a cloned dataset with a not equal expression added to the WHERE
    2749. +
    2750. * clause.
    2751. *
    2752. -
    2753. * DB.from("items").selectMore("b").sql
    2754. -
    2755. * //=> SELECT b FROM items
    2756. +
    2757. * @example
    2758. +
    2759. * DB.from("test").neq({x : 1});
    2760. +
    2761. * //=> SELECT * FROM test WHERE (x != 1)
    2762. +
    2763. * DB.from("test").neq({x : 1, y : 10});
    2764. +
    2765. * //=> SELECT * FROM test WHERE ((x != 1) AND (y != 10))
    2766. *
    2767. -
    2768. * @param [...] cols variable number of columns to add to the select statement
    2769. +
    2770. * @param {Object} obj object used to create the not equal expression
    2771. *
    2772. -
    2773. * @return {patio.Dataset} returns a cloned dataset with the new select columns appended.
    2774. +
    2775. * @return {patio.Dataset} a cloned dataset with the not equal expression added to the WHERE clause.
    2776. */
    2777. -
    2778. selectMore: function (cols) {
    2779. -
    2780. 10 cols = argsToArray(arguments);
    2781. -
    2782. 10 var currentSelect = this.__opts.select;
    2783. -
    2784. 10 return this.select.apply(this, (currentSelect || []).concat(cols));
    2785. +
    2786. neq: function (obj) {
    2787. +
    2788. 2 return this.filter(this.__createBoolExpression("neq", obj));
    2789. },
    2790. /**
    2791. -
    2792. * Set the default values for insert and update statements. The values hash passed
    2793. -
    2794. * to insert or update are merged into this hash, so any values in the hash passed
    2795. -
    2796. * to insert or update will override values passed to this method.
    2797. -
    2798. *
    2799. -
    2800. * @example
    2801. -
    2802. * DB.from("items").setDefaults({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();
    2803. -
    2804. * //=> INSERT INTO items (a, c, b) VALUES ('d', 'c', 'b')
    2805. -
    2806. *
    2807. -
    2808. * @param {Object} hash object with key value pairs to use as override values
    2809. *
    2810. -
    2811. * @return {patio.Dataset} a cloned dataset with the defaults added to the current datasets defaults.
    2812. -
    2813. */
    2814. -
    2815. setDefaults: function (hash) {
    2816. -
    2817. 5 return this.mergeOptions({defaults: merge({}, this.__opts.defaults || {}, hash)});
    2818. -
    2819. },
    2820. -
    2821. -
    2822. /**
    2823. -
    2824. * Set values that override hash arguments given to insert and update statements.
    2825. -
    2826. * This hash is merged into the hash provided to insert or update, so values
    2827. -
    2828. * will override any values given in the insert/update hashes.
    2829. +
    2830. * Returns a cloned dataset with an equal expression added to the WHERE
    2831. +
    2832. * clause.
    2833. *
    2834. * @example
    2835. -
    2836. * DB.from("items").setOverrides({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();
    2837. -
    2838. * //=> INSERT INTO items (a, c, b) VALUES ('a', 'c', 'b')
    2839. +
    2840. * DB.from("test").eq({x : 1});
    2841. +
    2842. * //=> SELECT * FROM test WHERE (x = 1)
    2843. +
    2844. * DB.from("test").eq({x : 1, y : 10});
    2845. +
    2846. * //=> SELECT * FROM test WHERE ((x = 1) AND (y = 10))
    2847. *
    2848. -
    2849. * @param {Object} hash object with key value pairs to use as override values
    2850. +
    2851. * @param {Object} obj object used to create the equal expression
    2852. *
    2853. -
    2854. * @return {patio.Dataset} a cloned dataset with the overrides added to the current datasets overrides.
    2855. +
    2856. * @return {patio.Dataset} a cloned dataset with the equal expression added to the WHERE clause.
    2857. */
    2858. -
    2859. setOverrides: function (hash) {
    2860. -
    2861. 5 return this.mergeOptions({overrides: merge({}, this.__opts.overrides || {}, hash)});
    2862. +
    2863. eq: function (obj) {
    2864. +
    2865. 2 return this.filter(this.__createBoolExpression("eq", obj));
    2866. },
    2867. /**
    2868. -
    2869. * Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
    2870. -
    2871. * @example
    2872. -
    2873. * DB.from("items").group("a").having({a : 1}).where("b").unfiltered().sql;
    2874. -
    2875. * //=> SELECT * FROM items GROUP BY a
    2876. *
    2877. -
    2878. * @return {patio.Dataset} a cloned dataset with no HAVING or WHERE clause.
    2879. -
    2880. */
    2881. -
    2882. unfiltered: function () {
    2883. -
    2884. 14 return this.mergeOptions({where: null, having: null});
    2885. -
    2886. },
    2887. -
    2888. -
    2889. /**
    2890. -
    2891. * Returns a copy of the dataset with no GROUP or HAVING clause.
    2892. +
    2893. * Returns a cloned dataset with a greater than expression added to the WHERE
    2894. +
    2895. * clause.
    2896. *
    2897. * @example
    2898. -
    2899. * DB.from("t").group("a").having({a : 1}).where("b").ungrouped().sql;
    2900. -
    2901. * //=> SELECT * FROM t WHERE b
    2902. +
    2903. * DB.from("test").gt({x : 1});
    2904. +
    2905. * //=> SELECT * FROM test WHERE (x > 1)
    2906. +
    2907. * DB.from("test").gt({x : 1, y : 10});
    2908. +
    2909. * //=> SELECT * FROM test WHERE ((x > 1) AND (y > 10))
    2910. *
    2911. -
    2912. * @return {patio.Dataset} a cloned dataset with no GROUP or HAVING clause.
    2913. +
    2914. * @param {Object} obj object used to create the greater than expression.
    2915. +
    2916. *
    2917. +
    2918. * @return {patio.Dataset} a cloned dataset with the greater than expression added to the WHERE clause.
    2919. */
    2920. -
    2921. ungrouped: function () {
    2922. -
    2923. 1 return this.mergeOptions({group: null, having: null});
    2924. +
    2925. gt: function (obj) {
    2926. +
    2927. 2 return this.filter(this.__createBoolExpression("gt", obj));
    2928. },
    2929. /**
    2930. -
    2931. * Adds a UNION clause using a second dataset object.
    2932. -
    2933. * A UNION compound dataset returns all rows in either the current dataset
    2934. -
    2935. * or the given dataset.
    2936. -
    2937. * Options:
    2938. -
    2939. * :alias :: Use the given value as the from_self alias
    2940. -
    2941. * :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
    2942. -
    2943. * :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
    2944. -
    2945. *
    2946. -
    2947. * @example
    2948. -
    2949. * DB.from("items").union(DB.from("otherItems")).sql;
    2950. -
    2951. * //=> SELECT * FROM items UNION SELECT * FROM other_items
    2952. -
    2953. *
    2954. -
    2955. * DB.from("items").union(DB.from("otherItems"), {all : true, fromSelf : false}).sql;
    2956. -
    2957. * //=> SELECT * FROM items UNION ALL SELECT * FROM other_items
    2958. *
    2959. -
    2960. * DB.from("items").union(DB.from("otherItems"), {alias : "i"})
    2961. -
    2962. * //=> SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS i
    2963. +
    2964. * Returns a cloned dataset with a less than expression added to the WHERE
    2965. +
    2966. * clause.
    2967. *
    2968. -
    2969. * @param {patio.Dataset} dataset dataset to union with
    2970. -
    2971. * @param {Object} opts addional options
    2972. -
    2973. * @param {String|patio.sql.Identifier} [opts.alias] Alias to use as the fromSelf alias.
    2974. -
    2975. * @param {Boolean} [opt.all=false] Set to true to use UNION ALL instead of UNION so duplicate rows can occur
    2976. -
    2977. * @param {Boolean} [opts.fromSelf=true] Set to false to not wrap the returned dataset in a fromSelf.
    2978. +
    2979. * @example
    2980. +
    2981. * DB.from("test").lt({x : 1});
    2982. +
    2983. * //=> SELECT * FROM test WHERE (x < 1)
    2984. +
    2985. * DB.from("test").lt({x : 1, y : 10});
    2986. +
    2987. * //=> SELECT * FROM test WHERE ((x < 1) AND (y < 10))
    2988. *
    2989. -
    2990. * @return {patio.Dataset} a cloned dataset with the union.
    2991. +
    2992. * @param {Object} obj object used to create the less than expression.
    2993. *
    2994. +
    2995. * @return {patio.Dataset} a cloned dataset with the less than expression added to the WHERE clause.
    2996. */
    2997. -
    2998. union: function (dataset, opts) {
    2999. -
    3000. 21 opts = isUndefined(opts) ? {} : opts;
    3001. -
    3002. 21 if (!isHash(opts)) {
    3003. -
    3004. 3 opts = {all: opts};
    3005. -
    3006. }
    3007. -
    3008. 21 return this.compoundClone("union", dataset, opts);
    3009. +
    3010. lt: function (obj) {
    3011. +
    3012. 2 return this.filter(this.__createBoolExpression("lt", obj));
    3013. },
    3014. /**
    3015. -
    3016. * Returns a copy of the dataset with no limit or offset.
    3017. +
    3018. * Returnes a cloned dataset with the IS NOT expression added to the WHERE
    3019. +
    3020. * clause.
    3021. *
    3022. * @example
    3023. -
    3024. * DB.from("t").limit(10, 20).unlimited().sql;
    3025. -
    3026. * //=> SELECT * FROM t
    3027. -
    3028. *
    3029. -
    3030. * @return {patio.Dataset} a cloned dataset with no limit or offset.
    3031. -
    3032. */
    3033. -
    3034. unlimited: function () {
    3035. -
    3036. 1 return this.mergeOptions({limit: null, offset: null});
    3037. -
    3038. },
    3039. -
    3040. -
    3041. /**
    3042. -
    3043. * Returns a copy of the dataset with no order.
    3044. *
    3045. -
    3046. * @example
    3047. -
    3048. * DB.from("t").order("a", sql.identifier("b").desc()).unordered().sql;
    3049. -
    3050. * //=> SELECT * FROM t
    3051. +
    3052. * DB.from("test").isNot({boolFlag : null});
    3053. +
    3054. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL);
    3055. +
    3056. * DB.from("test").isNot({boolFlag : false, otherFlag : true, name : null});
    3057. +
    3058. * => SELECT * FROM test WHERE ((boolFlag IS NOT FALSE) AND (otherFlag IS NOT TRUE) AND (name IS NOT NULL));
    3059. *
    3060. -
    3061. * @return {patio.Dataset} a cloned dataset with no order.
    3062. -
    3063. */
    3064. -
    3065. unordered: function () {
    3066. -
    3067. 115 return this.order(null);
    3068. -
    3069. },
    3070. -
    3071. -
    3072. /**
    3073. -
    3074. * Add a condition to the WHERE clause. See {@link patio.Dataset#filter} for argument types.
    3075. +
    3076. * @param {Object} obj object used to create the IS NOT expression for.
    3077. *
    3078. -
    3079. * @example
    3080. -
    3081. * DB.from("test").where('price < ? AND id in ?', 100, [1, 2, 3]).sql;
    3082. -
    3083. * //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
    3084. -
    3085. * DB.from("test").where('price < {price} AND id in {ids}', {price:100, ids:[1, 2, 3]}).sql;
    3086. -
    3087. * //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))")
    3088. +
    3089. * @return {patio.Dataset} a cloned dataset with the IS NOT expression added to the WHERE clause.
    3090. *
    3091. */
    3092. -
    3093. where: function () {
    3094. -
    3095. 252 return this._filter.apply(this, ["where"].concat(argsToArray(arguments)));
    3096. +
    3097. isNot: function (obj) {
    3098. +
    3099. 4 return this.filter(this.__createBoolExpression("isNot", obj));
    3100. },
    3101. /**
    3102. -
    3103. * Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
    3104. -
    3105. * A common table expression acts as an inline view for the query.
    3106. +
    3107. * Returnes a cloned dataset with the IS expression added to the WHERE
    3108. +
    3109. * clause.
    3110. *
    3111. -
    3112. * @name with
    3113. * @example
    3114. *
    3115. -
    3116. * DB.from("t")["with"]("t", db.from("x"))["with"]("j", db.from("y")).sql;
    3117. -
    3118. * //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y) SELECT * FROM t'
    3119. -
    3120. *
    3121. -
    3122. * DB.from("t")["with"]("t", db.from("x")).withRecursive("j", db.from("y"), db.from("j")).sql;
    3123. -
    3124. * //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t'
    3125. +
    3126. * DB.from("test").is({boolFlag : null});
    3127. +
    3128. * => SELECT * FROM test WHERE (boolFlag IS NULL);
    3129. +
    3130. * DB.from("test").is({boolFlag : false, otherFlag : true, name : null});
    3131. +
    3132. * => SELECT * FROM test WHERE ((boolFlag IS FALSE) AND (otherFlag IS TRUE) AND (name IS NULL));
    3133. *
    3134. -
    3135. * DB.from("t")["with"]("t", db.from("x"), {args:["b"]}).sql;
    3136. -
    3137. * //=> 'WITH t(b) AS (SELECT * FROM x) SELECT * FROM t'
    3138. +
    3139. * @param {Object} obj object used to create the IS expression for.
    3140. *
    3141. -
    3142. * @param {String} name the name of the to assign to the CTE.
    3143. -
    3144. * @param {patio.Dataset} dataset the dataset to use for the CTE.
    3145. -
    3146. * @param {Object} opts extra options.
    3147. -
    3148. * @param {String[]} [opts.args] colums/args for the CTE.
    3149. -
    3150. * @param {Boolean} [opts.recursive] set to true that the CTE is recursive.
    3151. +
    3152. * @return {patio.Dataset} a cloned dataset with the IS expression added to the WHERE clause.
    3153. *
    3154. -
    3155. * @return {patio.Dataset} a cloned dataset with the CTE.
    3156. */
    3157. -
    3158. "with": function (name, dataset, opts) {
    3159. -
    3160. 6 if (!this.supportsCte) {
    3161. -
    3162. 1 throw new QueryError("this dataset does not support common table expressions");
    3163. -
    3164. }
    3165. -
    3166. 5 return this.mergeOptions({
    3167. -
    3168. "with": (this.__opts["with"] || []).concat([merge(opts || {}, {name: this.stringToIdentifier(name), dataset: dataset})])
    3169. -
    3170. });
    3171. +
    3172. is: function (obj) {
    3173. +
    3174. 4 return this.filter(this.__createBoolExpression("is", obj));
    3175. },
    3176. /**
    3177. -
    3178. * Add a recursive common table expression (CTE) with the given name, a dataset that
    3179. -
    3180. * defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
    3181. -
    3182. * of the CTE.
    3183. -
    3184. *
    3185. -
    3186. * @example
    3187. -
    3188. *
    3189. -
    3190. * //Sing withRecursive call.
    3191. -
    3192. * DB.from("t").withRecursive("t", db.from("x"), db.from("t")).sql;
    3193. -
    3194. * //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
    3195. +
    3196. * Returnes a cloned dataset with the IS NOT NULL boolean expression added to the WHERE
    3197. +
    3198. * clause.
    3199. *
    3200. -
    3201. * //Multiple withRecursive calls.
    3202. -
    3203. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"))
    3204. -
    3205. * .withRecursive("j", db.from("y"), db.from("j")).sql;
    3206. -
    3207. * //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t),
    3208. -
    3209. * j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t';
    3210. +
    3211. * @example
    3212. *
    3213. -
    3214. * //Adding args
    3215. -
    3216. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {args:["b", "c"]}).sql;
    3217. -
    3218. * //=> 'WITH t(b, c) AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
    3219. +
    3220. * DB.from("test").isNotNull("boolFlag");
    3221. +
    3222. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL);
    3223. +
    3224. * DB.from("test").isNotNull("boolFlag", "otherFlag");
    3225. +
    3226. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL AND otherFlag IS NOT NULL);
    3227. *
    3228. -
    3229. * //Setting union all to false
    3230. -
    3231. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {unionAll:false}).sql;
    3232. -
    3233. * //=> 'WITH t AS (SELECT * FROM x UNION SELECT * FROM t) SELECT * FROM t');
    3234. +
    3235. * @param {String...} arr variable number of arguments to create an IS NOT NULL expression for.
    3236. *
    3237. -
    3238. * @param {String} name the name to assign to the CTE
    3239. -
    3240. * @param {patio.Dataset} nonRecursive the non-recursive part of the CTE
    3241. -
    3242. * @param {patio.Dataset} recursive the recursive part of the CTE
    3243. -
    3244. * @param {Object} [opts={}] extra options
    3245. -
    3246. * @param {String[]} [opts.args] columns to include with the CTE
    3247. -
    3248. * @param {Boolena} [opts.unionAll] set to false to use UNION instead of UNION ALL when combining non recursive
    3249. -
    3250. * with recursive.
    3251. +
    3252. * @return {patio.Dataset} a cloned dataset with the IS NOT NULL expression added to the WHERE clause.
    3253. *
    3254. -
    3255. * @return {patio.Dataset} a cloned dataset with the CTE.
    3256. */
    3257. -
    3258. withRecursive: function (name, nonRecursive, recursive, opts) {
    3259. -
    3260. 7 if (!this.supportsCte) {
    3261. -
    3262. 1 throw new QueryError("This dataset does not support common table expressions");
    3263. -
    3264. }
    3265. -
    3266. 6 opts = opts || {};
    3267. -
    3268. 6 var wit = (this.__opts["with"] || []).concat([merge(opts, {recursive: true, name: this.stringToIdentifier(name), dataset: nonRecursive.union(recursive, {all: opts.unionAll != false, fromSelf: false})})]);
    3269. -
    3270. 6 return this.mergeOptions({"with": wit});
    3271. +
    3272. isNotNull: function (arr) {
    3273. +
    3274. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);
    3275. +
    3276. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    3277. },
    3278. /**
    3279. -
    3280. * Returns a copy of the dataset with the static SQL used. This is useful if you want
    3281. -
    3282. * to keep the same {@link patio.Dataset#rowCb}/{@link patio.Dataset#graph},
    3283. -
    3284. * but change the SQL used to custom SQL.
    3285. +
    3286. * Returnes a cloned dataset with the IS NULL boolean expression added to the WHERE
    3287. +
    3288. * clause.
    3289. *
    3290. * @example
    3291. -
    3292. * DB.from("items").withSql('SELECT * FROM foo')
    3293. -
    3294. * //=> SELECT * FROM foo
    3295. *
    3296. -
    3297. * @param {String} sql sql for the dataset to use.
    3298. +
    3299. * DB.from("test").isNull("boolFlag");
    3300. +
    3301. * => SELECT * FROM test WHERE (boolFlag IS NULL);
    3302. +
    3303. * DB.from("test").isNull("boolFlag", "otherFlag");
    3304. +
    3305. * => SELECT * FROM test WHERE (boolFlag IS NULL AND otherFlag IS NULL);
    3306. *
    3307. -
    3308. * @return {patio.Dataset} a cloned dataset with the static sql set.
    3309. -
    3310. */
    3311. -
    3312. withSql: function (sql) {
    3313. -
    3314. 46 var args = argsToArray(arguments).slice(1);
    3315. -
    3316. 46 if (args.length) {
    3317. -
    3318. 23 sql = new PlaceHolderLiteralString(sql, args)
    3319. -
    3320. }
    3321. -
    3322. 46 return this.mergeOptions({sql: sql});
    3323. -
    3324. },
    3325. -
    3326. -
    3327. -
    3328. /**
    3329. -
    3330. * Add the dataset to the list of compounds
    3331. +
    3332. * @param {String...} arr variable number of arguments to create an IS NULL expression for.
    3333. *
    3334. -
    3335. * @param {String} type the type of compound (i.e. "union", "intersect")
    3336. -
    3337. * @param {patio.Dataset} dataset the dataset to add to
    3338. -
    3339. * @param [Object] [options={}] compound option to use (i.e {all : true})
    3340. +
    3341. * @return {patio.Dataset} a cloned dataset with the IS NULL expression added to the WHERE clause.
    3342. *
    3343. -
    3344. * @return {patio.Dataset} ds with the dataset added to the compounds.
    3345. */
    3346. -
    3347. compoundClone: function (type, dataset, options) {
    3348. -
    3349. 51 var ds = this._compoundFromSelf().mergeOptions({compounds: (array.toArray(this.__opts.compounds || [])).concat([
    3350. -
    3351. [type, dataset._compoundFromSelf(), options.all]
    3352. -
    3353. ])});
    3354. -
    3355. 51 return options.fromSelf === false ? ds : ds.fromSelf(options);
    3356. +
    3357. isNull: function (arr) {
    3358. +
    3359. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);
    3360. +
    3361. 2 return this.filter(this.__createBoolExpression("is", arr));
    3362. },
    3363. /**
    3364. -
    3365. * Returns the a cloned dataset with out the {@link patio.Dataset#rowCb}
    3366. +
    3367. * Returnes a cloned dataset with the IS NOT TRUE boolean expression added to the WHERE
    3368. +
    3369. * clause.
    3370. *
    3371. * @example
    3372. -
    3373. * var ds = DB.from("test");
    3374. -
    3375. * ds.rowCb = function(r){
    3376. -
    3377. * r.a = r.a * 2;
    3378. -
    3379. * }
    3380. *
    3381. -
    3382. * ds.all().then(function(ret){
    3383. -
    3384. * //ret === [{a : 4}, {a : 6}]
    3385. -
    3386. * });
    3387. -
    3388. * ds.naked().all().then(function(ret){
    3389. -
    3390. * //ret === [{a : 2}, {a : 3}];
    3391. -
    3392. * });
    3393. +
    3394. * DB.from("test").isNotTrue("boolFlag");
    3395. +
    3396. * => SELECT * FROM test WHERE (boolFlag IS NOT TRUE);
    3397. +
    3398. * DB.from("test").isNotTrue("boolFlag", "otherFlag");
    3399. +
    3400. * => SELECT * FROM test WHERE (boolFlag IS NOT TRUE AND otherFlag IS NOT TRUE);
    3401. *
    3402. -
    3403. * @return {patio.Dataset} a cloned dataset with out the {@link patio.Dataset#rowCb}
    3404. -
    3405. */
    3406. -
    3407. naked: function () {
    3408. -
    3409. 2266 var ds = this.mergeOptions({});
    3410. -
    3411. 2266 ds.rowCb = null;
    3412. -
    3413. 2266 return ds;
    3414. -
    3415. },
    3416. -
    3417. -
    3418. /**
    3419. -
    3420. * @private
    3421. -
    3422. * Protected
    3423. +
    3424. * @param {String...} arr variable number of arguments to create an IS NOT TRUE expression for.
    3425. *
    3426. -
    3427. * Internal filter method so it works on either the having or where clauses.
    3428. -
    3429. */
    3430. -
    3431. _filter: function (clause) {
    3432. -
    3433. 3765 var cond = argsToArray(arguments).slice(1), cb;
    3434. -
    3435. 3765 if (cond.length && isFunction(cond[cond.length - 1])) {
    3436. -
    3437. 59 cb = cond.pop();
    3438. -
    3439. }
    3440. -
    3441. 3765 cond = cond.length == 1 ? cond[0] : cond
    3442. -
    3443. 3765 if ((cond == null || cond == undefined || cond === "") || (isArray(cond) && cond.length == 0 && !cb) || (isObject(cond) && isEmpty(cond) && !cb)) {
    3444. -
    3445. 293 return this.mergeOptions();
    3446. -
    3447. } else {
    3448. -
    3449. 3472 cond = this._filterExpr(cond, cb);
    3450. -
    3451. 3469 var cl = this.__opts[clause];
    3452. -
    3453. 3469 cl && (cond = new BooleanExpression("AND", cl, cond));
    3454. -
    3455. 3469 var opts = {};
    3456. -
    3457. 3469 opts[clause] = cond;
    3458. -
    3459. 3469 return this.mergeOptions(opts);
    3460. -
    3461. }
    3462. -
    3463. },
    3464. -
    3465. -
    3466. /**
    3467. -
    3468. * Splits a possible implicit alias, handling both {@link patio.sql.AliasedExpression}s
    3469. -
    3470. * and strings. Returns an array of two elements, with the first being the
    3471. -
    3472. * main expression, and the second being the alias. Alias may be null if it is a
    3473. -
    3474. * string that does not contain an alias {table}___{alias}.
    3475. -
    3476. */
    3477. -
    3478. _splitAlias: function (c) {
    3479. -
    3480. 619 var ret;
    3481. -
    3482. 619 if (isInstanceOf(c, AliasedExpression)) {
    3483. -
    3484. 5 ret = [c.expression, c.alias];
    3485. -
    3486. 614 } else if (isString(c)) {
    3487. -
    3488. 0 var parts = this._splitString(c), cTable = parts[0], column = parts[1], alias = parts[2];
    3489. -
    3490. 0 if (alias) {
    3491. -
    3492. 0 ret = [cTable ? new QualifiedIdentifier(cTable, column) : column, alias];
    3493. -
    3494. } else {
    3495. -
    3496. 0 ret = [c, null];
    3497. -
    3498. }
    3499. -
    3500. } else {
    3501. -
    3502. 614 ret = [c, null];
    3503. -
    3504. }
    3505. -
    3506. 619 return ret;
    3507. -
    3508. },
    3509. -
    3510. -
    3511. -
    3512. /**
    3513. -
    3514. * @private
    3515. -
    3516. * Inverts the given order by breaking it into a list of column references
    3517. -
    3518. * and inverting them.
    3519. +
    3520. * @return {patio.Dataset} a cloned dataset with the IS NOT TRUE expression added to the WHERE clause.
    3521. *
    3522. -
    3523. * ds.invertOrder([sql.identifier("id").desc()]]) //=> [id]
    3524. -
    3525. * ds.invertOrder("category", sql.identifier("price").desc()]) #=> [category.desc(), price]
    3526. */
    3527. -
    3528. -
    3529. _invertOrder: function (order) {
    3530. -
    3531. 46 var ret = order;
    3532. -
    3533. 46 if (order) {
    3534. -
    3535. 45 ret = order.map(function (o) {
    3536. -
    3537. 57 if (isInstanceOf(o, OrderedExpression)) {
    3538. -
    3539. 17 return o.invert();
    3540. -
    3541. } else {
    3542. -
    3543. 40 return new OrderedExpression(isString(o) ? new Identifier(o) : o);
    3544. -
    3545. }
    3546. -
    3547. }, this);
    3548. -
    3549. }
    3550. -
    3551. 46 return ret;
    3552. +
    3553. isNotTrue: function (arr) {
    3554. +
    3555. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);
    3556. +
    3557. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    3558. },
    3559. /**
    3560. -
    3561. * Creates a boolean expression that each key is compared to its value using the provided operator.
    3562. +
    3563. * Returnes a cloned dataset with the IS TRUE boolean expression added to the WHERE
    3564. +
    3565. * clause.
    3566. *
    3567. * @example
    3568. *
    3569. -
    3570. * ds.__createBoolExpression("gt", {x : 1, y:2, z : 5}) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))
    3571. -
    3572. * ds.__createBoolExpression("gt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))
    3573. -
    3574. * ds.__createBoolExpression("lt", {x : 1, y:2, z : 5}) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))
    3575. -
    3576. * ds.__createBoolExpression("lt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))
    3577. +
    3578. * DB.from("test").isTrue("boolFlag");
    3579. +
    3580. * => SELECT * FROM test WHERE (boolFlag IS TRUE);
    3581. +
    3582. * DB.from("test").isTrue("boolFlag", "otherFlag");
    3583. +
    3584. * => SELECT * FROM test WHERE (boolFlag IS TRUE AND otherFlag IS TRUE);
    3585. *
    3586. -
    3587. * @param {String} op valid boolean expression operator to capare each K,V pair with
    3588. -
    3589. * @param {Object| Array } obj object or two dimensional array containing key value pairs
    3590. +
    3591. * @param {String...} arr variable number of arguments to create an IS TRUE expression for.
    3592. +
    3593. *
    3594. +
    3595. * @return {patio.Dataset} a cloned dataset with the IS TRUE expression added to the WHERE clause.
    3596. *
    3597. -
    3598. * @return {patio.sql.BooleanExpression} boolean expression joined by a AND of each key value pair compared by the op
    3599. */
    3600. -
    3601. __createBoolExpression: function (op, obj) {
    3602. -
    3603. 32 var pairs = [];
    3604. -
    3605. 32 if (Expression.isConditionSpecifier(obj)) {
    3606. -
    3607. 32 if (isHash(obj)) {
    3608. -
    3609. 18 obj = array.toArray(obj);
    3610. -
    3611. }
    3612. -
    3613. 32 obj.forEach(function (pair) {
    3614. -
    3615. 42 pairs.push(new BooleanExpression(op, new Identifier(pair[0]), pair[1]));
    3616. -
    3617. });
    3618. -
    3619. }
    3620. -
    3621. 32 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
    3622. +
    3623. isTrue: function (arr) {
    3624. +
    3625. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);
    3626. +
    3627. 2 return this.filter(this.__createBoolExpression("is", arr));
    3628. },
    3629. /**
    3630. -
    3631. * @private
    3632. -
    3633. *
    3634. -
    3635. * Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
    3636. +
    3637. * Returnes a cloned dataset with the IS NOT FALSE boolean expression added to the WHERE
    3638. +
    3639. * clause.
    3640. *
    3641. * @example
    3642. *
    3643. -
    3644. * ds.__createBetweenExpression({x : [1,2]}) => //=> WHERE ((x >= 1) AND (x <= 10))
    3645. -
    3646. * ds.__createBetweenExpression({x : [1,2]}, true) => //=> WHERE ((x < 1) OR (x > 10))
    3647. +
    3648. * DB.from("test").isNotFalse("boolFlag");
    3649. +
    3650. * => SELECT * FROM test WHERE (boolFlag IS NOT FALSE);
    3651. +
    3652. * DB.from("test").isNotFalse("boolFlag", "otherFlag");
    3653. +
    3654. * => SELECT * FROM test WHERE (boolFlag IS NOT FALSE AND otherFlag IS NOT FALSE);
    3655. +
    3656. * @param {String...} arr variable number of arguments to create an IS NOT FALSE expression for.
    3657. *
    3658. -
    3659. * @param {Object} obj object where the keys are columns and the values are two element arrays.
    3660. -
    3661. * @param {Boolean} [invert] if set to true it inverts the between to make it not between the two values
    3662. +
    3663. * @return {patio.Dataset} a cloned dataset with the IS NOT FALSE expression added to the WHERE clause.
    3664. *
    3665. -
    3666. * @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
    3667. */
    3668. -
    3669. __createBetweenExpression: function (obj, invert) {
    3670. -
    3671. 4 var pairs = [];
    3672. -
    3673. 4 for (var i in obj) {
    3674. -
    3675. 4 var v = obj[i];
    3676. -
    3677. 4 if (isArray(v) && v.length) {
    3678. -
    3679. 2 var ident = this.stringToIdentifier(i);
    3680. -
    3681. 2 pairs.push(new BooleanExpression("AND", new BooleanExpression("gte", ident, v[0]), new BooleanExpression("lte", ident, v[1])));
    3682. -
    3683. } else {
    3684. -
    3685. 2 throw new QueryError("Between requires an array for the value");
    3686. -
    3687. }
    3688. -
    3689. }
    3690. -
    3691. 2 var ret = pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs))
    3692. -
    3693. 2 return invert ? BooleanExpression.invert(ret) : ret;
    3694. +
    3695. isNotFalse: function (arr) {
    3696. +
    3697. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);
    3698. +
    3699. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    3700. },
    3701. -
    3702. /**
    3703. -
    3704. * @private
    3705. -
    3706. * Converts an array to a two dimensional array where the first element
    3707. -
    3708. * is the identifier and the second argument is the value that the value should equal
    3709. -
    3710. * used by is{Null|NotNull|True|NotTrue|False|notFalse} functions to join all the values passed in.
    3711. -
    3712. * @param {String[]|patio.sql.Identifier} arr array of elements to make a condition specifier out of
    3713. -
    3714. * @param defaultOp the value to assign a value if one is not provided.
    3715. +
    3716. * Returnes a cloned dataset with the IS FALSE boolean expression added to the WHERE
    3717. +
    3718. * clause.
    3719. +
    3720. *
    3721. +
    3722. * @example
    3723. +
    3724. *
    3725. +
    3726. * DB.from("test").isFalse("boolFlag");
    3727. +
    3728. * => SELECT * FROM test WHERE (boolFlag IS FALSE);
    3729. +
    3730. * DB.from("test").isFalse("boolFlag", "otherFlag");
    3731. +
    3732. * => SELECT * FROM test WHERE (boolFlag IS FALSE AND otherFlag IS FALSE);
    3733. +
    3734. * @param {String...} arr variable number of arguments to create an IS FALSE expression for.
    3735. +
    3736. *
    3737. +
    3738. * @return {patio.Dataset} a cloned dataset with the IS FALSE expression added to the WHERE clause.
    3739. *
    3740. -
    3741. * @return { [[]] } an array of two element arrays.
    3742. */
    3743. -
    3744. __arrayToConditionSpecifier: function (arr, defaultOp) {
    3745. -
    3746. 22 var ret = [];
    3747. -
    3748. 22 arr.forEach(function (a) {
    3749. -
    3750. 28 if (isString(a)) {
    3751. -
    3752. 18 a = this.stringToIdentifier(a);
    3753. -
    3754. }
    3755. -
    3756. 28 if (isInstanceOf(a, Identifier)) {
    3757. -
    3758. 18 ret.push([a, defaultOp]);
    3759. -
    3760. 10 } else if (isHash(a)) {
    3761. -
    3762. 4 ret = ret.concat(array.toArray(a));
    3763. -
    3764. } else {
    3765. -
    3766. 6 throw new QueryError("Invalid condition specifier " + a);
    3767. -
    3768. }
    3769. -
    3770. }, this);
    3771. -
    3772. 16 return ret;
    3773. +
    3774. isFalse: function (arr) {
    3775. +
    3776. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);
    3777. +
    3778. 2 return this.filter(this.__createBoolExpression("is", arr));
    3779. },
    3780. /**
    3781. -
    3782. * @private
    3783. *
    3784. -
    3785. * SQL expression object based on the expr type. See {@link patio.Dataset#filter}
    3786. +
    3787. * Returns a cloned dataset with a greater than or equal to expression added to the WHERE
    3788. +
    3789. * clause.
    3790. +
    3791. *
    3792. +
    3793. * @example
    3794. +
    3795. * DB.from("test").gte({x : 1});
    3796. +
    3797. * //=> SELECT * FROM test WHERE (x >= 1)
    3798. +
    3799. * DB.from("test").gte({x : 1, y : 10});
    3800. +
    3801. * //=> SELECT * FROM test WHERE ((x >= 1) AND (y >= 10))
    3802. +
    3803. *
    3804. +
    3805. * @param {Object} obj object used to create the greater than or equal to expression.
    3806. +
    3807. *
    3808. +
    3809. * @return {patio.Dataset} a cloned dataset with the greater than or equal to expression added to the WHERE clause.
    3810. */
    3811. -
    3812. _filterExpr: function (expr, cb, joinCond) {
    3813. -
    3814. 4283 expr = (isUndefined(expr) || isNull(expr) || (isArray(expr) && !expr.length)) ? null : expr;
    3815. -
    3816. 4283 if (expr && cb) {
    3817. -
    3818. 1 return new BooleanExpression(joinCond || "AND", this._filterExpr(expr, null, joinCond), this._filterExpr(cb, null, joinCond))
    3819. -
    3820. 4282 } else if (cb) {
    3821. -
    3822. 58 expr = cb
    3823. -
    3824. }
    3825. -
    3826. 4282 if (isInstanceOf(expr, Expression)) {
    3827. -
    3828. 459 if (isInstanceOf(expr, NumericExpression, StringExpression)) {
    3829. -
    3830. 2 throw new QueryError("Invalid SQL Expression type : " + expr);
    3831. -
    3832. }
    3833. -
    3834. 457 return expr;
    3835. -
    3836. 3823 } else if (isArray(expr)) {
    3837. -
    3838. 740 if (expr.length) {
    3839. -
    3840. 740 var first = expr[0];
    3841. -
    3842. 740 if (isString(first)) {
    3843. -
    3844. 25 return new PlaceHolderLiteralString(first, expr.slice(1), true);
    3845. -
    3846. 715 } else if (Expression.isConditionSpecifier(expr)) {
    3847. -
    3848. 708 return BooleanExpression.fromValuePairs(expr, joinCond)
    3849. -
    3850. } else {
    3851. -
    3852. 7 return BooleanExpression.fromArgs([joinCond || "AND"].concat(expr.map(function (e) {
    3853. -
    3854. 15 return this._filterExpr(e, null, joinCond);
    3855. -
    3856. }, this)));
    3857. -
    3858. }
    3859. -
    3860. }
    3861. -
    3862. 3083 } else if (isFunction(expr)) {
    3863. -
    3864. 59 return this._filterExpr(expr.call(sql, sql), null, joinCond);
    3865. -
    3866. 3024 } else if (isBoolean(expr)) {
    3867. -
    3868. 7 return new BooleanExpression("NOOP", expr);
    3869. -
    3870. 3017 } else if (isString(expr)) {
    3871. -
    3872. 1 return this.stringToIdentifier(expr);
    3873. -
    3874. 3016 } else if (isInstanceOf(expr, LiteralString)) {
    3875. -
    3876. 16 return new LiteralString("(" + expr + ")");
    3877. -
    3878. 3000 } else if (isHash(expr)) {
    3879. -
    3880. 2999 return BooleanExpression.fromValuePairs(expr, joinCond);
    3881. -
    3882. } else {
    3883. -
    3884. 1 throw new QueryError("Invalid filter argument");
    3885. -
    3886. }
    3887. +
    3888. gte: function (arr) {
    3889. +
    3890. 2 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "gte");
    3891. +
    3892. 2 return this.filter(this.__createBoolExpression("gte", arr));
    3893. },
    3894. -
    3895. -
    3896. /**@ignore*/
    3897. -
    3898. getters: {
    3899. -
    3900. -
    3901. /**
    3902. -
    3903. * @ignore
    3904. -
    3905. * Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
    3906. -
    3907. */
    3908. -
    3909. isSimpleSelectAll: function () {
    3910. -
    3911. 33 var o = {}, opts = this.__opts, count = 0;
    3912. -
    3913. 33 for (var i in opts) {
    3914. -
    3915. 70 if (opts[i] != null && this._static.NON_SQL_OPTIONS.indexOf(i) == -1) {
    3916. -
    3917. 37 o[i] = opts[i];
    3918. -
    3919. 37 count++;
    3920. -
    3921. }
    3922. -
    3923. }
    3924. -
    3925. 33 var f = o.from
    3926. -
    3927. 33 return count == 1 && f.length == 1 && (isString(f[0]) || isInstanceOf(f[0], AliasedExpression, Identifier));
    3928. -
    3929. }
    3930. -
    3931. }
    3932. -
    3933. },
    3934. -
    3935. -
    3936. static: {
    3937. -
    3938. /**@lends patio.Dataset*/
    3939. -
    3940. -
    3941. /**
    3942. -
    3943. * These strings have {name}Join methods created (e.g. {@link patio.Dataset#innerJoin}) that
    3944. -
    3945. * call {@link patio.Dataset#joinTable} with the string, passing along the arguments and
    3946. -
    3947. * block from the method call.
    3948. -
    3949. **/
    3950. -
    3951. CONDITIONED_JOIN_TYPES: ["inner", "fullOuter", "rightOuter", "leftOuter", "full", "right", "left"],
    3952. -
    3953. /**
    3954. *
    3955. -
    3956. * These strings have {name}Join methods created (e.g. naturalJoin) that
    3957. -
    3958. * call {@link patio.Dataset#joinTable}. They only accept a single table
    3959. -
    3960. * argument which is passed to {@link patio.Dataset#joinTable}, and they throw an error
    3961. -
    3962. * if called with a block.
    3963. -
    3964. **/
    3965. -
    3966. UNCONDITIONED_JOIN_TYPES: ["natural", "naturalLeft", "naturalRight", "naturalFull", "cross"],
    3967. -
    3968. -
    3969. /**
    3970. -
    3971. * The dataset options that require the removal of cached columns
    3972. -
    3973. * if changed.
    3974. -
    3975. */
    3976. -
    3977. COLUMN_CHANGE_OPTS: ["select", "sql", "from", "join"],
    3978. -
    3979. -
    3980. /**
    3981. -
    3982. * Which options don't affect the SQL generation. Used by {@link patio.Dataset#simpleSelectAll}
    3983. -
    3984. * to determine if this is a simple SELECT * FROM table.
    3985. -
    3986. */
    3987. -
    3988. NON_SQL_OPTIONS: ["server", "defaults", "overrides", "graph", "eagerGraph", "graphAliases"],
    3989. -
    3990. -
    3991. -
    3992. /**
    3993. -
    3994. * All methods that return modified datasets with a joined table added.
    3995. +
    3996. * Returns a cloned dataset with a less than or equal to expression added to the WHERE
    3997. +
    3998. * clause.
    3999. +
    4000. *
    4001. +
    4002. * @example
    4003. +
    4004. * DB.from("test").gte({x : 1});
    4005. +
    4006. * //=> SELECT * FROM test WHERE (x <= 1)
    4007. +
    4008. * DB.from("test").gte({x : 1, y : 10});
    4009. +
    4010. * //=> SELECT * FROM test WHERE ((x <= 1) AND (y <= 10))
    4011. +
    4012. *
    4013. +
    4014. * @param {Object} obj object used to create the less than or equal to expression.
    4015. +
    4016. *
    4017. +
    4018. * @return {patio.Dataset} a cloned dataset with the less than or equal to expression added to the WHERE clause.
    4019. */
    4020. -
    4021. JOIN_METHODS: ["join", "joinTable"],
    4022. +
    4023. lte: function (obj) {
    4024. +
    4025. 2 var arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "lte");
    4026. +
    4027. 2 return this.filter(this.__createBoolExpression("lte", obj));
    4028. +
    4029. },
    4030. /**
    4031. -
    4032. * Methods that return modified datasets
    4033. -
    4034. */
    4035. -
    4036. QUERY_METHODS: ['addGraphAliases', "and", "distinct", "except", "exclude", "filter", "find", "is", "isNot",
    4037. -
    4038. "eq", "neq", "lt", "lte", "gt", "gte", "forUpdate", "from", "fromSelf", "graph", "grep", "group",
    4039. -
    4040. "groupAndCount", "groupBy", "having", "intersect", "invert", "limit", "lockStyle", "naked", "or", "order",
    4041. -
    4042. "orderAppend", "orderBy", "orderMore", "orderPrepend", "qualify", "reverse",
    4043. -
    4044. "reverseOrder", "select", "selectAll", "selectAppend", "selectMore", "setDefaults",
    4045. -
    4046. "setGraphAliases", "setOverrides", "unfiltered", "ungraphed", "ungrouped", "union", "unlimited",
    4047. -
    4048. "unordered", "where", "with", "withRecursive", "withSql"],
    4049. -
    4050. -
    4051. init: function () {
    4052. -
    4053. 35 this._super(arguments);
    4054. -
    4055. //initialize our join methods array
    4056. -
    4057. 35 var joinMethods = this.JOIN_METHODS;
    4058. -
    4059. 35 var queryMethods = this.QUERY_METHODS;
    4060. -
    4061. 35 this.UNCONDITIONED_JOIN_TYPES.forEach(function (joinType) {
    4062. -
    4063. 175 var m = joinType + "Join";
    4064. -
    4065. 175 joinMethods.push(m);
    4066. -
    4067. 175 queryMethods.push(m);
    4068. -
    4069. });
    4070. -
    4071. 35 this.CONDITIONED_JOIN_TYPES.forEach(function (joinType) {
    4072. -
    4073. 245 var m = joinType + "Join";
    4074. -
    4075. 245 joinMethods.push(m);
    4076. -
    4077. 245 queryMethods.push(m);
    4078. -
    4079. });
    4080. -
    4081. 35 this.QUERY_METHODS = queryMethods.concat(joinMethods);
    4082. -
    4083. }
    4084. -
    4085. }
    4086. -
    4087. }).
    4088. -
    4089. as(module);
    -
    - -
    - - - - - -
    -
    plugins/validation.js
    -
    -
    - Coverage97.66 - SLOC535 - LOC128 - Missed3 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. array = comb.array,
    4. -
    5. compact = array.compact,
    6. -
    7. flatten = array.flatten,
    8. -
    9. toArray = array.toArray,
    10. -
    11. net = require("net"),
    12. -
    13. isIP = net.isIP,
    14. -
    15. isIPv4 = net.isIPv4,
    16. -
    17. isIPv6 = net.isIPv6,
    18. -
    19. validator = require("validator"),
    20. -
    21. validatorCheck = validator.check,
    22. -
    23. dateCmp = comb.date.compare,
    24. -
    25. isArray = comb.isArray,
    26. -
    27. combDeepEqual = comb.deepEqual,
    28. -
    29. combIsBoolean = comb.isBoolean,
    30. -
    31. isString = comb.isString,
    32. -
    33. combIsDefined = comb.isDefined,
    34. -
    35. combIsNull = comb.isNull,
    36. -
    37. ModelError = require("../errors.js").ModelError,
    38. -
    39. isFunction = comb.isFunction,
    40. -
    41. format = comb.string.format,
    42. -
    43. Promise = comb.Promise,
    44. -
    45. serial = comb.serial,
    46. -
    47. when = comb.when,
    48. -
    49. merge = comb.merge,
    50. -
    51. define = comb.define;
    52. -
    53. -
    54. 1var Validator = define(null, {
    55. -
    56. instance:{
    57. +
    58. * Returns a cloned dataset with a between clause added
    59. +
    60. * to the where clause.
    61. +
    62. *
    63. +
    64. * @example
    65. +
    66. * ds.notBetween({x:[1, 2]}).sql;
    67. +
    68. * //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))
    69. +
    70. *
    71. +
    72. * ds.find({x:{notBetween:[1, 2]}}).sql;
    73. +
    74. * //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))
    75. +
    76. * @param {Object} obj object where the key is the column and the value is an array where the first element
    77. +
    78. * is the item to be greater than or equal to than and the second item is less than or equal to than.
    79. +
    80. *
    81. +
    82. * @return {patio.Dataset} a cloned dataset with a between clause added
    83. +
    84. * to the where clause.
    85. +
    86. */
    87. +
    88. between: function (obj) {
    89. +
    90. 2 return this.filter(this.__createBetweenExpression(obj));
    91. +
    92. },
    93. -
    94. constructor:function validator(col) {
    95. -
    96. 44 this.col = col;
    97. -
    98. 44 this.__actions = [];
    99. +
    100. /**
    101. +
    102. * Returns a cloned dataset with a not between clause added
    103. +
    104. * to the where clause.
    105. +
    106. *
    107. +
    108. * @example
    109. +
    110. * ds.notBetween({x:[1, 2]}).sql;
    111. +
    112. * //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))
    113. +
    114. *
    115. +
    116. * ds.find({x:{notBetween:[1, 2]}}).sql;
    117. +
    118. * //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))
    119. +
    120. * @param {Object} obj object where the key is the column and the value is an array where the first element
    121. +
    122. * is the item to be less than and the second item is greater than.
    123. +
    124. *
    125. +
    126. * @return {patio.Dataset} a cloned dataset with a not between clause added
    127. +
    128. * to the where clause.
    129. +
    130. */
    131. +
    132. notBetween: function (obj) {
    133. +
    134. 2 return this.filter(this.__createBetweenExpression(obj, true));
    135. },
    136. -
    137. __addAction:function __addAction(action, opts) {
    138. -
    139. 46 this.__actions.push({
    140. -
    141. action:action,
    142. -
    143. opts:merge({onlyDefined:true, onlyNotNull:false}, opts)
    144. -
    145. });
    146. -
    147. 46 return this;
    148. +
    149. /**
    150. +
    151. * Returns a cloned dataset with the given lock style. If style is a
    152. +
    153. * string, it will be used directly.Currently "update" is respected
    154. +
    155. * by most databases, and "share" is supported by some.
    156. +
    157. *
    158. +
    159. * @example
    160. +
    161. * DB.from("items").lockStyle('FOR SHARE') # SELECT * FROM items FOR SHARE
    162. +
    163. *
    164. +
    165. * @param {String} style the lock style to use.
    166. +
    167. *
    168. +
    169. * @return {patio.Dataset} a cloned datase with the given lock style.
    170. +
    171. **/
    172. +
    173. lockStyle: function (style) {
    174. +
    175. 4 return this.mergeOptions({lock: style});
    176. },
    177. -
    178. isAfter:function (date, opts) {
    179. -
    180. 1 opts = opts || {};
    181. -
    182. 1 var cmpDate = combIsBoolean(opts.date) ? opts.date : true;
    183. -
    184. 1 return this.__addAction(function (col) {
    185. -
    186. 3 return dateCmp(col, date, cmpDate ? "date" : "datetime") > 0;
    187. -
    188. }, merge({message:"{col} must be after " + date + " got {val}."}, opts));
    189. +
    190. /**
    191. +
    192. * Returns a copy of the dataset with the order changed. If the dataset has an
    193. +
    194. * existing order, it is ignored and overwritten with this order. If null is given
    195. +
    196. * the returned dataset has no order. This can accept multiple arguments
    197. +
    198. * of varying kinds, such as SQL functions. This also takes a function similar
    199. +
    200. * to {@link patio.Dataset#filter}
    201. +
    202. *
    203. +
    204. * @example
    205. +
    206. *
    207. +
    208. * DB.from("items").order("name")
    209. +
    210. * //=> SELECT * FROM items ORDER BY name
    211. +
    212. *
    213. +
    214. * DB.from("items").order("a", "b")
    215. +
    216. * //=> SELECT * FROM items ORDER BY a, b
    217. +
    218. *
    219. +
    220. * DB.from("items").order(sql.literal('a + b'))
    221. +
    222. * //=> SELECT * FROM items ORDER BY a + b
    223. +
    224. *
    225. +
    226. * DB.from("items").order(sql.identifier("a").plus("b"))
    227. +
    228. * //=> SELECT * FROM items ORDER BY (a + b)
    229. +
    230. *
    231. +
    232. * DB.from("items").order(sql.identifier("name").desc())
    233. +
    234. * //=> SELECT * FROM items ORDER BY name DESC
    235. +
    236. *
    237. +
    238. * DB.from("items").order(sql.identifier("name").asc({nulls : "last"))
    239. +
    240. * //=> SELECT * FROM items ORDER BY name ASC NULLS LAST
    241. +
    242. *
    243. +
    244. * DB.from("items").order(function(){
    245. +
    246. * return this.sum("name").desc();
    247. +
    248. * }); //=> SELECT * FROM items ORDER BY sum(name) DESC
    249. +
    250. *
    251. +
    252. * DB.from("items").order(null)
    253. +
    254. * //=>SELECT * FROM items
    255. +
    256. * @param arg variable number of arguments similar to {@link patio.Dataset#filter}
    257. +
    258. *
    259. +
    260. * @return {patio.Dataset} a cloned dataset with the order changed.
    261. +
    262. * */
    263. +
    264. order: function (args) {
    265. +
    266. 381 args = argsToArray(arguments);
    267. +
    268. 381 var order = [];
    269. +
    270. 381 args = compact(args).length ? args : null;
    271. +
    272. 381 if (args) {
    273. +
    274. 263 args.forEach(function (a) {
    275. +
    276. 326 if (isString(a)) {
    277. +
    278. 204 order.push(this.stringToIdentifier(a));
    279. +
    280. 122 } else if (isFunction(a)) {
    281. +
    282. 16 var res = a.apply(sql, [sql]);
    283. +
    284. 16 order = order.concat(isArray(res) ? res : [res]);
    285. +
    286. } else {
    287. +
    288. 106 order.push(a);
    289. +
    290. }
    291. +
    292. }, this);
    293. +
    294. } else {
    295. +
    296. 118 order = null;
    297. +
    298. }
    299. +
    300. 381 return this.mergeOptions({order: order});
    301. },
    302. -
    303. isBefore:function (date, opts) {
    304. -
    305. 1 opts = opts || {};
    306. -
    307. 1 var cmpDate = combIsBoolean(opts.date) ? opts.date : true;
    308. -
    309. 1 return this.__addAction(function (col) {
    310. -
    311. 3 return dateCmp(col, date, cmpDate ? "date" : "datetime") === -1;
    312. -
    313. }, merge({message:"{col} must be before " + date + " got {val}."}, opts));
    314. +
    315. /**
    316. +
    317. * Alias of {@link patio.Dataset#orderMore};
    318. +
    319. */
    320. +
    321. orderAppend: function () {
    322. +
    323. 4 return this.orderMore.apply(this, arguments);
    324. },
    325. -
    326. isDefined:function isDefined(opts) {
    327. -
    328. 1 return this.__addAction(function (col) {
    329. -
    330. 3 return combIsDefined(col);
    331. -
    332. }, merge({message:"{col} must be defined.", onlyDefined:false, onlyNotNull:false}, opts));
    333. +
    334. /**
    335. +
    336. * @see patio.Dataset#order
    337. +
    338. */
    339. +
    340. orderBy: function () {
    341. +
    342. 6 return this.order.apply(this, arguments);
    343. },
    344. -
    345. isNotDefined:function isNotDefined(opts) {
    346. -
    347. 1 return this.__addAction(function (col) {
    348. -
    349. 3 return !combIsDefined(col);
    350. -
    351. }, merge({message:"{col} cannot be defined.", onlyDefined:false, onlyNotNull:false}, opts));
    352. +
    353. /**
    354. +
    355. * Returns a copy of the dataset with the order columns added
    356. +
    357. * to the end of the existing order. For more detail
    358. +
    359. * @see patio.Dataset#order
    360. +
    361. *
    362. +
    363. * @example
    364. +
    365. *
    366. +
    367. * DB.from("items").order("a").order("b");
    368. +
    369. * //=> SELECT * FROM items ORDER BY b
    370. +
    371. *
    372. +
    373. * DB.from("items").order("a").orderMore("b");
    374. +
    375. * //=>SELECT * FROM items ORDER BY a, b
    376. +
    377. */
    378. +
    379. orderMore: function () {
    380. +
    381. 11 var args = argsToArray(arguments);
    382. +
    383. 11 if (this.__opts.order) {
    384. +
    385. 9 args = this.__opts.order.concat(args);
    386. +
    387. }
    388. +
    389. 11 return this.order.apply(this, args);
    390. },
    391. -
    392. isNotNull:function isNotNull(opts) {
    393. -
    394. 3 return this.__addAction(function (col) {
    395. -
    396. 21 return combIsDefined(col) && !combIsNull(col);
    397. -
    398. }, merge({message:"{col} cannot be null.", onlyDefined:false, onlyNotNull:false}, opts));
    399. +
    400. /**
    401. +
    402. * Returns a copy of the dataset with the order columns added
    403. +
    404. * to the beginning of the existing order. For more detail
    405. +
    406. * @see patio.Dataset#order
    407. +
    408. *
    409. +
    410. * @example
    411. +
    412. * DB.from("items").order("a").order("b");
    413. +
    414. * //=> SELECT * FROM items ORDER BY b
    415. +
    416. *
    417. +
    418. * DB.from("items").order("a").orderPrepend("b");
    419. +
    420. * //=>SELECT * FROM items ORDER BY b, a
    421. +
    422. *
    423. +
    424. *
    425. +
    426. **/
    427. +
    428. orderPrepend: function () {
    429. +
    430. 4 var ds = this.order.apply(this, arguments);
    431. +
    432. 4 return this.__opts.order ? ds.orderMore.apply(ds, this.__opts.order) : ds;
    433. },
    434. -
    435. isNull:function isNull(opts) {
    436. -
    437. 1 return this.__addAction(function (col) {
    438. -
    439. 3 return !combIsDefined(col) || combIsNull(col);
    440. -
    441. }, merge({message:"{col} must be null got {val}.", onlyDefined:false, onlyNotNull:false}, opts));
    442. +
    443. /**
    444. +
    445. * Qualify to the given table, or {@link patio.Dataset#firstSourceAlias} if not table is given.
    446. +
    447. *
    448. +
    449. * @example
    450. +
    451. * DB.from("items").filter({id : 1}).qualify();
    452. +
    453. * //=> SELECT items.* FROM items WHERE (items.id = 1)
    454. +
    455. *
    456. +
    457. * DB.from("items").filter({id : 1}).qualify("i");
    458. +
    459. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    460. +
    461. *
    462. +
    463. * @param {String|patio.sql.Identifier} [table={@link patio.Dataset#firstSourceAlias}] the table name to qualify to.
    464. +
    465. *
    466. +
    467. * @return {patio.Dataset} a cloned dataset qualified to the table or {@link patio.Dataset#firstSourceAlias}
    468. +
    469. **/
    470. +
    471. qualify: function (table) {
    472. +
    473. 19 table = table || this.firstSourceAlias;
    474. +
    475. 19 return this.qualifyTo(table);
    476. },
    477. -
    478. isEq:function eq(val, opts) {
    479. -
    480. 4 return this.__addAction(function (col) {
    481. -
    482. 15 return combDeepEqual(col, val);
    483. -
    484. }, merge({message:"{col} must === " + val + " got {val}."}, opts));
    485. +
    486. /**
    487. +
    488. * Qualify the dataset to its current first source(first from clause). This is useful
    489. +
    490. * if you have unqualified identifiers in the query that all refer to
    491. +
    492. * the first source, and you want to join to another table which
    493. +
    494. * has columns with the same name as columns in the current dataset.
    495. +
    496. * See {@link patio.Dataset#qualifyTo}
    497. +
    498. *
    499. +
    500. * @example
    501. +
    502. *
    503. +
    504. * DB.from("items").filter({id : 1}).qualifyToFirstSource();
    505. +
    506. * //=> SELECT items.* FROM items WHERE (items.id = 1)
    507. +
    508. *
    509. +
    510. * @return {patio.Dataset} a cloned dataset that is qualified with the first source.
    511. +
    512. * */
    513. +
    514. qualifyToFirstSource: function () {
    515. +
    516. 18 return this.qualifyTo(this.firstSourceAlias);
    517. },
    518. -
    519. isNeq:function neq(val, opts) {
    520. -
    521. 2 return this.__addAction(function (col) {
    522. -
    523. 8 return !combDeepEqual(col, val);
    524. -
    525. }, merge({message:"{col} must !== " + val + "."}, opts));
    526. +
    527. /**
    528. +
    529. * Return a copy of the dataset with unqualified identifiers in the
    530. +
    531. * SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the
    532. +
    533. * given table. If no columns are currently selected, select all
    534. +
    535. * columns of the given table.
    536. +
    537. *
    538. +
    539. * @example
    540. +
    541. * DB.from("items").filter({id : 1}).qualifyTo("i");
    542. +
    543. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    544. +
    545. *
    546. +
    547. * @param {String} table the name to qualify identifier to.
    548. +
    549. *
    550. +
    551. * @return {patio.Dataset} a cloned dataset with unqualified identifiers qualified.
    552. +
    553. */
    554. +
    555. qualifyTo: function (table) {
    556. +
    557. 40 var o = this.__opts;
    558. +
    559. 40 if (o.sql) {
    560. +
    561. 2 return this.mergeOptions();
    562. +
    563. }
    564. +
    565. 38 var h = {};
    566. +
    567. 38 array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) {
    568. +
    569. 65 h[k] = this._qualifiedExpression(o[k], table);
    570. +
    571. }, this);
    572. +
    573. 38 if (!o.select || isEmpty(o.select)) {
    574. +
    575. 14 h.select = [new ColumnAll(table)];
    576. +
    577. }
    578. +
    579. 38 return this.mergeOptions(h);
    580. },
    581. -
    582. isLike:function like(val, opts) {
    583. -
    584. 3 return this.__addAction(function (col) {
    585. -
    586. 14 return !!col.match(val);
    587. -
    588. }, merge({message:"{col} must be like " + val + " got {val}."}, opts));
    589. +
    590. /**
    591. +
    592. * Same as {@link patio.Dataset@qualifyTo} except that it forces qualification on methods called
    593. +
    594. * after it has been called.
    595. +
    596. *
    597. +
    598. * @example
    599. +
    600. *
    601. +
    602. * //qualfyTo would generate
    603. +
    604. * DB.from("items").qualifyTo("i").filter({id : 1});
    605. +
    606. * //=> SELECT i.* FROM items WHERE (id = 1)
    607. +
    608. *
    609. +
    610. * //alwaysQualify qualifies filter also.
    611. +
    612. * DB.from("items").alwaysQualify("i").filter({id : 1});
    613. +
    614. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    615. +
    616. *
    617. +
    618. *
    619. +
    620. * @param {String|patio.sql.Identifier} [table=this.firstSourceAlias] the table to qualify to.
    621. +
    622. * @return {patio.Dataset} a cloned dataset that will always qualify.
    623. +
    624. */
    625. +
    626. alwaysQualify: function (table) {
    627. +
    628. 3 return this.mergeOptions({alwaysQualify: table || this.firstSourceAlias});
    629. },
    630. -
    631. isNotLike:function notLike(val, opts) {
    632. -
    633. 2 return this.__addAction(function (col) {
    634. -
    635. 6 return !(!!col.match(val));
    636. -
    637. }, merge({message:"{col} must not be like " + val + "."}, opts));
    638. -
    639. },
    640. -
    641. isLt:function lt(num, opts) {
    642. -
    643. 1 return this.__addAction(function (col) {
    644. -
    645. 3 return col < num;
    646. -
    647. }, merge({message:"{col} must be < " + num + " got {val}."}, opts));
    648. +
    649. /**
    650. +
    651. * Returns a copy of the dataset with the order reversed. If no order is
    652. +
    653. * given, the existing order is inverted.
    654. +
    655. *
    656. +
    657. * @example
    658. +
    659. * DB.from("items").reverse("id");
    660. +
    661. * //=> SELECT * FROM items ORDER BY id DESC
    662. +
    663. *
    664. +
    665. * DB.from("items").order("id").reverse();
    666. +
    667. * //=> SELECT * FROM items ORDER BY id DESC
    668. +
    669. *
    670. +
    671. * DB.from("items").order("id").reverse(sql.identifier("name").asc);
    672. +
    673. * //=> SELECT * FROM items ORDER BY name ASC
    674. +
    675. *
    676. +
    677. * @param {String|patio.sql.Identifier|Function} args variable number of columns add to order before reversing.
    678. +
    679. *
    680. +
    681. * @return {patio.Dataset} a cloned dataset with the order reversed.
    682. +
    683. *
    684. +
    685. **/
    686. +
    687. reverse: function (args) {
    688. +
    689. 46 args = argsToArray(arguments);
    690. +
    691. 46 return this.order.apply(this, this._invertOrder(args.length ? args : this.__opts.order));
    692. },
    693. -
    694. isGt:function gt(num, opts) {
    695. -
    696. 1 return this.__addAction(function (col) {
    697. -
    698. 3 return col > num;
    699. -
    700. }, merge({message:"{col} must be > " + num + " got {val}."}, opts));
    701. +
    702. /**
    703. +
    704. * @see patio.Dataset#reverse
    705. +
    706. */
    707. +
    708. reverseOrder: function () {
    709. +
    710. 16 return this.reverse.apply(this, arguments);
    711. },
    712. -
    713. isLte:function lte(num, opts) {
    714. -
    715. 1 return this.__addAction(function (col) {
    716. -
    717. 3 return col <= num;
    718. -
    719. }, merge({message:"{col} must be <= " + num + " got {val}."}, opts));
    720. -
    721. },
    722. +
    723. /**
    724. +
    725. * Returns a copy of the dataset with the columns selected changed
    726. +
    727. * to the given columns. This also takes a function similar to {@link patio.Dataset#filter}
    728. +
    729. *
    730. +
    731. * @example
    732. +
    733. * DB.from("items").select("a");
    734. +
    735. * //=> SELECT a FROM items
    736. +
    737. *
    738. +
    739. * DB.from("items").select("a", "b");
    740. +
    741. * //=> SELECT a, b FROM items
    742. +
    743. *
    744. +
    745. * DB.from("items").select("a", function(){
    746. +
    747. * return this.sum("b")
    748. +
    749. * }).sql; //=> SELECT a, sum(b) FROM items
    750. +
    751. *
    752. +
    753. * @param {String|patio.sql.Identifier|Function} args variable number of colums to select
    754. +
    755. * @return {patio.Dataset} a cloned dataset with the columns selected changed.
    756. +
    757. */
    758. +
    759. select: function (args) {
    760. +
    761. 663 args = flatten(argsToArray(arguments));
    762. +
    763. 663 var columns = [];
    764. +
    765. 663 args.forEach(function (c) {
    766. +
    767. 1043 if (isFunction(c)) {
    768. +
    769. 23 var res = c.apply(sql, [sql]);
    770. +
    771. 23 columns = columns.concat(isArray(res) ? res : [res]);
    772. +
    773. } else {
    774. +
    775. 1020 columns.push(c);
    776. +
    777. }
    778. +
    779. });
    780. +
    781. 663 var select = [];
    782. +
    783. 663 columns.forEach(function (c) {
    784. +
    785. 1046 if (isHash(c)) {
    786. +
    787. 3 for (var i in c) {
    788. +
    789. 4 select.push(new AliasedExpression(new Identifier(i), c[i]));
    790. +
    791. }
    792. +
    793. 1043 } else if (isString(c)) {
    794. +
    795. 344 select.push(this.stringToIdentifier(c));
    796. +
    797. } else {
    798. +
    799. 699 select.push(c);
    800. +
    801. }
    802. +
    803. }, this);
    804. +
    805. 663 return this.mergeOptions({select: select});
    806. -
    807. isGte:function gte(num, opts) {
    808. -
    809. 1 return this.__addAction(function (col) {
    810. -
    811. 3 return col >= num;
    812. -
    813. }, merge({message:"{col} must be >= " + num + " got {val}."}, opts));
    814. },
    815. -
    816. isIn:function isIn(arr, opts) {
    817. -
    818. 2 if (!isArray(arr)) {
    819. -
    820. 1 throw new Error("isIn requires an array of values");
    821. -
    822. }
    823. -
    824. 1 return this.__addAction(function (col) {
    825. -
    826. 3 return arr.indexOf(col) !== -1;
    827. -
    828. }, merge({message:"{col} must be in " + arr.join(",") + " got {val}."}, opts));
    829. +
    830. /**
    831. +
    832. * Returns a cloned dataset that selects *.
    833. +
    834. *
    835. +
    836. * @return {patio.Dataset} a cloned dataset that selects *.
    837. +
    838. */
    839. +
    840. selectAll: function () {
    841. +
    842. 6 return this.mergeOptions({select: null});
    843. },
    844. -
    845. isNotIn:function notIn(arr, opts) {
    846. -
    847. 2 if (!isArray(arr)) {
    848. -
    849. 1 throw new Error("notIn requires an array of values");
    850. +
    851. /**
    852. +
    853. * Selects the columns if only if there is not already select sources.
    854. +
    855. *
    856. +
    857. * @example
    858. +
    859. *
    860. +
    861. * var ds = DB.from("items"); //SELECT * FROM items
    862. +
    863. *
    864. +
    865. * ds.select("a"); //SELECT a FROM items;
    866. +
    867. * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
    868. +
    869. * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
    870. +
    871. *
    872. +
    873. * @param cols columns to select if there is not already select sources.
    874. +
    875. * @return {patio.Dataset} a cloned dataset with the appropriate select sources.
    876. +
    877. */
    878. +
    879. selectIfNoSource: function (cols) {
    880. +
    881. 0 var ret;
    882. +
    883. 0 if (!this.hasSelectSource) {
    884. +
    885. 0 ret = this.select.apply(this, arguments);
    886. +
    887. } else {
    888. +
    889. 0 ret = this.mergeOptions();
    890. }
    891. -
    892. 1 return this.__addAction(function (col) {
    893. -
    894. 3 return arr.indexOf(col) === -1;
    895. -
    896. }, merge({message:"{col} cannot be in " + arr.join(",") + " got {val}."}, opts));
    897. -
    898. },
    899. -
    900. -
    901. isMacAddress:function isMaxAddress(opts) {
    902. -
    903. 1 return this.__addAction(function (col) {
    904. -
    905. 4 return !!col.match(/^([0-9A-F]{2}[:\-]){5}([0-9A-F]{2})$/);
    906. -
    907. }, merge({message:"{col} must be a valid MAC address got {val}."}, opts));
    908. -
    909. },
    910. -
    911. -
    912. isIPAddress:function isIpAddress(opts) {
    913. -
    914. 1 return this.__addAction(function (col) {
    915. -
    916. 4 return !!isIP(col);
    917. -
    918. }, merge({message:"{col} must be a valid IPv4 or IPv6 address got {val}."}, opts));
    919. -
    920. },
    921. -
    922. -
    923. isIPv4Address:function isIpAddress(opts) {
    924. -
    925. 1 return this.__addAction(function (col) {
    926. -
    927. 3 return isIPv4(col);
    928. -
    929. }, merge({message:"{col} must be a valid IPv4 address got {val}."}, opts));
    930. -
    931. },
    932. -
    933. -
    934. isIPv6Address:function isIpAddress(opts) {
    935. -
    936. 1 return this.__addAction(function (col) {
    937. -
    938. 3 return isIPv6(col);
    939. -
    940. }, merge({message:"{col} must be a valid IPv6 address got {val}."}, opts));
    941. +
    942. 0 return ret;
    943. },
    944. -
    945. isUUID:function isUUID(opts) {
    946. -
    947. 1 return this.__addAction(function (col) {
    948. -
    949. 3 return !!col.match(/^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/);
    950. -
    951. }, merge({message:"{col} must be a valid UUID got {val}"}, opts));
    952. +
    953. /**
    954. +
    955. * Returns a copy of the dataset with the given columns added
    956. +
    957. * to the existing selected columns. If no columns are currently selected,
    958. +
    959. * it will select the columns given in addition to *.
    960. +
    961. *
    962. +
    963. * @example
    964. +
    965. * DB.from("items").select("a").selectAppend("b").sql;
    966. +
    967. * //=> SELECT b FROM items
    968. +
    969. *
    970. +
    971. * DB.from("items").select("a").selectAppend("b", "c", "d").sql
    972. +
    973. * //=> SELECT a, b, c, d FROM items
    974. +
    975. *
    976. +
    977. * DB.from("items").selectAppend("b").sql
    978. +
    979. * //=> SELECT *, b FROM items
    980. +
    981. *
    982. +
    983. * @param [...] cols variable number of columns to add to the select statement
    984. +
    985. *
    986. +
    987. * @return {patio.Dataset} returns a cloned dataset with the new select columns appended.
    988. +
    989. */
    990. +
    991. selectAppend: function (cols) {
    992. +
    993. 7 cols = argsToArray(arguments);
    994. +
    995. 7 var currentSelect = this.__opts.select;
    996. +
    997. 7 if (!currentSelect || !currentSelect.length) {
    998. +
    999. 3 currentSelect = [this._static.WILDCARD];
    1000. +
    1001. }
    1002. +
    1003. 7 return this.select.apply(this, currentSelect.concat(cols));
    1004. },
    1005. -
    1006. isEmail:function isEmail(opts) {
    1007. -
    1008. 1 return this.__addAction(function (col) {
    1009. -
    1010. 3 return validatorCheck(col).isEmail();
    1011. -
    1012. }, merge({message:"{col} must be a valid Email Address got {val}"}, opts));
    1013. +
    1014. /**
    1015. +
    1016. * Returns a copy of the dataset with the given columns added
    1017. +
    1018. * to the existing selected columns. If no columns are currently selected
    1019. +
    1020. * it will just select the columns given.
    1021. +
    1022. *
    1023. +
    1024. * @example
    1025. +
    1026. * DB.from("items").select("a").select("b").sql;
    1027. +
    1028. * //=> SELECT b FROM items
    1029. +
    1030. *
    1031. +
    1032. * DB.from("items").select("a").selectMore("b", "c", "d").sql
    1033. +
    1034. * //=> SELECT a, b, c, d FROM items
    1035. +
    1036. *
    1037. +
    1038. * DB.from("items").selectMore("b").sql
    1039. +
    1040. * //=> SELECT b FROM items
    1041. +
    1042. *
    1043. +
    1044. * @param [...] cols variable number of columns to add to the select statement
    1045. +
    1046. *
    1047. +
    1048. * @return {patio.Dataset} returns a cloned dataset with the new select columns appended.
    1049. +
    1050. */
    1051. +
    1052. selectMore: function (cols) {
    1053. +
    1054. 10 cols = argsToArray(arguments);
    1055. +
    1056. 10 var currentSelect = this.__opts.select;
    1057. +
    1058. 10 return this.select.apply(this, (currentSelect || []).concat(cols));
    1059. },
    1060. -
    1061. isUrl:function isUrl(opts) {
    1062. -
    1063. 1 return this.__addAction(function (col) {
    1064. -
    1065. 3 return validatorCheck(col).isUrl();
    1066. -
    1067. }, merge({message:"{col} must be a valid url got {val}"}, opts));
    1068. +
    1069. /**
    1070. +
    1071. * Set the default values for insert and update statements. The values hash passed
    1072. +
    1073. * to insert or update are merged into this hash, so any values in the hash passed
    1074. +
    1075. * to insert or update will override values passed to this method.
    1076. +
    1077. *
    1078. +
    1079. * @example
    1080. +
    1081. * DB.from("items").setDefaults({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();
    1082. +
    1083. * //=> INSERT INTO items (a, c, b) VALUES ('d', 'c', 'b')
    1084. +
    1085. *
    1086. +
    1087. * @param {Object} hash object with key value pairs to use as override values
    1088. +
    1089. *
    1090. +
    1091. * @return {patio.Dataset} a cloned dataset with the defaults added to the current datasets defaults.
    1092. +
    1093. */
    1094. +
    1095. setDefaults: function (hash) {
    1096. +
    1097. 5 return this.mergeOptions({defaults: merge({}, this.__opts.defaults || {}, hash)});
    1098. },
    1099. -
    1100. isAlpha:function isAlpha(opts) {
    1101. -
    1102. 2 return this.__addAction(function (col) {
    1103. -
    1104. 11 return validatorCheck(col).isAlpha();
    1105. -
    1106. }, merge({message:"{col} must be a only letters got {val}"}, opts));
    1107. +
    1108. /**
    1109. +
    1110. * Set values that override hash arguments given to insert and update statements.
    1111. +
    1112. * This hash is merged into the hash provided to insert or update, so values
    1113. +
    1114. * will override any values given in the insert/update hashes.
    1115. +
    1116. *
    1117. +
    1118. * @example
    1119. +
    1120. * DB.from("items").setOverrides({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();
    1121. +
    1122. * //=> INSERT INTO items (a, c, b) VALUES ('a', 'c', 'b')
    1123. +
    1124. *
    1125. +
    1126. * @param {Object} hash object with key value pairs to use as override values
    1127. +
    1128. *
    1129. +
    1130. * @return {patio.Dataset} a cloned dataset with the overrides added to the current datasets overrides.
    1131. +
    1132. */
    1133. +
    1134. setOverrides: function (hash) {
    1135. +
    1136. 5 return this.mergeOptions({overrides: merge({}, this.__opts.overrides || {}, hash)});
    1137. },
    1138. -
    1139. isAlphaNumeric:function isAlphaNumeric(opts) {
    1140. -
    1141. 1 return this.__addAction(function (col) {
    1142. -
    1143. 3 return validatorCheck(col).isAlphanumeric();
    1144. -
    1145. }, merge({message:"{col} must be a alphanumeric got {val}"}, opts));
    1146. +
    1147. /**
    1148. +
    1149. * Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
    1150. +
    1151. * @example
    1152. +
    1153. * DB.from("items").group("a").having({a : 1}).where("b").unfiltered().sql;
    1154. +
    1155. * //=> SELECT * FROM items GROUP BY a
    1156. +
    1157. *
    1158. +
    1159. * @return {patio.Dataset} a cloned dataset with no HAVING or WHERE clause.
    1160. +
    1161. */
    1162. +
    1163. unfiltered: function () {
    1164. +
    1165. 14 return this.mergeOptions({where: null, having: null});
    1166. },
    1167. -
    1168. hasLength:function hasLength(min, max, opts) {
    1169. -
    1170. 2 return this.__addAction(function (col) {
    1171. -
    1172. 6 return validatorCheck(col).len(min, max);
    1173. -
    1174. }, merge({message:"{col} must have a length between " + min + (max ? " and " + max : "") + "."}, opts));
    1175. +
    1176. /**
    1177. +
    1178. * Returns a copy of the dataset with no GROUP or HAVING clause.
    1179. +
    1180. *
    1181. +
    1182. * @example
    1183. +
    1184. * DB.from("t").group("a").having({a : 1}).where("b").ungrouped().sql;
    1185. +
    1186. * //=> SELECT * FROM t WHERE b
    1187. +
    1188. *
    1189. +
    1190. * @return {patio.Dataset} a cloned dataset with no GROUP or HAVING clause.
    1191. +
    1192. */
    1193. +
    1194. ungrouped: function () {
    1195. +
    1196. 1 return this.mergeOptions({group: null, having: null});
    1197. },
    1198. -
    1199. isLowercase:function isLowercase(opts) {
    1200. -
    1201. 1 return this.__addAction(function (col) {
    1202. -
    1203. 3 return validatorCheck(col).isLowercase();
    1204. -
    1205. }, merge({message:"{col} must be lowercase got {val}."}, opts));
    1206. +
    1207. /**
    1208. +
    1209. * Adds a UNION clause using a second dataset object.
    1210. +
    1211. * A UNION compound dataset returns all rows in either the current dataset
    1212. +
    1213. * or the given dataset.
    1214. +
    1215. * Options:
    1216. +
    1217. * :alias :: Use the given value as the from_self alias
    1218. +
    1219. * :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
    1220. +
    1221. * :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
    1222. +
    1223. *
    1224. +
    1225. * @example
    1226. +
    1227. * DB.from("items").union(DB.from("otherItems")).sql;
    1228. +
    1229. * //=> SELECT * FROM items UNION SELECT * FROM other_items
    1230. +
    1231. *
    1232. +
    1233. * DB.from("items").union(DB.from("otherItems"), {all : true, fromSelf : false}).sql;
    1234. +
    1235. * //=> SELECT * FROM items UNION ALL SELECT * FROM other_items
    1236. +
    1237. *
    1238. +
    1239. * DB.from("items").union(DB.from("otherItems"), {alias : "i"})
    1240. +
    1241. * //=> SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS i
    1242. +
    1243. *
    1244. +
    1245. * @param {patio.Dataset} dataset dataset to union with
    1246. +
    1247. * @param {Object} opts addional options
    1248. +
    1249. * @param {String|patio.sql.Identifier} [opts.alias] Alias to use as the fromSelf alias.
    1250. +
    1251. * @param {Boolean} [opt.all=false] Set to true to use UNION ALL instead of UNION so duplicate rows can occur
    1252. +
    1253. * @param {Boolean} [opts.fromSelf=true] Set to false to not wrap the returned dataset in a fromSelf.
    1254. +
    1255. *
    1256. +
    1257. * @return {patio.Dataset} a cloned dataset with the union.
    1258. +
    1259. *
    1260. +
    1261. */
    1262. +
    1263. union: function (dataset, opts) {
    1264. +
    1265. 21 opts = isUndefined(opts) ? {} : opts;
    1266. +
    1267. 21 if (!isHash(opts)) {
    1268. +
    1269. 3 opts = {all: opts};
    1270. +
    1271. }
    1272. +
    1273. 21 return this.compoundClone("union", dataset, opts);
    1274. },
    1275. -
    1276. isUppercase:function isUppercase(opts) {
    1277. -
    1278. 1 return this.__addAction(function (col) {
    1279. -
    1280. 3 return validatorCheck(col).isUppercase();
    1281. -
    1282. }, merge({message:"{col} must be uppercase got {val}."}, opts));
    1283. +
    1284. /**
    1285. +
    1286. * Returns a copy of the dataset with no limit or offset.
    1287. +
    1288. *
    1289. +
    1290. * @example
    1291. +
    1292. * DB.from("t").limit(10, 20).unlimited().sql;
    1293. +
    1294. * //=> SELECT * FROM t
    1295. +
    1296. *
    1297. +
    1298. * @return {patio.Dataset} a cloned dataset with no limit or offset.
    1299. +
    1300. */
    1301. +
    1302. unlimited: function () {
    1303. +
    1304. 1 return this.mergeOptions({limit: null, offset: null});
    1305. },
    1306. -
    1307. isEmpty:function isEmpty(opts) {
    1308. -
    1309. 1 return this.__addAction(function (col) {
    1310. -
    1311. 3 try {
    1312. -
    1313. 3 validatorCheck(col).notEmpty();
    1314. -
    1315. 2 return false;
    1316. -
    1317. } catch (e) {
    1318. -
    1319. 1 return true;
    1320. -
    1321. }
    1322. -
    1323. }, merge({message:"{col} must be empty got {val}."}, opts));
    1324. +
    1325. /**
    1326. +
    1327. * Returns a copy of the dataset with no order.
    1328. +
    1329. *
    1330. +
    1331. * @example
    1332. +
    1333. * DB.from("t").order("a", sql.identifier("b").desc()).unordered().sql;
    1334. +
    1335. * //=> SELECT * FROM t
    1336. +
    1337. *
    1338. +
    1339. * @return {patio.Dataset} a cloned dataset with no order.
    1340. +
    1341. */
    1342. +
    1343. unordered: function () {
    1344. +
    1345. 115 return this.order(null);
    1346. },
    1347. -
    1348. isNotEmpty:function isNotEmpty(opts) {
    1349. -
    1350. 2 return this.__addAction(function (col) {
    1351. -
    1352. 11 return validatorCheck(col).notEmpty();
    1353. -
    1354. }, merge({message:"{col} must not be empty."}, opts));
    1355. +
    1356. /**
    1357. +
    1358. * Add a condition to the WHERE clause. See {@link patio.Dataset#filter} for argument types.
    1359. +
    1360. *
    1361. +
    1362. * @example
    1363. +
    1364. * DB.from("test").where('price < ? AND id in ?', 100, [1, 2, 3]).sql;
    1365. +
    1366. * //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
    1367. +
    1368. * DB.from("test").where('price < {price} AND id in {ids}', {price:100, ids:[1, 2, 3]}).sql;
    1369. +
    1370. * //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))")
    1371. +
    1372. *
    1373. +
    1374. */
    1375. +
    1376. where: function () {
    1377. +
    1378. 252 return this._filter.apply(this, ["where"].concat(argsToArray(arguments)));
    1379. },
    1380. -
    1381. isCreditCard:function isCreditCard(opts) {
    1382. -
    1383. 0 return this.__addAction(function (col) {
    1384. -
    1385. 0 return validatorCheck(col).isCreditCard();
    1386. -
    1387. }, merge({message:"{col} is an invalid credit card"}, opts));
    1388. +
    1389. /**
    1390. +
    1391. * Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
    1392. +
    1393. * A common table expression acts as an inline view for the query.
    1394. +
    1395. *
    1396. +
    1397. * @name with
    1398. +
    1399. * @example
    1400. +
    1401. *
    1402. +
    1403. * DB.from("t")["with"]("t", db.from("x"))["with"]("j", db.from("y")).sql;
    1404. +
    1405. * //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y) SELECT * FROM t'
    1406. +
    1407. *
    1408. +
    1409. * DB.from("t")["with"]("t", db.from("x")).withRecursive("j", db.from("y"), db.from("j")).sql;
    1410. +
    1411. * //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t'
    1412. +
    1413. *
    1414. +
    1415. * DB.from("t")["with"]("t", db.from("x"), {args:["b"]}).sql;
    1416. +
    1417. * //=> 'WITH t(b) AS (SELECT * FROM x) SELECT * FROM t'
    1418. +
    1419. *
    1420. +
    1421. * @param {String} name the name of the to assign to the CTE.
    1422. +
    1423. * @param {patio.Dataset} dataset the dataset to use for the CTE.
    1424. +
    1425. * @param {Object} opts extra options.
    1426. +
    1427. * @param {String[]} [opts.args] colums/args for the CTE.
    1428. +
    1429. * @param {Boolean} [opts.recursive] set to true that the CTE is recursive.
    1430. +
    1431. *
    1432. +
    1433. * @return {patio.Dataset} a cloned dataset with the CTE.
    1434. +
    1435. */
    1436. +
    1437. "with": function (name, dataset, opts) {
    1438. +
    1439. 6 if (!this.supportsCte) {
    1440. +
    1441. 1 throw new QueryError("this dataset does not support common table expressions");
    1442. +
    1443. }
    1444. +
    1445. 5 return this.mergeOptions({
    1446. +
    1447. "with": (this.__opts["with"] || []).concat([merge(opts || {}, {name: this.stringToIdentifier(name), dataset: dataset})])
    1448. +
    1449. });
    1450. },
    1451. -
    1452. check:function (fun, opts) {
    1453. -
    1454. 4 return this.__addAction(fun, opts);
    1455. +
    1456. /**
    1457. +
    1458. * Add a recursive common table expression (CTE) with the given name, a dataset that
    1459. +
    1460. * defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
    1461. +
    1462. * of the CTE.
    1463. +
    1464. *
    1465. +
    1466. * @example
    1467. +
    1468. *
    1469. +
    1470. * //Sing withRecursive call.
    1471. +
    1472. * DB.from("t").withRecursive("t", db.from("x"), db.from("t")).sql;
    1473. +
    1474. * //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
    1475. +
    1476. *
    1477. +
    1478. * //Multiple withRecursive calls.
    1479. +
    1480. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"))
    1481. +
    1482. * .withRecursive("j", db.from("y"), db.from("j")).sql;
    1483. +
    1484. * //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t),
    1485. +
    1486. * j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t';
    1487. +
    1488. *
    1489. +
    1490. * //Adding args
    1491. +
    1492. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {args:["b", "c"]}).sql;
    1493. +
    1494. * //=> 'WITH t(b, c) AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
    1495. +
    1496. *
    1497. +
    1498. * //Setting union all to false
    1499. +
    1500. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {unionAll:false}).sql;
    1501. +
    1502. * //=> 'WITH t AS (SELECT * FROM x UNION SELECT * FROM t) SELECT * FROM t');
    1503. +
    1504. *
    1505. +
    1506. * @param {String} name the name to assign to the CTE
    1507. +
    1508. * @param {patio.Dataset} nonRecursive the non-recursive part of the CTE
    1509. +
    1510. * @param {patio.Dataset} recursive the recursive part of the CTE
    1511. +
    1512. * @param {Object} [opts={}] extra options
    1513. +
    1514. * @param {String[]} [opts.args] columns to include with the CTE
    1515. +
    1516. * @param {Boolena} [opts.unionAll] set to false to use UNION instead of UNION ALL when combining non recursive
    1517. +
    1518. * with recursive.
    1519. +
    1520. *
    1521. +
    1522. * @return {patio.Dataset} a cloned dataset with the CTE.
    1523. +
    1524. */
    1525. +
    1526. withRecursive: function (name, nonRecursive, recursive, opts) {
    1527. +
    1528. 7 if (!this.supportsCte) {
    1529. +
    1530. 1 throw new QueryError("This dataset does not support common table expressions");
    1531. +
    1532. }
    1533. +
    1534. 6 opts = opts || {};
    1535. +
    1536. 6 var wit = (this.__opts["with"] || []).concat([merge(opts, {recursive: true, name: this.stringToIdentifier(name), dataset: nonRecursive.union(recursive, {all: opts.unionAll != false, fromSelf: false})})]);
    1537. +
    1538. 6 return this.mergeOptions({"with": wit});
    1539. },
    1540. -
    1541. validate:function validate(value) {
    1542. -
    1543. 218 var errOpts = {col:this.col, val:value};
    1544. -
    1545. 218 return compact(this.__actions.map(function (action) {
    1546. -
    1547. 248 var actionOpts = action.opts;
    1548. -
    1549. 248 if (!actionOpts.onlyDefined || (combIsDefined(value) &&
    1550. -
    1551. (!actionOpts.onlyNotNull || !combIsNull(value)) )) {
    1552. -
    1553. 186 var ret = null;
    1554. -
    1555. 186 try {
    1556. -
    1557. 186 if (!action.action(value)) {
    1558. -
    1559. 69 ret = format(actionOpts.message, errOpts);
    1560. -
    1561. }
    1562. -
    1563. } catch (e) {
    1564. -
    1565. 28 ret = format(actionOpts.message, errOpts);
    1566. -
    1567. }
    1568. -
    1569. 186 return ret;
    1570. -
    1571. }
    1572. -
    1573. }, this));
    1574. -
    1575. }
    1576. -
    1577. -
    1578. }
    1579. -
    1580. });
    1581. -
    1582. -
    1583. 1function shouldValidate(opts, def) {
    1584. -
    1585. 115 opts = opts || {};
    1586. -
    1587. 115 return combIsBoolean(opts.validate) ? opts.validate : def;
    1588. -
    1589. }
    1590. -
    1591. -
    1592. 1function validateHook(prop, next, opts) {
    1593. -
    1594. 115 if (shouldValidate(opts, prop) && !this.isValid()) {
    1595. -
    1596. 45 next(flatten(toArray(this.errors).map(function (entry) {
    1597. -
    1598. 64 return entry[1].map(function (err) {
    1599. -
    1600. 48 return new Error(err);
    1601. -
    1602. });
    1603. -
    1604. })));
    1605. -
    1606. } else {
    1607. -
    1608. 70 next();
    1609. -
    1610. }
    1611. -
    1612. }
    1613. -
    1614. -
    1615. 1define(null, {
    1616. +
    1617. /**
    1618. +
    1619. * Returns a copy of the dataset with the static SQL used. This is useful if you want
    1620. +
    1621. * to keep the same {@link patio.Dataset#rowCb}/{@link patio.Dataset#graph},
    1622. +
    1623. * but change the SQL used to custom SQL.
    1624. +
    1625. *
    1626. +
    1627. * @example
    1628. +
    1629. * DB.from("items").withSql('SELECT * FROM foo')
    1630. +
    1631. * //=> SELECT * FROM foo
    1632. +
    1633. *
    1634. +
    1635. * @param {String} sql sql for the dataset to use.
    1636. +
    1637. *
    1638. +
    1639. * @return {patio.Dataset} a cloned dataset with the static sql set.
    1640. +
    1641. */
    1642. +
    1643. withSql: function (sql) {
    1644. +
    1645. 46 var args = argsToArray(arguments).slice(1);
    1646. +
    1647. 46 if (args.length) {
    1648. +
    1649. 23 sql = new PlaceHolderLiteralString(sql, args)
    1650. +
    1651. }
    1652. +
    1653. 46 return this.mergeOptions({sql: sql});
    1654. +
    1655. },
    1656. -
    1657. instance:{
    1658. /**
    1659. -
    1660. * A validation plugin for patio models. This plugin adds a <code>validate</code> method to each {@link patio.Model}
    1661. -
    1662. * class that adds it as a plugin. This plugin does not include most typecast checks as <code>patio</code> already checks
    1663. -
    1664. * types upon column assignment.
    1665. +
    1666. * Add the dataset to the list of compounds
    1667. *
    1668. -
    1669. * To do single col validation
    1670. -
    1671. * {@code
    1672. +
    1673. * @param {String} type the type of compound (i.e. "union", "intersect")
    1674. +
    1675. * @param {patio.Dataset} dataset the dataset to add to
    1676. +
    1677. * @param [Object] [options={}] compound option to use (i.e {all : true})
    1678. *
    1679. -
    1680. * var Model = patio.addModel("validator", {
    1681. -
    1682. * plugins:[ValidatorPlugin]
    1683. -
    1684. * });
    1685. -
    1686. * //this ensures column assignment
    1687. -
    1688. * Model.validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);
    1689. -
    1690. * //col2 does not have to be assigned but if it is it must match /hello/ig.
    1691. -
    1692. * Model.validate("col2").like(/hello/ig);
    1693. -
    1694. * //Ensures that the emailAddress column is a valid email address.
    1695. -
    1696. * Model.validate("emailAddress").isEmailAddress();
    1697. -
    1698. * }
    1699. +
    1700. * @return {patio.Dataset} ds with the dataset added to the compounds.
    1701. +
    1702. */
    1703. +
    1704. compoundClone: function (type, dataset, options) {
    1705. +
    1706. 51 var ds = this._compoundFromSelf().mergeOptions({compounds: (array.toArray(this.__opts.compounds || [])).concat([
    1707. +
    1708. [type, dataset._compoundFromSelf(), options.all]
    1709. +
    1710. ])});
    1711. +
    1712. 51 return options.fromSelf === false ? ds : ds.fromSelf(options);
    1713. +
    1714. },
    1715. +
    1716. +
    1717. /**
    1718. +
    1719. * Returns the a cloned dataset with out the {@link patio.Dataset#rowCb}
    1720. *
    1721. -
    1722. * Or you can do a mass validation through a callback.
    1723. -
    1724. * {@code
    1725. +
    1726. * @example
    1727. +
    1728. * var ds = DB.from("test");
    1729. +
    1730. * ds.rowCb = function(r){
    1731. +
    1732. * r.a = r.a * 2;
    1733. +
    1734. * }
    1735. *
    1736. -
    1737. * var Model = patio.addModel("validator", {
    1738. -
    1739. * plugins:[ValidatorPlugin]
    1740. +
    1741. * ds.all().then(function(ret){
    1742. +
    1743. * //ret === [{a : 4}, {a : 6}]
    1744. * });
    1745. -
    1746. * Model.validate(function(validate){
    1747. -
    1748. * //this ensures column assignment
    1749. -
    1750. * validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);
    1751. -
    1752. * //col2 does not have to be assigned but if it is it must match /hello/ig.
    1753. -
    1754. * validate("col2").isLike(/hello/ig);
    1755. -
    1756. * //Ensures that the emailAddress column is a valid email address.
    1757. -
    1758. * validate("emailAddress").isEmail();
    1759. +
    1760. * ds.naked().all().then(function(ret){
    1761. +
    1762. * //ret === [{a : 2}, {a : 3}];
    1763. * });
    1764. -
    1765. * }
    1766. *
    1767. -
    1768. * To check if a {@link patio.Model} is valid you can run the <code>isValid</code> method.
    1769. +
    1770. * @return {patio.Dataset} a cloned dataset with out the {@link patio.Dataset#rowCb}
    1771. +
    1772. */
    1773. +
    1774. naked: function () {
    1775. +
    1776. 2266 var ds = this.mergeOptions({});
    1777. +
    1778. 2266 ds.rowCb = null;
    1779. +
    1780. 2266 return ds;
    1781. +
    1782. },
    1783. +
    1784. +
    1785. /**
    1786. +
    1787. * @private
    1788. *
    1789. -
    1790. * {@code
    1791. -
    1792. * var model1 = new Model({col2 : 'grape', emailAddress : "test"}),
    1793. -
    1794. * model2 = new Model({col1 : "grape", col2 : "hello", emailAddress : "test@test.com"});
    1795. +
    1796. * Adds a group of conditions joined by the second arg, wrapped in parens, connected to an existing where/having
    1797. +
    1798. * clause by the first arg.
    1799. +
    1800. * If the where/having clause doesn't yet exist, a where clause is created with the group.
    1801. *
    1802. -
    1803. * model1.isValid() //false
    1804. -
    1805. * model2.isValid() //true
    1806. -
    1807. * }
    1808. +
    1809. * @example
    1810. *
    1811. -
    1812. * To get the errors associated with an invalid model you can access the errors property
    1813. +
    1814. * DB.from("items").filter({id, [1,2,3]})._addGroupedCondition("AND", "OR", [{price: {lt : 0}}, {price: {gt: 10}]).sql;
    1815. +
    1816. * //=> SELECT
    1817. +
    1818. * *
    1819. +
    1820. * FROM
    1821. +
    1822. * items
    1823. +
    1824. * WHERE
    1825. +
    1826. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
    1827. *
    1828. -
    1829. * {@code
    1830. -
    1831. * model1.errors; //{ col1: [ 'col1 must be defined.' ],
    1832. -
    1833. * // col2: [ 'col2 must be like /hello/gi got grape.' ],
    1834. -
    1835. * // emailAddress: [ 'emailAddress must be a valid Email Address got test' ] }
    1836. -
    1837. * }
    1838. +
    1839. * DB.from("items")._addGroupedCondition("AND", "OR", [{price: {lt : 0}}, {price: {gt: 10}]).sql;
    1840. +
    1841. * //=> SELECT
    1842. +
    1843. * *
    1844. +
    1845. * FROM
    1846. +
    1847. * items
    1848. +
    1849. * WHERE
    1850. +
    1851. * ((price < 0) OR (price > 10))
    1852. *
    1853. -
    1854. * Validation is also run pre save and pre update. To prevent this you can specify the <code>validate</code> option
    1855. +
    1856. * @param {String} If there is an existing where/having clause, this arg will join the new condition group to it. "AND" if undef. Should be "AND" or "OR"
    1857. +
    1858. * @param {String} The conditions will be joined this. Same expectations as the first param.
    1859. +
    1860. * @param See {@link patio.Dataset#filter}.
    1861. *
    1862. -
    1863. * {@code
    1864. -
    1865. * model1.save(null, {validate : false});
    1866. -
    1867. * model2.save(null, {validate : false});
    1868. -
    1869. * }
    1870. +
    1871. * @return {patio.Dataset} a cloned dataset with the condition group added to the WHERE/HAVING clause.
    1872. +
    1873. */
    1874. +
    1875. _addGroupedCondition: function (addedByBool, groupedByBool) {
    1876. +
    1877. 6 groupedByBool = isUndefined(groupedByBool) ? "AND" : groupedByBool;
    1878. +
    1879. 6 var tOpts = this.__opts,
    1880. +
    1881. clause = (tOpts.having ? "having" : "where"),
    1882. +
    1883. clauseObj = tOpts[clause];
    1884. +
    1885. 6 var args = argsToArray(arguments, 2);
    1886. +
    1887. 6 args = args.length == 1 ? args[0] : args;
    1888. +
    1889. 6 var opts = {};
    1890. +
    1891. 6 if (clauseObj) {
    1892. +
    1893. 3 addedByBool = isUndefined(addedByBool) ? "AND" : addedByBool;
    1894. +
    1895. 3 opts[clause] = new BooleanExpression(addedByBool, clauseObj, this._filterExpr(args, null, groupedByBool));
    1896. +
    1897. } else {
    1898. +
    1899. 3 opts[clause] = this._filterExpr(args, null, groupedByBool);
    1900. +
    1901. }
    1902. +
    1903. 6 return this.mergeOptions(opts);
    1904. +
    1905. },
    1906. +
    1907. +
    1908. /**
    1909. +
    1910. * @private
    1911. +
    1912. * Protected
    1913. *
    1914. -
    1915. * Or you can specify the class level properties <code>validateOnSave</code> and <code>validateOnUpdate</code>
    1916. -
    1917. * to false respectively
    1918. -
    1919. * {@code
    1920. -
    1921. * Model.validateOnSave = false;
    1922. -
    1923. * Model.validateOnUpdate = false;
    1924. -
    1925. * }
    1926. +
    1927. * Internal filter method so it works on either the having or where clauses.
    1928. +
    1929. */
    1930. +
    1931. _filter: function (clause) {
    1932. +
    1933. 3765 var cond = argsToArray(arguments).slice(1), cb;
    1934. +
    1935. 3765 if (cond.length && isFunction(cond[cond.length - 1])) {
    1936. +
    1937. 59 cb = cond.pop();
    1938. +
    1939. }
    1940. +
    1941. 3765 cond = cond.length == 1 ? cond[0] : cond
    1942. +
    1943. 3765 if ((cond == null || cond == undefined || cond === "") || (isArray(cond) && cond.length == 0 && !cb) || (isObject(cond) && isEmpty(cond) && !cb)) {
    1944. +
    1945. 293 return this.mergeOptions();
    1946. +
    1947. } else {
    1948. +
    1949. 3472 cond = this._filterExpr(cond, cb);
    1950. +
    1951. 3469 var cl = this.__opts[clause];
    1952. +
    1953. 3469 cl && (cond = new BooleanExpression("AND", cl, cond));
    1954. +
    1955. 3469 var opts = {};
    1956. +
    1957. 3469 opts[clause] = cond;
    1958. +
    1959. 3469 return this.mergeOptions(opts);
    1960. +
    1961. }
    1962. +
    1963. },
    1964. +
    1965. +
    1966. /**
    1967. +
    1968. * Splits a possible implicit alias, handling both {@link patio.sql.AliasedExpression}s
    1969. +
    1970. * and strings. Returns an array of two elements, with the first being the
    1971. +
    1972. * main expression, and the second being the alias. Alias may be null if it is a
    1973. +
    1974. * string that does not contain an alias {table}___{alias}.
    1975. +
    1976. */
    1977. +
    1978. _splitAlias: function (c) {
    1979. +
    1980. 619 var ret;
    1981. +
    1982. 619 if (isInstanceOf(c, AliasedExpression)) {
    1983. +
    1984. 5 ret = [c.expression, c.alias];
    1985. +
    1986. 614 } else if (isString(c)) {
    1987. +
    1988. 0 var parts = this._splitString(c), cTable = parts[0], column = parts[1], alias = parts[2];
    1989. +
    1990. 0 if (alias) {
    1991. +
    1992. 0 ret = [cTable ? new QualifiedIdentifier(cTable, column) : column, alias];
    1993. +
    1994. } else {
    1995. +
    1996. 0 ret = [c, null];
    1997. +
    1998. }
    1999. +
    2000. } else {
    2001. +
    2002. 614 ret = [c, null];
    2003. +
    2004. }
    2005. +
    2006. 619 return ret;
    2007. +
    2008. },
    2009. +
    2010. +
    2011. +
    2012. /**
    2013. +
    2014. * @private
    2015. +
    2016. * Inverts the given order by breaking it into a list of column references
    2017. +
    2018. * and inverting them.
    2019. *
    2020. -
    2021. * Avaiable validation methods are.
    2022. +
    2023. * ds.invertOrder([sql.identifier("id").desc()]]) //=> [id]
    2024. +
    2025. * ds.invertOrder("category", sql.identifier("price").desc()]) #=> [category.desc(), price]
    2026. +
    2027. */
    2028. +
    2029. +
    2030. _invertOrder: function (order) {
    2031. +
    2032. 46 var ret = order;
    2033. +
    2034. 46 if (order) {
    2035. +
    2036. 45 ret = order.map(function (o) {
    2037. +
    2038. 57 if (isInstanceOf(o, OrderedExpression)) {
    2039. +
    2040. 17 return o.invert();
    2041. +
    2042. } else {
    2043. +
    2044. 40 return new OrderedExpression(isString(o) ? new Identifier(o) : o);
    2045. +
    2046. }
    2047. +
    2048. }, this);
    2049. +
    2050. }
    2051. +
    2052. 46 return ret;
    2053. +
    2054. },
    2055. +
    2056. +
    2057. /**
    2058. +
    2059. * Creates a boolean expression that each key is compared to its value using the provided operator.
    2060. *
    2061. -
    2062. * <ul>
    2063. -
    2064. * <li><code>isAfter</code> : check that a date is after a specified date</li>
    2065. -
    2066. * <li><code>isBefore</code> : check that a date is after before a specified date </li>
    2067. -
    2068. * <li><code>isDefined</code> : ensure that a column is defined</li>
    2069. -
    2070. * <li><code>isNotDefined</code> : ensure that a column is not defined</li>
    2071. -
    2072. * <li><code>isNotNull</code> : ensure that a column is defined and not null</li>
    2073. -
    2074. * <li><code>isNull</code> : ensure that a column is not defined or null</li>
    2075. -
    2076. * <li><code>isEq</code> : ensure that a column equals a value <b>this uses deep equal</b></li>
    2077. -
    2078. * <li><code>isNeq</code> : ensure that a column does not equal a value <b>this uses deep equal</b></li>
    2079. -
    2080. * <li><code>isLike</code> : ensure that a column is like a value, can be a regexp or string</li>
    2081. -
    2082. * <li><code>isNotLike</code> : ensure that a column is not like a value, can be a regexp or string</li>
    2083. -
    2084. * <li><code>isLt</code> : ensure that a column is less than a value</li>
    2085. -
    2086. * <li><code>isGt</code> : ensure that a column is greater than a value</li>
    2087. -
    2088. * <li><code>isLte</code> : ensure that a column is less than or equal to a value</li>
    2089. -
    2090. * <li><code>isGte</code> : ensure that a column is greater than or equal to a value</li>
    2091. -
    2092. * <li><code>isIn</code> : ensure that a column is contained in an array of values</li>
    2093. -
    2094. * <li><code>isNotIn</code> : ensure that a column is not contained in an array of values</li>
    2095. -
    2096. * <li><code>isMacAddress</code> : ensure that a column is a valid MAC address</li>
    2097. -
    2098. * <li><code>isIPAddress</code> : ensure that a column is a valid IPv4 or IPv6 address</li>
    2099. -
    2100. * <li><code>isIPv4Address</code> : ensure that a column is a valid IPv4 address</li>
    2101. -
    2102. * <li><code>isIPv6Address</code> : ensure that a column is a valid IPv6 address</li>
    2103. -
    2104. * <li><code>isUUID</code> : ensure that a column is a valid UUID</li>
    2105. -
    2106. * <li><code>isEmail</code> : ensure that a column is a valid email address</li>
    2107. -
    2108. * <li><code>isUrl</code> : ensure than a column is a valid URL</li>
    2109. -
    2110. * <li><code>isAlpha</code> : ensure than a column is all letters</li>
    2111. -
    2112. * <li><code>isAlphaNumeric</code> : ensure than a column is all letters or numbers</li>
    2113. -
    2114. * <li><code>hasLength</code> : ensure than a column is fits within the specified length accepts a min and optional max value</li>
    2115. -
    2116. * <li><code>isLowercase</code> : ensure than a column is lowercase</li>
    2117. -
    2118. * <li><code>isUppercase</code> : ensure than a column is uppercase</li>
    2119. -
    2120. * <li><code>isEmpty</code> : ensure than a column empty (i.e. a blank string)</li>
    2121. -
    2122. * <li><code>isNotEmpty</code> : ensure than a column not empty (i.e. not a blank string)</li>
    2123. -
    2124. * <li><code>isCreditCard</code> : ensure than a is a valid credit card number</li>
    2125. -
    2126. * <li><code>check</code> : accepts a function to perform validation</li>
    2127. -
    2128. * </ul>
    2129. +
    2130. * @example
    2131. +
    2132. *
    2133. +
    2134. * ds.__createBoolExpression("gt", {x : 1, y:2, z : 5}) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))
    2135. +
    2136. * ds.__createBoolExpression("gt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))
    2137. +
    2138. * ds.__createBoolExpression("lt", {x : 1, y:2, z : 5}) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))
    2139. +
    2140. * ds.__createBoolExpression("lt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))
    2141. +
    2142. *
    2143. +
    2144. * @param {String} op valid boolean expression operator to capare each K,V pair with
    2145. +
    2146. * @param {Object| Array } obj object or two dimensional array containing key value pairs
    2147. +
    2148. *
    2149. +
    2150. * @return {patio.sql.BooleanExpression} boolean expression joined by a AND of each key value pair compared by the op
    2151. +
    2152. */
    2153. +
    2154. __createBoolExpression: function (op, obj) {
    2155. +
    2156. 32 var pairs = [];
    2157. +
    2158. 32 if (Expression.isConditionSpecifier(obj)) {
    2159. +
    2160. 32 if (isHash(obj)) {
    2161. +
    2162. 18 obj = array.toArray(obj);
    2163. +
    2164. }
    2165. +
    2166. 32 obj.forEach(function (pair) {
    2167. +
    2168. 42 pairs.push(new BooleanExpression(op, new Identifier(pair[0]), pair[1]));
    2169. +
    2170. });
    2171. +
    2172. }
    2173. +
    2174. 32 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
    2175. +
    2176. },
    2177. +
    2178. +
    2179. /**
    2180. +
    2181. * @private
    2182. *
    2183. -
    2184. * All of the validation methods are chainable, and accept an options argument.
    2185. +
    2186. * Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
    2187. *
    2188. -
    2189. * The options include
    2190. -
    2191. * <ul>
    2192. -
    2193. * <li><code>message</code> : a message to return if a column fails validation. The message can include <code>{val}</code> and <code>{col}</code>
    2194. -
    2195. * replacements which will insert the invalid value and the column name.
    2196. -
    2197. * </li>
    2198. -
    2199. * <li><code>[onlyDefined=true]</code> : set to false to run the method even if the column value is not defined.</li>
    2200. -
    2201. * <li><code>[onlyNotNull=true]</code> : set to false to run the method even if the column value is null.</li>
    2202. -
    2203. * </ul>
    2204. +
    2205. * @example
    2206. *
    2207. +
    2208. * ds.__createBetweenExpression({x : [1,2]}) => //=> WHERE ((x >= 1) AND (x <= 10))
    2209. +
    2210. * ds.__createBetweenExpression({x : [1,2]}, true) => //=> WHERE ((x < 1) OR (x > 10))
    2211. *
    2212. -
    2213. * @constructs
    2214. -
    2215. * @name ValidatorPlugin
    2216. -
    2217. * @memberOf patio.plugins
    2218. -
    2219. * @property {Object} [errors={}] the validation errors for this model.
    2220. +
    2221. * @param {Object} obj object where the keys are columns and the values are two element arrays.
    2222. +
    2223. * @param {Boolean} [invert] if set to true it inverts the between to make it not between the two values
    2224. *
    2225. +
    2226. * @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
    2227. */
    2228. -
    2229. constructor:function () {
    2230. -
    2231. 114 this._super(arguments);
    2232. -
    2233. 114 this.errors = {};
    2234. +
    2235. __createBetweenExpression: function (obj, invert) {
    2236. +
    2237. 4 var pairs = [];
    2238. +
    2239. 4 for (var i in obj) {
    2240. +
    2241. 4 var v = obj[i];
    2242. +
    2243. 4 if (isArray(v) && v.length) {
    2244. +
    2245. 2 var ident = this.stringToIdentifier(i);
    2246. +
    2247. 2 pairs.push(new BooleanExpression("AND", new BooleanExpression("gte", ident, v[0]), new BooleanExpression("lte", ident, v[1])));
    2248. +
    2249. } else {
    2250. +
    2251. 2 throw new QueryError("Between requires an array for the value");
    2252. +
    2253. }
    2254. +
    2255. }
    2256. +
    2257. 2 var ret = pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs))
    2258. +
    2259. 2 return invert ? BooleanExpression.invert(ret) : ret;
    2260. },
    2261. +
    2262. /**
    2263. -
    2264. * Validates a model, returning an array of error messages for each invalid property.
    2265. -
    2266. * @return {String[]} an array of error messages for each invalid property.
    2267. +
    2268. * @private
    2269. +
    2270. * Converts an array to a two dimensional array where the first element
    2271. +
    2272. * is the identifier and the second argument is the value that the value should equal
    2273. +
    2274. * used by is{Null|NotNull|True|NotTrue|False|notFalse} functions to join all the values passed in.
    2275. +
    2276. * @param {String[]|patio.sql.Identifier} arr array of elements to make a condition specifier out of
    2277. +
    2278. * @param defaultOp the value to assign a value if one is not provided.
    2279. +
    2280. *
    2281. +
    2282. * @return { [[]] } an array of two element arrays.
    2283. */
    2284. -
    2285. validate:function () {
    2286. -
    2287. 159 this.errors = {};
    2288. -
    2289. 159 return flatten(this._static.validators.map(function runValidator(validator) {
    2290. -
    2291. 218 var col = validator.col, val = this.__values[validator.col], ret = validator.validate(val);
    2292. -
    2293. 218 this.errors[col] = ret;
    2294. -
    2295. 218 return ret;
    2296. -
    2297. }, this));
    2298. +
    2299. __arrayToConditionSpecifier: function (arr, defaultOp) {
    2300. +
    2301. 22 var ret = [];
    2302. +
    2303. 22 arr.forEach(function (a) {
    2304. +
    2305. 28 if (isString(a)) {
    2306. +
    2307. 18 a = this.stringToIdentifier(a);
    2308. +
    2309. }
    2310. +
    2311. 28 if (isInstanceOf(a, Identifier)) {
    2312. +
    2313. 18 ret.push([a, defaultOp]);
    2314. +
    2315. 10 } else if (isHash(a)) {
    2316. +
    2317. 4 ret = ret.concat(array.toArray(a));
    2318. +
    2319. } else {
    2320. +
    2321. 6 throw new QueryError("Invalid condition specifier " + a);
    2322. +
    2323. }
    2324. +
    2325. }, this);
    2326. +
    2327. 16 return ret;
    2328. },
    2329. /**
    2330. -
    2331. * Returns if this model passes validation.
    2332. +
    2333. * @private
    2334. *
    2335. -
    2336. * @return {Boolean}
    2337. +
    2338. * SQL expression object based on the expr type. See {@link patio.Dataset#filter}
    2339. */
    2340. -
    2341. isValid:function () {
    2342. -
    2343. 159 return this.validate().length === 0;
    2344. +
    2345. _filterExpr: function (expr, cb, joinCond) {
    2346. +
    2347. 4295 expr = (isUndefined(expr) || isNull(expr) || (isArray(expr) && !expr.length)) ? null : expr;
    2348. +
    2349. 4295 if (expr && cb) {
    2350. +
    2351. 1 return new BooleanExpression(joinCond || "AND", this._filterExpr(expr, null, joinCond), this._filterExpr(cb, null, joinCond))
    2352. +
    2353. 4294 } else if (cb) {
    2354. +
    2355. 58 expr = cb
    2356. +
    2357. }
    2358. +
    2359. 4294 if (isInstanceOf(expr, Expression)) {
    2360. +
    2361. 459 if (isInstanceOf(expr, NumericExpression, StringExpression)) {
    2362. +
    2363. 2 throw new QueryError("Invalid SQL Expression type : " + expr);
    2364. +
    2365. }
    2366. +
    2367. 457 return expr;
    2368. +
    2369. 3835 } else if (isArray(expr)) {
    2370. +
    2371. 744 if (expr.length) {
    2372. +
    2373. 744 var first = expr[0];
    2374. +
    2375. 744 if (isString(first)) {
    2376. +
    2377. 25 return new PlaceHolderLiteralString(first, expr.slice(1), true);
    2378. +
    2379. 719 } else if (Expression.isConditionSpecifier(expr)) {
    2380. +
    2381. 708 return BooleanExpression.fromValuePairs(expr, joinCond)
    2382. +
    2383. } else {
    2384. +
    2385. 11 return BooleanExpression.fromArgs([joinCond || "AND"].concat(expr.map(function (e) {
    2386. +
    2387. 23 return this._filterExpr(e, null, joinCond);
    2388. +
    2389. }, this)));
    2390. +
    2391. }
    2392. +
    2393. }
    2394. +
    2395. 3091 } else if (isFunction(expr)) {
    2396. +
    2397. 59 return this._filterExpr(expr.call(sql, sql), null, joinCond);
    2398. +
    2399. 3032 } else if (isBoolean(expr)) {
    2400. +
    2401. 7 return new BooleanExpression("NOOP", expr);
    2402. +
    2403. 3025 } else if (isString(expr)) {
    2404. +
    2405. 1 return this.stringToIdentifier(expr);
    2406. +
    2407. 3024 } else if (isInstanceOf(expr, LiteralString)) {
    2408. +
    2409. 16 return new LiteralString("(" + expr + ")");
    2410. +
    2411. 3008 } else if (isHash(expr)) {
    2412. +
    2413. 3007 return BooleanExpression.fromValuePairs(expr, joinCond);
    2414. +
    2415. } else {
    2416. +
    2417. 1 throw new QueryError("Invalid filter argument");
    2418. +
    2419. }
    2420. +
    2421. },
    2422. +
    2423. +
    2424. +
    2425. /**@ignore*/
    2426. +
    2427. getters: {
    2428. +
    2429. +
    2430. /**
    2431. +
    2432. * @ignore
    2433. +
    2434. * Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
    2435. +
    2436. */
    2437. +
    2438. isSimpleSelectAll: function () {
    2439. +
    2440. 33 var o = {}, opts = this.__opts, count = 0;
    2441. +
    2442. 33 for (var i in opts) {
    2443. +
    2444. 70 if (opts[i] != null && this._static.NON_SQL_OPTIONS.indexOf(i) == -1) {
    2445. +
    2446. 37 o[i] = opts[i];
    2447. +
    2448. 37 count++;
    2449. +
    2450. }
    2451. +
    2452. }
    2453. +
    2454. 33 var f = o.from
    2455. +
    2456. 33 return count == 1 && f.length == 1 && (isString(f[0]) || isInstanceOf(f[0], AliasedExpression, Identifier));
    2457. +
    2458. }
    2459. }
    2460. },
    2461. -
    2462. "static":{
    2463. -
    2464. /**@lends patio.plugins.ValidatorPlugin*/
    2465. +
    2466. static: {
    2467. +
    2468. /**@lends patio.Dataset*/
    2469. /**
    2470. -
    2471. * Set to false to prevent model validation when saving.
    2472. -
    2473. * @default true
    2474. -
    2475. */
    2476. -
    2477. validateOnSave:true,
    2478. +
    2479. * These strings have {name}Join methods created (e.g. {@link patio.Dataset#innerJoin}) that
    2480. +
    2481. * call {@link patio.Dataset#joinTable} with the string, passing along the arguments and
    2482. +
    2483. * block from the method call.
    2484. +
    2485. **/
    2486. +
    2487. CONDITIONED_JOIN_TYPES: ["inner", "fullOuter", "rightOuter", "leftOuter", "full", "right", "left"],
    2488. /**
    2489. -
    2490. * Set to false to prevent model validation when updating.
    2491. -
    2492. * @default true
    2493. +
    2494. *
    2495. +
    2496. * These strings have {name}Join methods created (e.g. naturalJoin) that
    2497. +
    2498. * call {@link patio.Dataset#joinTable}. They only accept a single table
    2499. +
    2500. * argument which is passed to {@link patio.Dataset#joinTable}, and they throw an error
    2501. +
    2502. * if called with a block.
    2503. +
    2504. **/
    2505. +
    2506. UNCONDITIONED_JOIN_TYPES: ["natural", "naturalLeft", "naturalRight", "naturalFull", "cross"],
    2507. +
    2508. +
    2509. /**
    2510. +
    2511. * The dataset options that require the removal of cached columns
    2512. +
    2513. * if changed.
    2514. */
    2515. -
    2516. validateOnUpdate:true,
    2517. +
    2518. COLUMN_CHANGE_OPTS: ["select", "sql", "from", "join"],
    2519. -
    2520. init:function () {
    2521. -
    2522. 35 this._super(arguments);
    2523. -
    2524. },
    2525. +
    2526. /**
    2527. +
    2528. * Which options don't affect the SQL generation. Used by {@link patio.Dataset#simpleSelectAll}
    2529. +
    2530. * to determine if this is a simple SELECT * FROM table.
    2531. +
    2532. */
    2533. +
    2534. NON_SQL_OPTIONS: ["server", "defaults", "overrides", "graph", "eagerGraph", "graphAliases"],
    2535. -
    2536. __initValidation:function () {
    2537. -
    2538. 43 if (!this.__isValidationInited) {
    2539. -
    2540. 34 this.validators = [];
    2541. -
    2542. 34 this.pre("save", function preSaveValidate(next, opts) {
    2543. -
    2544. 114 validateHook.call(this, this._static.validateOnSave, next, opts);
    2545. -
    2546. });
    2547. -
    2548. 34 this.pre("update", function preUpdateValidate(next, opts) {
    2549. -
    2550. 1 validateHook.call(this, this._static.validateOnSave, next, opts);
    2551. -
    2552. });
    2553. -
    2554. 34 this.__isValidationInited = true;
    2555. -
    2556. }
    2557. -
    2558. },
    2559. -
    2560. __getValidator:function validator(name) {
    2561. -
    2562. 44 var ret = new Validator(name);
    2563. -
    2564. 44 this.validators.push(ret);
    2565. -
    2566. 44 return ret;
    2567. -
    2568. },
    2569. +
    2570. /**
    2571. +
    2572. * All methods that return modified datasets with a joined table added.
    2573. +
    2574. */
    2575. +
    2576. JOIN_METHODS: ["join", "joinTable"],
    2577. /**
    2578. -
    2579. * Sets up validation for a model.
    2580. -
    2581. *
    2582. -
    2583. * To do single col validation
    2584. -
    2585. * {@code
    2586. -
    2587. *
    2588. -
    2589. * var Model = patio.addModel("validator", {
    2590. -
    2591. * plugins:[ValidatorPlugin]
    2592. -
    2593. * });
    2594. -
    2595. * //this ensures column assignment
    2596. -
    2597. * Model.validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);
    2598. -
    2599. * //col2 does not have to be assigned but if it is it must match /hello/ig.
    2600. -
    2601. * Model.validate("col2").like(/hello/ig);
    2602. -
    2603. * //Ensures that the emailAddress column is a valid email address.
    2604. -
    2605. * Model.validate("emailAddress").isEmailAddress();
    2606. -
    2607. * }
    2608. -
    2609. *
    2610. -
    2611. * Or you can do a mass validation through a callback.
    2612. -
    2613. * {@code
    2614. -
    2615. *
    2616. -
    2617. * var Model = patio.addModel("validator", {
    2618. -
    2619. * plugins:[ValidatorPlugin]
    2620. -
    2621. * });
    2622. -
    2623. * Model.validate(function(validate){
    2624. -
    2625. * //this ensures column assignment
    2626. -
    2627. * validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);
    2628. -
    2629. * //col2 does not have to be assigned but if it is it must match /hello/ig.
    2630. -
    2631. * validate("col2").isLike(/hello/ig);
    2632. -
    2633. * //Ensures that the emailAddress column is a valid email address.
    2634. -
    2635. * validate("emailAddress").isEmail();
    2636. -
    2637. * });
    2638. -
    2639. * }
    2640. -
    2641. *
    2642. -
    2643. *
    2644. -
    2645. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    2646. -
    2647. *
    2648. -
    2649. * @throws {patio.ModelError} if name is not a function or string.
    2650. -
    2651. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    2652. +
    2653. * Methods that return modified datasets
    2654. */
    2655. -
    2656. validate:function (name) {
    2657. -
    2658. 43 this.__initValidation();
    2659. -
    2660. 43 var ret;
    2661. -
    2662. 43 if (isFunction(name)) {
    2663. -
    2664. 1 name.apply(this, [this.__getValidator.bind(this)]);
    2665. -
    2666. 1 ret = this;
    2667. -
    2668. 42 } else if (isString(name)) {
    2669. -
    2670. 42 ret = this.__getValidator(name);
    2671. -
    2672. } else {
    2673. -
    2674. 0 throw new ModelError("name is must be a string or function when validating");
    2675. -
    2676. }
    2677. -
    2678. 43 return ret;
    2679. +
    2680. QUERY_METHODS: ['addGraphAliases', "and", "distinct", "except", "exclude", "filter", "find", "is", "isNot",
    2681. +
    2682. "eq", "neq", "lt", "lte", "gt", "gte", "forUpdate", "from", "fromSelf", "graph", "grep", "group",
    2683. +
    2684. "groupAndCount", "groupBy", "having", "intersect", "invert", "limit", "lockStyle", "naked", "or", "order",
    2685. +
    2686. "orderAppend", "orderBy", "orderMore", "orderPrepend", "qualify", "reverse",
    2687. +
    2688. "reverseOrder", "select", "selectAll", "selectAppend", "selectMore", "setDefaults",
    2689. +
    2690. "setGraphAliases", "setOverrides", "unfiltered", "ungraphed", "ungrouped", "union", "unlimited",
    2691. +
    2692. "unordered", "where", "with", "withRecursive", "withSql"],
    2693. +
    2694. +
    2695. init: function () {
    2696. +
    2697. 35 this._super(arguments);
    2698. +
    2699. //initialize our join methods array
    2700. +
    2701. 35 var joinMethods = this.JOIN_METHODS;
    2702. +
    2703. 35 var queryMethods = this.QUERY_METHODS;
    2704. +
    2705. 35 this.UNCONDITIONED_JOIN_TYPES.forEach(function (joinType) {
    2706. +
    2707. 175 var m = joinType + "Join";
    2708. +
    2709. 175 joinMethods.push(m);
    2710. +
    2711. 175 queryMethods.push(m);
    2712. +
    2713. });
    2714. +
    2715. 35 this.CONDITIONED_JOIN_TYPES.forEach(function (joinType) {
    2716. +
    2717. 245 var m = joinType + "Join";
    2718. +
    2719. 245 joinMethods.push(m);
    2720. +
    2721. 245 queryMethods.push(m);
    2722. +
    2723. });
    2724. +
    2725. 35 this.QUERY_METHODS = queryMethods.concat(joinMethods);
    2726. }
    2727. }
    2728. -
    2729. -
    2730. }).as(module);
    2731. -
    +
  • }).
  • +
  • as(module);
  • @@ -21056,8 +21113,8 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 25725 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 25725 this._super(arguments);
  • +
  • 25745 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 25745 this._super(arguments);
  • },
  • /**
  • @@ -21736,7 +21793,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(){
  • -
  • 42250 return this.__quoteIdentifiers;
  • +
  • 42266 return this.__quoteIdentifiers;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21744,80 +21801,80 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(){
  • -
  • 13414 return this.__providesAccurateRowsMatched;
  • +
  • 13418 return this.__providesAccurateRowsMatched;
  • },
  • //Whether the dataset requires SQL standard datetimes (false by default,
  • // as most allow strings with ISO 8601 format).
  • /**@ignore*/
  • requiresSqlStandardDateTimes:function(){
  • -
  • 13421 return this.__requiresSqlStandardDateTimes;
  • +
  • 13425 return this.__requiresSqlStandardDateTimes;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(){
  • -
  • 13427 return this.__supportsCte;
  • +
  • 13431 return this.__supportsCte;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • /**@ignore*/
  • supportsDistinctOn:function(){
  • -
  • 2291 return this.__supportsDistinctOn;
  • +
  • 2295 return this.__supportsDistinctOn;
  • },
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(){
  • -
  • 13450 return this.__supportsIntersectExcept;
  • +
  • 13454 return this.__supportsIntersectExcept;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(){
  • -
  • 13426 return this.__supportsIntersectExceptAll;
  • +
  • 13430 return this.__supportsIntersectExceptAll;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(){
  • -
  • 13623 return this.__supportsIsTrue;
  • +
  • 13627 return this.__supportsIsTrue;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(){
  • -
  • 13425 return this.__supportsJoinUsing;
  • +
  • 13429 return this.__supportsJoinUsing;
  • },
  • //Whether modifying joined datasets is supported.
  • /**@ignore*/
  • supportsModifyingJoins:function(){
  • -
  • 2531 return this.__supportsModifyingJoins;
  • +
  • 2535 return this.__supportsModifyingJoins;
  • },
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(){
  • -
  • 13418 return this.__supportsMultipleColumnIn;
  • +
  • 13422 return this.__supportsMultipleColumnIn;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • /**@ignore*/
  • supportsTimestampTimezones:function(){
  • -
  • 2288 return this.__supportsTimestampTimezones;
  • +
  • 2292 return this.__supportsTimestampTimezones;
  • },
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(){
  • -
  • 13414 return this.__supportsTimestampUsecs;
  • +
  • 13418 return this.__supportsTimestampUsecs;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(){
  • -
  • 13414 return this.__supportsWindowFunctions;
  • +
  • 13418 return this.__supportsWindowFunctions;
  • }
  • },
  • @@ -21828,7 +21885,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(val){
  • -
  • 13426 this.__quoteIdentifiers = val;
  • +
  • 13430 this.__quoteIdentifiers = val;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21836,80 +21893,80 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(val){
  • -
  • 13414 this.__providesAccurateRowsMatched = val;
  • +
  • 13418 this.__providesAccurateRowsMatched = val;
  • },
  • //Whether the dataset requires SQL standard datetimes (false by default,
  • // as most allow strings with ISO 8601 format).
  • /**@ignore*/
  • requiresSqlStandardDateTimes:function(val){
  • -
  • 13414 this.__requiresSqlStandardDateTimes = val;
  • +
  • 13418 this.__requiresSqlStandardDateTimes = val;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(val){
  • -
  • 13415 this.__supportsCte = val;
  • +
  • 13419 this.__supportsCte = val;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • /**@ignore*/
  • supportsDistinctOn:function(val){
  • -
  • 2289 this.__supportsDistinctOn = val;
  • +
  • 2293 this.__supportsDistinctOn = val;
  • },
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(val){
  • -
  • 13416 this.__supportsIntersectExcept = val;
  • +
  • 13420 this.__supportsIntersectExcept = val;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(val){
  • -
  • 13416 this.__supportsIntersectExceptAll = val;
  • +
  • 13420 this.__supportsIntersectExceptAll = val;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(val){
  • -
  • 13414 this.__supportsIsTrue = val;
  • +
  • 13418 this.__supportsIsTrue = val;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(val){
  • -
  • 13416 this.__supportsJoinUsing = val;
  • +
  • 13420 this.__supportsJoinUsing = val;
  • },
  • //Whether modifying joined datasets is supported.
  • /**@ignore*/
  • supportsModifyingJoins:function(val){
  • -
  • 2289 this.__supportsModifyingJoins = val;
  • +
  • 2293 this.__supportsModifyingJoins = val;
  • },
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(val){
  • -
  • 13414 this.__supportsMultipleColumnIn = val;
  • +
  • 13418 this.__supportsMultipleColumnIn = val;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • /**@ignore*/
  • supportsTimestampTimezones:function(val){
  • -
  • 2288 this.__supportsTimestampTimezones = val;
  • +
  • 2292 this.__supportsTimestampTimezones = val;
  • },
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(val){
  • -
  • 13414 this.__supportsTimestampUsecs = val;
  • +
  • 13418 this.__supportsTimestampUsecs = val;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(val){
  • -
  • 13414 this.__supportsWindowFunctions = val;
  • +
  • 13418 this.__supportsWindowFunctions = val;
  • }
  • }
  • diff --git a/docs/coverage.html b/docs/coverage.html index f4295537..fd8d971d 100644 --- a/docs/coverage.html +++ b/docs/coverage.html @@ -256,10 +256,10 @@
    - Coverage88.47 - SLOC22140 - LOC5315 - Missed613 + Coverage88.48 + SLOC22197 + LOC5311 + Missed612
    @@ -6921,11 +6921,11 @@
  • * @property {Boolean} hasSelectSource true if this dataset already has a select sources.
  • */
  • constructor:function (db, opts) {
  • -
  • 25725 this._super(arguments);
  • -
  • 25725 this.db = db;
  • -
  • 25725 this.__opts = {};
  • -
  • 25725 this.__rowCb = null;
  • -
  • 25725 if (db) {
  • +
  • 25745 this._super(arguments);
  • +
  • 25745 this.db = db;
  • +
  • 25745 this.__opts = {};
  • +
  • 25745 this.__rowCb = null;
  • +
  • 25745 if (db) {
  • 13274 this.__quoteIdentifiers = db.quoteIdentifiers;
  • 13274 this.__identifierInputMethod = db.identifierInputMethod;
  • 13274 this.__identifierOutputMethod = db.identifierOutputMethod;
  • @@ -6943,22 +6943,22 @@
  • * @return [patio.Dataset] a cloned dataset with the merged options
  • **/
  • mergeOptions:function (opts) {
  • -
  • 13414 opts = isUndefined(opts) ? {} : opts;
  • -
  • 13414 var ds = new this._static(this.db, {});
  • -
  • 13414 ds.rowCb = this.rowCb;
  • -
  • 13414 this._static.FEATURES.forEach(function (f) {
  • -
  • 187796 ds[f] = this[f];
  • +
  • 13418 opts = isUndefined(opts) ? {} : opts;
  • +
  • 13418 var ds = new this._static(this.db, {});
  • +
  • 13418 ds.rowCb = this.rowCb;
  • +
  • 13418 this._static.FEATURES.forEach(function (f) {
  • +
  • 187852 ds[f] = this[f];
  • }, this);
  • -
  • 13414 var dsOpts = ds.__opts = merge({}, this.__opts, opts);
  • -
  • 13414 ds.identifierInputMethod = this.identifierInputMethod;
  • -
  • 13414 ds.identifierOutputMethod = this.identifierOutputMethod;
  • -
  • 13414 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
  • -
  • 13414 if (Object.keys(opts).some(function (o) {
  • -
  • 12145 return columnChangeOpts.indexOf(o) !== -1;
  • +
  • 13418 var dsOpts = ds.__opts = merge({}, this.__opts, opts);
  • +
  • 13418 ds.identifierInputMethod = this.identifierInputMethod;
  • +
  • 13418 ds.identifierOutputMethod = this.identifierOutputMethod;
  • +
  • 13418 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
  • +
  • 13418 if (Object.keys(opts).some(function (o) {
  • +
  • 12149 return columnChangeOpts.indexOf(o) !== -1;
  • })) {
  • 2289 dsOpts.columns = null;
  • }
  • -
  • 13414 return ds;
  • +
  • 13418 return ds;
  • },
  • @@ -6985,17 +6985,17 @@
  • * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
  • */
  • stringToIdentifier:function (name) {
  • -
  • 12879 if (isString(name)) {
  • -
  • 8755 var parts = this._splitString(name);
  • -
  • 8755 var schema = parts[0], table = parts[1], alias = parts[2];
  • -
  • 8755 return (schema && table && alias
  • +
  • 12895 if (isString(name)) {
  • +
  • 8763 var parts = this._splitString(name);
  • +
  • 8763 var schema = parts[0], table = parts[1], alias = parts[2];
  • +
  • 8763 return (schema && table && alias
  • ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
  • : (schema && table
  • ? new QualifiedIdentifier(schema, table)
  • : (table && alias
  • ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
  • } else {
  • -
  • 4124 return name;
  • +
  • 4132 return name;
  • }
  • },
  • @@ -7023,20 +7023,20 @@
  • * </ul>
  • */
  • _splitString:function (s) {
  • -
  • 16920 var ret, m;
  • -
  • 16920 if ((m = s.match(this._static.COLUMN_REF_RE1)) !== null) {
  • +
  • 16928 var ret, m;
  • +
  • 16928 if ((m = s.match(this._static.COLUMN_REF_RE1)) !== null) {
  • 137 ret = m.slice(1);
  • }
  • -
  • 16783 else if ((m = s.match(this._static.COLUMN_REF_RE2)) !== null) {
  • +
  • 16791 else if ((m = s.match(this._static.COLUMN_REF_RE2)) !== null) {
  • 24 ret = [null, m[1], m[2]];
  • }
  • -
  • 16759 else if ((m = s.match(this._static.COLUMN_REF_RE3)) !== null) {
  • +
  • 16767 else if ((m = s.match(this._static.COLUMN_REF_RE3)) !== null) {
  • 1547 ret = [m[1], m[2], null];
  • }
  • else {
  • -
  • 15212 ret = [null, s, null];
  • +
  • 15220 ret = [null, s, null];
  • }
  • -
  • 16920 return ret;
  • +
  • 16928 return ret;
  • },
  • /**
  • @@ -7045,15 +7045,15 @@
  • getters:{
  • rowCb:function () {
  • -
  • 19292 return this.__rowCb;
  • +
  • 19296 return this.__rowCb;
  • },
  • identifierInputMethod:function () {
  • -
  • 13414 return this.__identifierInputMethod;
  • +
  • 13418 return this.__identifierInputMethod;
  • },
  • identifierOutputMethod:function () {
  • -
  • 13414 return this.__identifierOutputMethod;
  • +
  • 13418 return this.__identifierOutputMethod;
  • },
  • firstSourceAlias:function () {
  • @@ -7112,16 +7112,16 @@
  • /**@lends patio.Dataset.prototype*/
  • identifierInputMethod:function (meth) {
  • -
  • 13484 this.__identifierInputMethod = meth;
  • +
  • 13488 this.__identifierInputMethod = meth;
  • },
  • identifierOutputMethod:function (meth) {
  • -
  • 13484 this.__identifierOutputMethod = meth;
  • +
  • 13488 this.__identifierOutputMethod = meth;
  • },
  • rowCb:function (cb) {
  • -
  • 16639 if (isFunction(cb) || isNull(cb)) {
  • -
  • 16634 this.__rowCb = cb;
  • +
  • 16643 if (isFunction(cb) || isNull(cb)) {
  • +
  • 16638 this.__rowCb = cb;
  • } else {
  • 5 throw new DatasetError("rowCb mus be a function");
  • }
  • @@ -7347,14 +7347,14 @@
  • * @borrows patio.Dataset#leftJoin as leftJoin
  • * */
  • constructor:function (options, fromDb) {
  • -
  • 2717 if (this.synced) {
  • -
  • 2717 this.__emitter = new EventEmitter();
  • -
  • 2717 this._super(arguments);
  • -
  • 2717 this.patio = patio || require("./index");
  • -
  • 2717 fromDb = isBoolean(fromDb) ? fromDb : false;
  • -
  • 2717 this.__changed = {};
  • -
  • 2717 this.__values = {};
  • -
  • 2717 if (fromDb) {
  • +
  • 2715 if (this.synced) {
  • +
  • 2715 this.__emitter = new EventEmitter();
  • +
  • 2715 this._super(arguments);
  • +
  • 2715 this.patio = patio || require("./index");
  • +
  • 2715 fromDb = isBoolean(fromDb) ? fromDb : false;
  • +
  • 2715 this.__changed = {};
  • +
  • 2715 this.__values = {};
  • +
  • 2715 if (fromDb) {
  • 1519 this._hook("pre", "load");
  • 1519 this.__isNew = false;
  • 1519 this.__setFromDb(options, true);
  • @@ -7363,8 +7363,8 @@
  • 1519 this._static.emit("load", this);
  • }
  • } else {
  • -
  • 1198 this.__isNew = true;
  • -
  • 1198 this.__set(options);
  • +
  • 1196 this.__isNew = true;
  • +
  • 1196 this.__set(options);
  • }
  • } else {
  • 0 throw new ModelError("Model " + this.tableName + " has not been synced");
  • @@ -7372,15 +7372,15 @@
  • },
  • __set:function (values, ignore) {
  • -
  • 1322 values = values || {};
  • -
  • 1322 this.__ignore = ignore === true;
  • -
  • 1322 Object.keys(values).forEach(function (attribute) {
  • +
  • 1320 values = values || {};
  • +
  • 1320 this.__ignore = ignore === true;
  • +
  • 1320 Object.keys(values).forEach(function (attribute) {
  • 5863 var value = values[attribute];
  • //check if the column is a constrained value and is allowed to be set
  • 5863 !ignore && this._checkIfColumnIsConstrained(attribute);
  • 5863 this[attribute] = value;
  • }, this);
  • -
  • 1322 this.__ignore = false;
  • +
  • 1320 this.__ignore = false;
  • },
  • __setFromDb:function (values, ignore) {
  • @@ -7635,7 +7635,7 @@
  • },
  • synced:function () {
  • -
  • 11255 return this._static.synced;
  • +
  • 11253 return this._static.synced;
  • }
  • }
  • @@ -9564,7 +9564,7 @@
  • * @param {String} message the message to show.
  • */
  • 1patio.QueryError = function(message) {
  • -
  • 122 return new Error("QueryError : " + message);
  • +
  • 118 return new Error("QueryError : " + message);
  • };
  • /**
  • @@ -9662,21 +9662,21 @@
  • 1var virtualRow = function (name) {
  • -
  • 882 var WILDCARD = new LiteralString('*');
  • -
  • 882 var QUESTION_MARK = new LiteralString('?');
  • -
  • 882 var COMMA_SEPARATOR = new LiteralString(', ');
  • -
  • 882 var DOUBLE_UNDERSCORE = '__';
  • -
  • -
  • 882 var parts = name.split(DOUBLE_UNDERSCORE);
  • -
  • 882 var table = parts[0], column = parts[1];
  • -
  • 882 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • -
  • 882 var prox = methodMissing(ident, function (m) {
  • +
  • 876 var WILDCARD = new LiteralString('*');
  • +
  • 876 var QUESTION_MARK = new LiteralString('?');
  • +
  • 876 var COMMA_SEPARATOR = new LiteralString(', ');
  • +
  • 876 var DOUBLE_UNDERSCORE = '__';
  • +
  • +
  • 876 var parts = name.split(DOUBLE_UNDERSCORE);
  • +
  • 876 var table = parts[0], column = parts[1];
  • +
  • 876 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • +
  • 876 var prox = methodMissing(ident, function (m) {
  • 3 return function () {
  • 3 var args = argsToArray(arguments);
  • 3 return SQLFunction.fromArgs([m, name].concat(args));
  • }
  • }, column ? QualifiedIdentifier : Identifier);
  • -
  • 882 var ret = createFunctionWrapper(prox, function (m) {
  • +
  • 876 var ret = createFunctionWrapper(prox, function (m) {
  • 484 var args = argsToArray(arguments);
  • 484 if (args.length) {
  • 478 return SQLFunction.fromArgs([name].concat(args));
  • @@ -9686,8 +9686,8 @@
  • }, function () {
  • 0 return SQLFunction.fromArgs(arguments);
  • });
  • -
  • 882 ret.__proto__ = ident;
  • -
  • 882 return ret;
  • +
  • 876 ret.__proto__ = ident;
  • +
  • 876 return ret;
  • };
  • 1var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
  • @@ -10042,8 +10042,8 @@
  • * @see patio.sql.identifier
  • */
  • stringToIdentifier:function (name) {
  • -
  • 10491 !Dataset && (Dataset = require("./dataset"));
  • -
  • 10491 return new Dataset().stringToIdentifier(name);
  • +
  • 10507 !Dataset && (Dataset = require("./dataset"));
  • +
  • 10507 return new Dataset().stringToIdentifier(name);
  • },
  • /**
  • @@ -10139,7 +10139,7 @@
  • });
  • 1exports.sql = methodMissing(sql, function (name) {
  • -
  • 882 return virtualRow(name);
  • +
  • 876 return virtualRow(name);
  • });
  • 1var OPERTATOR_INVERSIONS = {
  • @@ -10949,13 +10949,13 @@
  • * @return {patio.sql.Expression} an expression.
  • */
  • fromArgs:function (args) {
  • -
  • 1960 var ret;
  • -
  • 1960 try {
  • -
  • 1960 ret = new this();
  • +
  • 1958 var ret;
  • +
  • 1958 try {
  • +
  • 1958 ret = new this();
  • } catch (ignore) {
  • }
  • -
  • 1960 this.apply(ret, args);
  • -
  • 1960 return ret;
  • +
  • 1958 this.apply(ret, args);
  • +
  • 1958 return ret;
  • },
  • /**
  • @@ -10972,8 +10972,8 @@
  • * @return {Boolean} true if the object is a Hash or is an array of two element arrays.
  • */
  • isConditionSpecifier:function (obj) {
  • -
  • 19142 return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
  • -
  • 7932 return isArray(i) && i.length === 2;
  • +
  • 19178 return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
  • +
  • 7944 return isArray(i) && i.length === 2;
  • }));
  • }
  • }
  • @@ -11197,35 +11197,35 @@
  • * </p>
  • */
  • constructor:function (op) {
  • -
  • 6642 if (op) {
  • -
  • 6054 var args = argsToArray(arguments,1 );
  • +
  • 6658 if (op) {
  • +
  • 6066 var args = argsToArray(arguments,1 );
  • //make a copy of the args
  • -
  • 6054 var origArgs = args.slice(0);
  • -
  • 6054 args.forEach(function (a, i) {
  • -
  • 12280 if (Expression.isConditionSpecifier(a)) {
  • +
  • 6066 var origArgs = args.slice(0);
  • +
  • 6066 args.forEach(function (a, i) {
  • +
  • 12304 if (Expression.isConditionSpecifier(a)) {
  • 6 args[i] = BooleanExpression.fromValuePairs(a);
  • }
  • });
  • -
  • 6054 op = op.toUpperCase();
  • +
  • 6066 op = op.toUpperCase();
  • -
  • 6054 if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
  • -
  • 953 if (args.length < 1) {
  • +
  • 6066 if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
  • +
  • 957 if (args.length < 1) {
  • 0 throw new ExpressionError("The " + op + " operator requires at least 1 argument")
  • }
  • -
  • 953 var oldArgs = args.slice(0);
  • -
  • 953 args = [];
  • -
  • 953 oldArgs.forEach(function (a) {
  • -
  • 2141 a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
  • +
  • 957 var oldArgs = args.slice(0);
  • +
  • 957 args = [];
  • +
  • 957 oldArgs.forEach(function (a) {
  • +
  • 2149 a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
  • });
  • -
  • 5101 } else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
  • -
  • 5038 if (args.length != 2) {
  • +
  • 5109 } else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
  • +
  • 5046 if (args.length != 2) {
  • 0 throw new ExpressionError("The " + op + " operator requires precisely 2 arguments");
  • }
  • //With IN/NOT IN, even if the second argument is an array of two element arrays,
  • //don't convert it into a boolean expression, since it's definitely being used
  • //as a value list.
  • -
  • 5038 if (IN_OPERATORS[op]) {
  • +
  • 5046 if (IN_OPERATORS[op]) {
  • 23 args[1] = origArgs[1]
  • }
  • 63 } else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
  • @@ -11235,8 +11235,8 @@
  • } else {
  • 0 throw new ExpressionError("Invalid operator " + op);
  • }
  • -
  • 6054 this.op = op;
  • -
  • 6054 this.args = args;
  • +
  • 6066 this.op = op;
  • +
  • 6066 this.args = args;
  • }
  • },
  • @@ -11249,9 +11249,9 @@
  • * @return String the SQL version of the {@link patio.sql.ComplexExpression}.
  • */
  • toString:function (ds) {
  • -
  • 5673 !Dataset && (Dataset = require("./dataset"));
  • -
  • 5673 ds = ds || new Dataset();
  • -
  • 5673 return ds.complexExpressionSql(this.op, this.args);
  • +
  • 5685 !Dataset && (Dataset = require("./dataset"));
  • +
  • 5685 ds = ds || new Dataset();
  • +
  • 5685 return ds.complexExpressionSql(this.op, this.args);
  • }
  • },
  • @@ -11468,40 +11468,40 @@
  • * @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
  • */
  • fromValuePairs:function (a, op, negate) {
  • -
  • 6758 !Dataset && (Dataset = require("./dataset"));
  • -
  • 6758 op = op || "AND", negate = negate || false;
  • -
  • 6758 var pairArr = [];
  • -
  • 6758 var isArr = isArray(a) && Expression.isConditionSpecifier(a);
  • -
  • 6758 if (isHash(a)) {
  • -
  • 3063 pairArr.push(this.__filterObject(a));
  • +
  • 6774 !Dataset && (Dataset = require("./dataset"));
  • +
  • 6774 op = op || "AND", negate = negate || false;
  • +
  • 6774 var pairArr = [];
  • +
  • 6774 var isArr = isArray(a) && Expression.isConditionSpecifier(a);
  • +
  • 6774 if (isHash(a)) {
  • +
  • 3071 pairArr.push(this.__filterObject(a));
  • } else {
  • -
  • 3695 for (var k in a) {
  • -
  • 4320 var v = isArr ? a[k][1] : a[k], ret;
  • -
  • 4320 k = isArr ? a[k][0] : k;
  • -
  • 4320 if (isArray(v) || isInstanceOf(v, Dataset)) {
  • +
  • 3703 for (var k in a) {
  • +
  • 4328 var v = isArr ? a[k][1] : a[k], ret;
  • +
  • 4328 k = isArr ? a[k][0] : k;
  • +
  • 4328 if (isArray(v) || isInstanceOf(v, Dataset)) {
  • 17 k = isArray(k) ? k.map(function (i) {
  • 12 return isString(i) ? sql.stringToIdentifier(i) : i
  • }) : isString(k) ? sql.stringToIdentifier(k) : k;
  • 17 ret = new BooleanExpression("IN", k, v);
  • -
  • 4303 } else if (isInstanceOf(v, NegativeBooleanConstant)) {
  • +
  • 4311 } else if (isInstanceOf(v, NegativeBooleanConstant)) {
  • 0 ret = new BooleanExpression("ISNOT", k, v.constant);
  • -
  • 4303 } else if (isInstanceOf(v, BooleanConstant)) {
  • +
  • 4311 } else if (isInstanceOf(v, BooleanConstant)) {
  • 0 ret = new BooleanExpression("IS", k, v.constant);
  • -
  • 4303 } else if (isNull(v) || isBoolean(v)) {
  • +
  • 4311 } else if (isNull(v) || isBoolean(v)) {
  • 180 ret = new BooleanExpression("IS", k, v);
  • -
  • 4123 } else if (isHash(v)) {
  • +
  • 4131 } else if (isHash(v)) {
  • 0 ret = BooleanExpression.__filterObject(v, k);
  • -
  • 4123 } else if (isRegExp(v)) {
  • +
  • 4131 } else if (isRegExp(v)) {
  • 61 ret = StringExpression.like(sql.stringToIdentifier(k), v);
  • } else {
  • -
  • 4062 ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
  • +
  • 4070 ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
  • }
  • -
  • 4320 negate && (ret = BooleanExpression.invert(ret));
  • -
  • 4320 pairArr.push(ret);
  • +
  • 4328 negate && (ret = BooleanExpression.invert(ret));
  • +
  • 4328 pairArr.push(ret);
  • }
  • }
  • //if We just have one then return the first otherwise create a new Boolean expression
  • -
  • 6758 return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
  • +
  • 6774 return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
  • },
  • /**
  • @@ -11526,13 +11526,13 @@
  • * @return {patio.sql.Expression} an expression to use in the filter
  • */
  • __filterObject:function (expr, key) {
  • -
  • 3172 var pairs = [], opts, newKey;
  • -
  • 3172 var twoArityOperators = this.TWO_ARITY_OPERATORS;
  • -
  • 3172 for (var k in expr) {
  • -
  • 3196 var v = expr[k];
  • -
  • 3196 if (isHash(v)) { //its a hash too filter it too!
  • +
  • 3180 var pairs = [], opts, newKey;
  • +
  • 3180 var twoArityOperators = this.TWO_ARITY_OPERATORS;
  • +
  • 3180 for (var k in expr) {
  • +
  • 3204 var v = expr[k];
  • +
  • 3204 if (isHash(v)) { //its a hash too filter it too!
  • 107 pairs.push(this.__filterObject(v, k));
  • -
  • 3089 } else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
  • +
  • 3097 } else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
  • //its a two arrity operator (e.g. '=', '>')
  • 110 newKey = isString(key) ? key.split(",") : [key];
  • 110 if (newKey.length > 1) {
  • @@ -11566,19 +11566,19 @@
  • } else {
  • //we're not a twoarity operator
  • //so we create a boolean expression out of it
  • -
  • 2979 newKey = k.split(",");
  • -
  • 2979 if (newKey.length == 1) {
  • -
  • 2973 newKey = sql.stringToIdentifier(newKey[0]);
  • +
  • 2987 newKey = k.split(",");
  • +
  • 2987 if (newKey.length == 1) {
  • +
  • 2981 newKey = sql.stringToIdentifier(newKey[0]);
  • }
  • -
  • 2979 opts = [
  • +
  • 2987 opts = [
  • [newKey, v]
  • ];
  • -
  • 2979 pairs.push(BooleanExpression.fromValuePairs(opts));
  • +
  • 2987 pairs.push(BooleanExpression.fromValuePairs(opts));
  • }
  • }
  • //if the total of pairs is one then we just return the first element
  • //otherwise we join them all with an AND
  • -
  • 3172 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
  • +
  • 3180 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
  • }
  • }
  • }).as(sql, "BooleanExpression");
  • @@ -11758,7 +11758,7 @@
  • * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
  • */
  • constructor:function (value) {
  • -
  • 14766 this.__value = value;
  • +
  • 14762 this.__value = value;
  • },
  • /**
  • @@ -11770,15 +11770,15 @@
  • * @return String the SQL version of the {@link patio.sql.Identifier}.
  • */
  • toString:function (ds) {
  • -
  • 19196 !Dataset && (Dataset = require("./dataset"));
  • -
  • 19196 ds = ds || new Dataset();
  • -
  • 19196 return ds.quoteIdentifier(this);
  • +
  • 19208 !Dataset && (Dataset = require("./dataset"));
  • +
  • 19208 ds = ds || new Dataset();
  • +
  • 19208 return ds.quoteIdentifier(this);
  • },
  • /**@ignore*/
  • getters:{
  • value:function () {
  • -
  • 22095 return this.__value;
  • +
  • 22107 return this.__value;
  • }
  • }
  • }
  • @@ -12335,7 +12335,7 @@
  • 1var addStringMethod = function (op) {
  • 24 return function () {
  • -
  • 3622 return this.__str[op].apply(this.__str, arguments);
  • +
  • 3626 return this.__str[op].apply(this.__str, arguments);
  • }
  • };
  • @@ -12358,7 +12358,7 @@
  • * @param {String} str the literal string.
  • */
  • constructor:function (str) {
  • -
  • 3075 this.__str = str;
  • +
  • 3057 this.__str = str;
  • }
  • }
  • }).as(sql, "LiteralString");
  • @@ -14086,9 +14086,9 @@
  • constructor:function () {
  • //We initialize these here because otherwise
  • //the will be blank because of recursive dependencies.
  • -
  • 25725 !patio && (patio = require("../index"));
  • -
  • 25725 !Dataset && (Dataset = patio.Dataset);
  • -
  • 25725 this._super(arguments);
  • +
  • 25745 !patio && (patio = require("../index"));
  • +
  • 25745 !Dataset && (Dataset = patio.Dataset);
  • +
  • 25745 this._super(arguments);
  • },
  • /**
  • @@ -14373,15 +14373,15 @@
  • * @return {String} a literal representation of the value.
  • */
  • literal:function (v) {
  • -
  • 38459 if (isInstanceOf(v, LiteralString)) {
  • +
  • 38491 if (isInstanceOf(v, LiteralString)) {
  • 194 return "" + v;
  • -
  • 38265 } else if (isString(v)) {
  • +
  • 38297 } else if (isString(v)) {
  • 5397 return this._literalString(v);
  • -
  • 32868 } else if (isNumber(v)) {
  • -
  • 6152 return this._literalNumber(v);
  • +
  • 32900 } else if (isNumber(v)) {
  • +
  • 6160 return this._literalNumber(v);
  • }
  • -
  • 26716 else if (isInstanceOf(v, Expression)) {
  • -
  • 24443 return this._literalExpression(v);
  • +
  • 26740 else if (isInstanceOf(v, Expression)) {
  • +
  • 24467 return this._literalExpression(v);
  • }
  • 2273 else if (isInstanceOf(v, Dataset)) {
  • 104 return this._literalDataset(v);
  • @@ -14572,22 +14572,22 @@
  • //Prepares an SQL statement by calling all clause methods for the given statement type.
  • _clauseSql:function (type) {
  • -
  • 5793 var sql = [("" + type).toUpperCase()];
  • -
  • 5793 try {
  • -
  • 5793 this._static[sql + "_CLAUSE_METHODS"].forEach(function (m) {
  • -
  • 58246 if (m.match("With")) {
  • -
  • 5516 this[m](sql);
  • +
  • 5797 var sql = [("" + type).toUpperCase()];
  • +
  • 5797 try {
  • +
  • 5797 this._static[sql + "_CLAUSE_METHODS"].forEach(function (m) {
  • +
  • 58298 if (m.match("With")) {
  • +
  • 5520 this[m](sql);
  • } else {
  • -
  • 52730 var sqlRet = this[m]();
  • -
  • 52728 if (sqlRet) {
  • -
  • 18662 sql.push(sqlRet);
  • +
  • 52778 var sqlRet = this[m]();
  • +
  • 52776 if (sqlRet) {
  • +
  • 18674 sql.push(sqlRet);
  • }
  • }
  • }, this);
  • } catch (e) {
  • 2 throw e;
  • }
  • -
  • 5791 return sql.join("");
  • +
  • 5795 return sql.join("");
  • },
  • @@ -14701,9 +14701,9 @@
  • * SQL fragment for complex expressions
  • **/
  • complexExpressionSql:function (op, args) {
  • -
  • 5658 var newOp;
  • -
  • 5658 var isOperators = this._static.IS_OPERATORS, isLiterals = this._static.IS_LITERALS;
  • -
  • 5658 if ((newOp = isOperators[op]) != null) {
  • +
  • 5670 var newOp;
  • +
  • 5670 var isOperators = this._static.IS_OPERATORS, isLiterals = this._static.IS_LITERALS;
  • +
  • 5670 if ((newOp = isOperators[op]) != null) {
  • 232 var r = args[1], v = isNull(r) ? isLiterals.NULL : isLiterals[r];
  • 232 if (r == null || this.supportsIsTrue) {
  • 232 if (isUndefined(v)) {
  • @@ -14719,7 +14719,7 @@
  • null)]);
  • }
  • -
  • 5426 } else if (["IN", "NOTIN"].indexOf(op) != -1) {
  • +
  • 5438 } else if (["IN", "NOTIN"].indexOf(op) != -1) {
  • 17 var cols = args[0], vals = args[1], colArray = isArray(cols), valArray = false, emptyValArray = false;
  • 17 if (isArray(vals)) {
  • @@ -14765,12 +14765,12 @@
  • ComplexExpression.IN_OPERATORS[op], this.literal(vals));
  • }
  • }
  • -
  • 5409 } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
  • -
  • 4596 var l = args[0];
  • -
  • 4596 return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
  • +
  • 5421 } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
  • +
  • 4604 var l = args[0];
  • +
  • 4604 return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
  • this.literal(args[1]));
  • -
  • 813 } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
  • -
  • 779 return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
  • +
  • 817 } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
  • +
  • 783 return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
  • 34 } else if (op == "NOT") {
  • 5 return string.format("NOT %s", this.literal(args[0]));
  • 29 } else if (op == "NOOP") {
  • @@ -14899,18 +14899,18 @@
  • * quote the name with {@link patio.dataset._Sql#_quotedIdentifier}.
  • */
  • quoteIdentifier:function (name) {
  • -
  • 29064 if (isInstanceOf(name, LiteralString)) {
  • +
  • 29076 if (isInstanceOf(name, LiteralString)) {
  • 228 return name;
  • } else {
  • -
  • 28836 if (isInstanceOf(name, Identifier)) {
  • -
  • 19530 name = name.value;
  • +
  • 28848 if (isInstanceOf(name, Identifier)) {
  • +
  • 19542 name = name.value;
  • }
  • -
  • 28836 name = this.inputIdentifier(name);
  • -
  • 28836 if (this.quoteIdentifiers) {
  • +
  • 28848 name = this.inputIdentifier(name);
  • +
  • 28848 if (this.quoteIdentifiers) {
  • 21729 name = this._quotedIdentifier(name)
  • }
  • }
  • -
  • 28836 return name;
  • +
  • 28848 return name;
  • },
  • /**
  • @@ -14920,9 +14920,9 @@
  • * identifierOutputMethod.
  • */
  • inputIdentifier:function (v) {
  • -
  • 28970 var i = this.__identifierInputMethod;
  • -
  • 28970 v = v.toString(this);
  • -
  • 28970 return !isUndefinedOrNull(i) ?
  • +
  • 28982 var i = this.__identifierInputMethod;
  • +
  • 28982 v = v.toString(this);
  • +
  • 28982 return !isUndefinedOrNull(i) ?
  • isFunction(v[i]) ?
  • v[i]() :
  • isFunction(comb[i]) ?
  • @@ -15010,7 +15010,7 @@
  • * column names. If the array is empty, a wildcard (*) is returned.
  • */
  • __columnList:function (columns) {
  • -
  • 4776 return (!columns || columns.length == 0) ? this._static.WILDCARD : this.__expressionList(columns);
  • +
  • 4780 return (!columns || columns.length == 0) ? this._static.WILDCARD : this.__expressionList(columns);
  • },
  • /**
  • @@ -15094,11 +15094,11 @@
  • * @return SQL fragment for a number.
  • */
  • _literalNumber:function (num) {
  • -
  • 6152 var ret = "" + num;
  • -
  • 6152 if (isNaN(num) || num == Infinity) {
  • +
  • 6160 var ret = "" + num;
  • +
  • 6160 if (isNaN(num) || num == Infinity) {
  • 0 ret = string.format("'%s'", ret);
  • }
  • -
  • 6152 return ret;
  • +
  • 6160 return ret;
  • },
  • /**
  • @@ -15147,7 +15147,7 @@
  • * @return SQL fragment for SQL::Expression, result depends on the specific type of expression.
  • * */
  • _literalExpression:function (v) {
  • -
  • 24453 return v.toString(this);
  • +
  • 24477 return v.toString(this);
  • },
  • /**
  • @@ -15180,9 +15180,9 @@
  • /*SQL STATEMENT CREATION METHODS*/
  • _selectQualifySql:function () {
  • -
  • 4323 var o = this.__opts;
  • -
  • 4323 var table = this.__opts.alwaysQualify;
  • -
  • 4323 if (table && !o.sql) {
  • +
  • 4327 var o = this.__opts;
  • +
  • 4327 var table = this.__opts.alwaysQualify;
  • +
  • 4327 if (table && !o.sql) {
  • 2 array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) {
  • 2 o[k] = this._qualifiedExpression(o[k], table);
  • }, this);
  • @@ -15200,19 +15200,19 @@
  • * @return the columns selected
  • * */
  • _selectColumnsSql:function () {
  • -
  • 3588 return " " + this.__columnList(this.__opts.select);
  • +
  • 3592 return " " + this.__columnList(this.__opts.select);
  • },
  • /**@return the DISTINCT clause.*/
  • _selectDistinctSql:function () {
  • -
  • 3588 var distinct = this.__opts.distinct, ret = [];
  • -
  • 3588 if (distinct) {
  • +
  • 3592 var distinct = this.__opts.distinct, ret = [];
  • +
  • 3592 if (distinct) {
  • 10 ret.push(" DISTINCT");
  • 10 if (distinct.length) {
  • 4 ret.push(format(" ON (%s)", this.__expressionList(distinct)));
  • }
  • }
  • -
  • 3588 return ret.join("");
  • +
  • 3592 return ret.join("");
  • },
  • /**
  • @@ -15221,30 +15221,30 @@
  • * work on all databases.
  • **/
  • _selectCompoundsSql:function () {
  • -
  • 3588 var opts = this.__opts, compounds = opts.compounds, ret = [];
  • -
  • 3588 if (compounds) {
  • +
  • 3592 var opts = this.__opts, compounds = opts.compounds, ret = [];
  • +
  • 3592 if (compounds) {
  • 49 compounds.forEach(function (c) {
  • 49 var type = c[0], dataset = c[1], all = c[2];
  • 49 ret.push(string.format(" %s%s %s", type.toUpperCase(), all ? " ALL" : "", this._subselectSql(dataset)));
  • }, this);
  • }
  • -
  • 3588 return ret.join("");
  • +
  • 3592 return ret.join("");
  • },
  • /**
  • * @return the sql to add the list of tables to select FROM
  • **/
  • _selectFromSql:function () {
  • -
  • 3606 var from = this.__opts.from;
  • -
  • 3606 return from ? string.format(" %s%s", this._static.FROM, this._sourceList(from)) : "";
  • +
  • 3610 var from = this.__opts.from;
  • +
  • 3610 return from ? string.format(" %s%s", this._static.FROM, this._sourceList(from)) : "";
  • },
  • /**
  • * @return the GROUP BY clause
  • **/
  • _selectGroupSql:function () {
  • -
  • 3588 var group = this.__opts.group;
  • -
  • 3588 return group ? string.format(" GROUP BY %s", this.__expressionList(group)) : "";
  • +
  • 3592 var group = this.__opts.group;
  • +
  • 3592 return group ? string.format(" GROUP BY %s", this.__expressionList(group)) : "";
  • },
  • @@ -15252,62 +15252,62 @@
  • *@return the sql to add the filter criteria in the HAVING clause
  • **/
  • _selectHavingSql:function () {
  • -
  • 3588 var having = this.__opts.having;
  • -
  • 3588 return having ? string.format(" HAVING %s", this.literal(having)) : "";
  • +
  • 3592 var having = this.__opts.having;
  • +
  • 3592 return having ? string.format(" HAVING %s", this.literal(having)) : "";
  • },
  • /**
  • * @return the JOIN clause.
  • **/
  • _selectJoinSql:function () {
  • -
  • 3591 var join = this.__opts.join, ret = [];
  • -
  • 3591 if (join) {
  • +
  • 3595 var join = this.__opts.join, ret = [];
  • +
  • 3595 if (join) {
  • 467 join.forEach(function (j) {
  • 771 ret.push(this.literal(j));
  • }, this);
  • }
  • -
  • 3591 return ret.join("");
  • +
  • 3595 return ret.join("");
  • },
  • /**
  • * @return the LIMIT and OFFSET clauses.
  • * */
  • _selectLimitSql:function () {
  • -
  • 3588 var ret = [], limit = this.__opts.limit, offset = this.__opts.offset;
  • -
  • 3588 !isUndefined(limit) && !isNull(limit) && (ret.push(format(" LIMIT %s", this.literal(limit))));
  • -
  • 3588 !isUndefined(offset) && !isNull(offset) && (ret.push(format(" OFFSET %s", this.literal(offset))));
  • -
  • 3588 return ret.join("");
  • +
  • 3592 var ret = [], limit = this.__opts.limit, offset = this.__opts.offset;
  • +
  • 3592 !isUndefined(limit) && !isNull(limit) && (ret.push(format(" LIMIT %s", this.literal(limit))));
  • +
  • 3592 !isUndefined(offset) && !isNull(offset) && (ret.push(format(" OFFSET %s", this.literal(offset))));
  • +
  • 3592 return ret.join("");
  • },
  • /**
  • * @return SQL for different locking modes.
  • **/
  • _selectLockSql:function () {
  • -
  • 3587 var lock = this.__opts.lock, ret = [];
  • -
  • 3587 if (lock) {
  • +
  • 3591 var lock = this.__opts.lock, ret = [];
  • +
  • 3591 if (lock) {
  • 3 if (lock == "update") {
  • 2 ret.push(this._static.FOR_UPDATE);
  • } else {
  • 1 ret.push(" ", lock);
  • }
  • }
  • -
  • 3587 return ret.join("");
  • +
  • 3591 return ret.join("");
  • },
  • /**
  • * @return the SQL ORDER BY clause fragment.
  • */
  • _selectOrderSql:function () {
  • -
  • 3600 var order = this.__opts.order;
  • -
  • 3600 return order ? string.format(" ORDER BY %s", this.__expressionList(order)) : "";
  • +
  • 3604 var order = this.__opts.order;
  • +
  • 3604 return order ? string.format(" ORDER BY %s", this.__expressionList(order)) : "";
  • },
  • /**
  • * @return the SQL WHERE clause fragment.
  • */
  • _selectWhereSql:function () {
  • -
  • 4554 var where = this.__opts.where;
  • -
  • 4554 return where ? string.format(" WHERE %s", this.literal(where)) : "";
  • +
  • 4558 var where = this.__opts.where;
  • +
  • 4558 return where ? string.format(" WHERE %s", this.literal(where)) : "";
  • },
  • /**
  • @@ -15315,8 +15315,8 @@
  • * @param sql
  • */
  • _selectWithSql:function (sql) {
  • -
  • 5516 var wit = this.__opts["with"];
  • -
  • 5516 if (wit && wit.length) {
  • +
  • 5520 var wit = this.__opts["with"];
  • +
  • 5520 if (wit && wit.length) {
  • //sql.length = 0;
  • 8 var base = sql.join("");
  • 8 sql.length = 0;
  • @@ -15459,15 +15459,15 @@
  • * Converts an array of source names into into a comma separated list.
  • **/
  • _sourceList:function (source) {
  • -
  • 5814 if (!Array.isArray(source)) {
  • +
  • 5818 if (!Array.isArray(source)) {
  • 718 source = [source];
  • }
  • -
  • 5814 if (!source || !source.length) {
  • +
  • 5818 if (!source || !source.length) {
  • 0 throw new QueryError("No source specified for the query");
  • }
  • -
  • 5814 return " " + source.map(
  • +
  • 5818 return " " + source.map(
  • function (s) {
  • -
  • 5996 return this.__tableRef(s);
  • +
  • 6000 return this.__tableRef(s);
  • }, this).join(this._static.COMMA_SEPARATOR);
  • },
  • @@ -15492,7 +15492,7 @@
  • * @returns SQL fragment specifying a table name.
  • **/
  • __tableRef:function (t) {
  • -
  • 6767 return isString(t) ? this._quotedIdentifier(t) : this.literal(t);
  • +
  • 6771 return isString(t) ? this._quotedIdentifier(t) : this.literal(t);
  • },
  • @@ -15522,12 +15522,12 @@
  • getters:{
  • //Same as selectS, not aliased directly to make subclassing simpler.
  • sql:function () {
  • -
  • 648 return this.selectSql;
  • +
  • 652 return this.selectSql;
  • },
  • selectSql:function () {
  • -
  • 3690 if (this.__opts.sql) return this._staticSql(this.__opts.sql);
  • -
  • 3588 else return this._clauseSql("select");
  • +
  • 3694 if (this.__opts.sql) return this._staticSql(this.__opts.sql);
  • +
  • 3592 else return this._clauseSql("select");
  • },
  • deleteSql:function () {
  • @@ -15982,10 +15982,10 @@
  • /**@ignore*/
  • constructor:function () {
  • -
  • 25725 if (!Dataset) {
  • +
  • 25745 if (!Dataset) {
  • 1 Dataset = require("../index").Dataset;
  • }
  • -
  • 25725 this._super(arguments);
  • +
  • 25745 this._super(arguments);
  • },
  • @@ -17202,10 +17202,10 @@
  • *
  • */
  • constructor:function () {
  • -
  • 2717 if (comb.isUndefinedOrNull(this.__associations)) {
  • -
  • 2629 this.__associations = {};
  • +
  • 2715 if (comb.isUndefinedOrNull(this.__associations)) {
  • +
  • 2628 this.__associations = {};
  • }
  • -
  • 2717 this._super(arguments);
  • +
  • 2715 this._super(arguments);
  • },
  • reload:function () {
  • @@ -17597,2865 +17597,2922 @@
  • }});
  • -
    +
    -
    dataset/query.js
    +
    plugins/validation.js
    - Coverage97.62 - SLOC2290 - LOC462 - Missed11 + Coverage97.66 + SLOC535 + LOC128 + Missed3
    -
    1. 1var comb = require("comb"),
    2. +
      1. 1var comb = require("comb"),
      2. array = comb.array,
      3. -
      4. flatten = array.flatten,
      5. compact = array.compact,
      6. -
      7. define = comb.define,
      8. -
      9. argsToArray = comb.argsToArray,
      10. -
      11. isString = comb.isString,
      12. -
      13. isEmpty = comb.isEmpty,
      14. -
      15. isNull = comb.isNull,
      16. -
      17. isBoolean = comb.isBoolean,
      18. -
      19. isNumber = comb.isNumber,
      20. -
      21. merge = comb.merge,
      22. +
      23. flatten = array.flatten,
      24. +
      25. toArray = array.toArray,
      26. +
      27. net = require("net"),
      28. +
      29. isIP = net.isIP,
      30. +
      31. isIPv4 = net.isIPv4,
      32. +
      33. isIPv6 = net.isIPv6,
      34. +
      35. validator = require("validator"),
      36. +
      37. validatorCheck = validator.check,
      38. +
      39. dateCmp = comb.date.compare,
      40. isArray = comb.isArray,
      41. -
      42. isObject = comb.isObject,
      43. +
      44. combDeepEqual = comb.deepEqual,
      45. +
      46. combIsBoolean = comb.isBoolean,
      47. +
      48. isString = comb.isString,
      49. +
      50. combIsDefined = comb.isDefined,
      51. +
      52. combIsNull = comb.isNull,
      53. +
      54. ModelError = require("../errors.js").ModelError,
      55. isFunction = comb.isFunction,
      56. -
      57. isUndefined = comb.isUndefined,
      58. -
      59. isHash = comb.isHash,
      60. -
      61. isInstanceOf = comb.isInstanceOf,
      62. -
      63. sql = require("../sql").sql,
      64. -
      65. LiteralString = sql.LiteralString,
      66. -
      67. Expression = sql.Expression,
      68. -
      69. ComplexExpression = sql.ComplexExpression,
      70. -
      71. BooleanExpression = sql.BooleanExpression,
      72. -
      73. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
      74. -
      75. Identifier = sql.Identifier,
      76. -
      77. QualifiedIdentifier = sql.QualifiedIdentifier,
      78. -
      79. AliasedExpression = sql.AliasedExpression,
      80. -
      81. StringExpression = sql.StringExpression,
      82. -
      83. NumericExpression = sql.NumericExpression,
      84. -
      85. OrderedExpression = sql.OrderedExpression,
      86. -
      87. JoinClause = sql.JoinClause,
      88. -
      89. JoinOnClause = sql.JoinOnClause,
      90. -
      91. JoinUsingClause = sql.JoinUsingClause,
      92. -
      93. ColumnAll = sql.ColumnAll,
      94. -
      95. QueryError = require("../errors").QueryError;
      96. +
      97. format = comb.string.format,
      98. +
      99. Promise = comb.Promise,
      100. +
      101. serial = comb.serial,
      102. +
      103. when = comb.when,
      104. +
      105. merge = comb.merge,
      106. +
      107. define = comb.define;
      108. +
      109. 1var Validator = define(null, {
      110. +
      111. instance:{
      112. -
      113. 1var Dataset;
      114. +
      115. constructor:function validator(col) {
      116. +
      117. 44 this.col = col;
      118. +
      119. 44 this.__actions = [];
      120. +
      121. },
      122. -
      123. 1function conditionedJoin(type) {
      124. -
      125. 539 var args = argsToArray(arguments, 1);
      126. -
      127. 539 return this.joinTable.apply(this, [type].concat(args));
      128. -
      129. }
      130. +
      131. __addAction:function __addAction(action, opts) {
      132. +
      133. 46 this.__actions.push({
      134. +
      135. action:action,
      136. +
      137. opts:merge({onlyDefined:true, onlyNotNull:false}, opts)
      138. +
      139. });
      140. +
      141. 46 return this;
      142. +
      143. },
      144. -
      145. 1function unConditionJoin(type, table) {
      146. -
      147. 6 var args = argsToArray(arguments, 1);
      148. -
      149. 6 return this.joinTable.apply(this, [type, table]);
      150. -
      151. }
      152. +
      153. isAfter:function (date, opts) {
      154. +
      155. 1 opts = opts || {};
      156. +
      157. 1 var cmpDate = combIsBoolean(opts.date) ? opts.date : true;
      158. +
      159. 1 return this.__addAction(function (col) {
      160. +
      161. 3 return dateCmp(col, date, cmpDate ? "date" : "datetime") > 0;
      162. +
      163. }, merge({message:"{col} must be after " + date + " got {val}."}, opts));
      164. +
      165. },
      166. +
      167. isBefore:function (date, opts) {
      168. +
      169. 1 opts = opts || {};
      170. +
      171. 1 var cmpDate = combIsBoolean(opts.date) ? opts.date : true;
      172. +
      173. 1 return this.__addAction(function (col) {
      174. +
      175. 3 return dateCmp(col, date, cmpDate ? "date" : "datetime") === -1;
      176. +
      177. }, merge({message:"{col} must be before " + date + " got {val}."}, opts));
      178. +
      179. },
      180. -
      181. 1define(null, {
      182. -
      183. /**@ignore*/
      184. -
      185. instance: {
      186. +
      187. isDefined:function isDefined(opts) {
      188. +
      189. 1 return this.__addAction(function (col) {
      190. +
      191. 3 return combIsDefined(col);
      192. +
      193. }, merge({message:"{col} must be defined.", onlyDefined:false, onlyNotNull:false}, opts));
      194. +
      195. },
      196. -
      197. /**@lends patio.Dataset.prototype*/
      198. +
      199. isNotDefined:function isNotDefined(opts) {
      200. +
      201. 1 return this.__addAction(function (col) {
      202. +
      203. 3 return !combIsDefined(col);
      204. +
      205. }, merge({message:"{col} cannot be defined.", onlyDefined:false, onlyNotNull:false}, opts));
      206. +
      207. },
      208. -
      209. /**
      210. -
      211. * @ignore
      212. -
      213. */
      214. -
      215. constructor: function () {
      216. -
      217. 25725 !Dataset && (Dataset = require("../index").Dataset);
      218. -
      219. 25725 this._super(arguments);
      220. -
      221. 25725 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
      222. -
      223. 180075 if (!this[type + "Join"]) {
      224. -
      225. 180075 this[type + "Join"] = conditionedJoin.bind(this, type);
      226. -
      227. }
      228. +
      229. isNotNull:function isNotNull(opts) {
      230. +
      231. 3 return this.__addAction(function (col) {
      232. +
      233. 21 return combIsDefined(col) && !combIsNull(col);
      234. +
      235. }, merge({message:"{col} cannot be null.", onlyDefined:false, onlyNotNull:false}, opts));
      236. +
      237. },
      238. -
      239. }, this);
      240. -
      241. 25725 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
      242. -
      243. 128625 if (!this[type + "Join"]) {
      244. -
      245. 128625 this[type + "Join"] = unConditionJoin.bind(this, type);
      246. -
      247. }
      248. +
      249. isNull:function isNull(opts) {
      250. +
      251. 1 return this.__addAction(function (col) {
      252. +
      253. 3 return !combIsDefined(col) || combIsNull(col);
      254. +
      255. }, merge({message:"{col} must be null got {val}.", onlyDefined:false, onlyNotNull:false}, opts));
      256. +
      257. },
      258. -
      259. }, this);
      260. +
      261. isEq:function eq(val, opts) {
      262. +
      263. 4 return this.__addAction(function (col) {
      264. +
      265. 15 return combDeepEqual(col, val);
      266. +
      267. }, merge({message:"{col} must === " + val + " got {val}."}, opts));
      268. },
      269. -
      270. /**
      271. -
      272. * Adds a RETURNING clause, which is not supported by all databases. If returning is
      273. -
      274. * used instead of returning the autogenerated primary key or update/delete returning the number of rows modified.
      275. -
      276. *
      277. -
      278. * @example
      279. -
      280. *
      281. -
      282. * ds.from("items").returning() //"RETURNING *"
      283. -
      284. * ds.from("items").returning(null) //"RETURNING NULL"
      285. -
      286. * ds.from("items").returning("id", "name") //"RETURNING id, name"
      287. -
      288. * ds.from("items").returning(["id", "name"]) //"RETURNING id, name"
      289. -
      290. *
      291. -
      292. * @param values columns to return. If values is an array then the array is assumed to contain the columns to
      293. -
      294. * return. Otherwise the arguments will be used.
      295. -
      296. * @return {patio.Dataset} a new dataset with the retuning option added.
      297. -
      298. */
      299. -
      300. returning: function (values) {
      301. -
      302. 1087 var args;
      303. -
      304. 1087 if (Array.isArray(values)) {
      305. -
      306. 0 args = values;
      307. -
      308. } else {
      309. -
      310. 1087 args = argsToArray(arguments);
      311. -
      312. }
      313. -
      314. 1087 return this.mergeOptions({returning: args.map(function (v) {
      315. -
      316. 1005 return isString(v) ? sql.stringToIdentifier(v) : v;
      317. -
      318. })});
      319. +
      320. isNeq:function neq(val, opts) {
      321. +
      322. 2 return this.__addAction(function (col) {
      323. +
      324. 8 return !combDeepEqual(col, val);
      325. +
      326. }, merge({message:"{col} must !== " + val + "."}, opts));
      327. },
      328. +
      329. isLike:function like(val, opts) {
      330. +
      331. 3 return this.__addAction(function (col) {
      332. +
      333. 14 return !!col.match(val);
      334. +
      335. }, merge({message:"{col} must be like " + val + " got {val}."}, opts));
      336. +
      337. },
      338. -
      339. /**
      340. -
      341. * Adds a futher filter to an existing filter using AND. This method is identical to {@link patio.Dataset#filter}
      342. -
      343. * except it expects an existing filter.
      344. -
      345. *
      346. -
      347. * <p>
      348. -
      349. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      350. -
      351. * </p>
      352. -
      353. *
      354. -
      355. * @example
      356. -
      357. * DB.from("table").filter("a").and("b").sql;
      358. -
      359. * //=>SELECT * FROM table WHERE a AND b
      360. -
      361. *
      362. -
      363. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      364. -
      365. *
      366. -
      367. * @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.
      368. -
      369. */
      370. -
      371. and: function () {
      372. -
      373. 7 var tOpts = this.__opts, clauseObj = tOpts[tOpts.having ? "having" : "where"];
      374. -
      375. 7 if (clauseObj) {
      376. -
      377. 6 return this.filter.apply(this, arguments);
      378. -
      379. } else {
      380. -
      381. 1 throw new QueryError("No existing filter found");
      382. -
      383. }
      384. +
      385. isNotLike:function notLike(val, opts) {
      386. +
      387. 2 return this.__addAction(function (col) {
      388. +
      389. 6 return !(!!col.match(val));
      390. +
      391. }, merge({message:"{col} must not be like " + val + "."}, opts));
      392. },
      393. -
      394. as: function (alias) {
      395. -
      396. 8 return new AliasedExpression(this, alias);
      397. +
      398. isLt:function lt(num, opts) {
      399. +
      400. 1 return this.__addAction(function (col) {
      401. +
      402. 3 return col < num;
      403. +
      404. }, merge({message:"{col} must be < " + num + " got {val}."}, opts));
      405. },
      406. -
      407. /**
      408. -
      409. * Adds an alternate filter to an existing WHERE/HAVING using OR.
      410. -
      411. *
      412. -
      413. * <p>
      414. -
      415. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      416. -
      417. * </p>
      418. -
      419. *
      420. -
      421. * @example
      422. -
      423. *
      424. -
      425. * DB.from("items").filter("a").or("b")
      426. -
      427. * //=> SELECT * FROM items WHERE a OR b
      428. -
      429. *
      430. -
      431. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      432. -
      433. * @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.
      434. -
      435. */
      436. -
      437. or: function () {
      438. -
      439. 12 var tOpts = this.__opts;
      440. -
      441. 12 var clause = (tOpts.having ? "having" : "where"), clauseObj = tOpts[clause];
      442. -
      443. 12 if (clauseObj) {
      444. -
      445. 9 var args = argsToArray(arguments);
      446. -
      447. 9 args = args.length == 1 ? args[0] : args;
      448. -
      449. 9 var opts = {};
      450. -
      451. 9 opts[clause] = new BooleanExpression("OR", clauseObj, this._filterExpr(args));
      452. -
      453. 9 return this.mergeOptions(opts);
      454. -
      455. } else {
      456. -
      457. 3 throw new QueryError("No existing filter found");
      458. +
      459. isGt:function gt(num, opts) {
      460. +
      461. 1 return this.__addAction(function (col) {
      462. +
      463. 3 return col > num;
      464. +
      465. }, merge({message:"{col} must be > " + num + " got {val}."}, opts));
      466. +
      467. },
      468. +
      469. +
      470. isLte:function lte(num, opts) {
      471. +
      472. 1 return this.__addAction(function (col) {
      473. +
      474. 3 return col <= num;
      475. +
      476. }, merge({message:"{col} must be <= " + num + " got {val}."}, opts));
      477. +
      478. },
      479. +
      480. +
      481. isGte:function gte(num, opts) {
      482. +
      483. 1 return this.__addAction(function (col) {
      484. +
      485. 3 return col >= num;
      486. +
      487. }, merge({message:"{col} must be >= " + num + " got {val}."}, opts));
      488. +
      489. },
      490. +
      491. +
      492. isIn:function isIn(arr, opts) {
      493. +
      494. 2 if (!isArray(arr)) {
      495. +
      496. 1 throw new Error("isIn requires an array of values");
      497. }
      498. +
      499. 1 return this.__addAction(function (col) {
      500. +
      501. 3 return arr.indexOf(col) !== -1;
      502. +
      503. }, merge({message:"{col} must be in " + arr.join(",") + " got {val}."}, opts));
      504. },
      505. -
      506. /**
      507. -
      508. * Adds to the where/having clause with an AND a group of ORed conditions wrapped in parens
      509. -
      510. *
      511. -
      512. * <p>
      513. -
      514. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      515. -
      516. * </p>
      517. -
      518. *
      519. -
      520. * @example
      521. -
      522. *
      523. -
      524. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
      525. -
      526. * //=> SELECT
      527. -
      528. * *
      529. -
      530. * FROM
      531. -
      532. * items
      533. -
      534. * WHERE
      535. -
      536. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
      537. -
      538. *
      539. -
      540. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      541. -
      542. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
      543. -
      544. */
      545. -
      546. andGroupedOr: function () {
      547. -
      548. 2 var tOpts = this.__opts,
      549. -
      550. clause = (tOpts.having ? "having" : "where"),
      551. -
      552. clauseObj = tOpts[clause];
      553. -
      554. 2 if (clauseObj) {
      555. -
      556. 1 var args = argsToArray(arguments);
      557. -
      558. 1 args = args.length == 1 ? args[0] : args;
      559. -
      560. 1 var opts = {};
      561. -
      562. 1 opts[clause] = new BooleanExpression("AND", clauseObj, this._filterExpr(args, null, "OR"));
      563. -
      564. 1 return this.mergeOptions(opts);
      565. -
      566. } else {
      567. -
      568. 1 throw new QueryError("No existing filter found");
      569. -
      570. }
      571. -
      572. },
      573. -
      574. -
      575. /**
      576. -
      577. * Adds to the where/having clause with an AND a group of ORed conditions wrapped in parens
      578. -
      579. *
      580. -
      581. * <p>
      582. -
      583. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      584. -
      585. * </p>
      586. -
      587. *
      588. -
      589. * @example
      590. -
      591. *
      592. -
      593. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
      594. -
      595. * //=> SELECT
      596. -
      597. * *
      598. -
      599. * FROM
      600. -
      601. * items
      602. -
      603. * WHERE
      604. -
      605. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
      606. -
      607. *
      608. -
      609. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      610. -
      611. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
      612. -
      613. */
      614. -
      615. andGroupedAnd: function () {
      616. -
      617. 2 var tOpts = this.__opts,
      618. -
      619. clause = (tOpts.having ? "having" : "where"),
      620. -
      621. clauseObj = tOpts[clause];
      622. -
      623. 2 if (clauseObj) {
      624. -
      625. 1 var args = argsToArray(arguments);
      626. -
      627. 1 args = args.length == 1 ? args[0] : args;
      628. -
      629. 1 var opts = {};
      630. -
      631. 1 opts[clause] = new BooleanExpression("AND", clauseObj, this._filterExpr(args, null, "AND"));
      632. -
      633. 1 return this.mergeOptions(opts);
      634. -
      635. } else {
      636. -
      637. 1 throw new QueryError("No existing filter found");
      638. -
      639. }
      640. -
      641. },
      642. -
      643. -
      644. /**
      645. -
      646. * Adds to the where/having clause with an OR a group of ANDed conditions wrapped in parens
      647. -
      648. *
      649. -
      650. * <p>
      651. -
      652. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      653. -
      654. * </p>
      655. -
      656. *
      657. -
      658. * @example
      659. -
      660. *
      661. -
      662. * DB.from("items").filter({id, [1,2,3]}).orGroupedAnd([{price: {lt : 0}}, {price: {gt: 10}]).sql;
      663. -
      664. * //=> SELECT
      665. -
      666. * *
      667. -
      668. * FROM
      669. -
      670. * items
      671. -
      672. * WHERE
      673. -
      674. * ((id IN (1, 2, 3)) OR ((price > 0) AND (price < 10)))
      675. -
      676. *
      677. -
      678. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      679. -
      680. * @return {patio.Dataset} a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.
      681. -
      682. */
      683. -
      684. orGroupedAnd: function () {
      685. -
      686. 3 return this.or.apply(this, arguments);
      687. -
      688. },
      689. -
      690. -
      691. /**
      692. -
      693. * Adds to the where/having clause with an AND a group of ORed conditions wrapped in parens
      694. -
      695. *
      696. -
      697. * <p>
      698. -
      699. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
      700. -
      701. * </p>
      702. -
      703. *
      704. -
      705. * @example
      706. -
      707. *
      708. -
      709. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
      710. -
      711. * //=> SELECT
      712. -
      713. * *
      714. -
      715. * FROM
      716. -
      717. * items
      718. -
      719. * WHERE
      720. -
      721. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
      722. -
      723. *
      724. -
      725. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
      726. -
      727. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
      728. -
      729. */
      730. -
      731. orGroupedOr: function () {
      732. -
      733. 1 var tOpts = this.__opts,
      734. -
      735. clause = (tOpts.having ? "having" : "where"),
      736. -
      737. clauseObj = tOpts[clause];
      738. -
      739. 1 if (clauseObj) {
      740. -
      741. 1 var args = argsToArray(arguments);
      742. -
      743. 1 args = args.length == 1 ? args[0] : args;
      744. -
      745. 1 var opts = {};
      746. -
      747. 1 opts[clause] = new BooleanExpression("OR", clauseObj, this._filterExpr(args, null, "OR"));
      748. -
      749. 1 return this.mergeOptions(opts);
      750. -
      751. } else {
      752. -
      753. 0 throw new QueryError("No existing filter found");
      754. -
      755. }
      756. -
      757. },
      758. -
      759. -
      760. /**
      761. -
      762. * Returns a copy of the dataset with the SQL DISTINCT clause.
      763. -
      764. * The DISTINCT clause is used to remove duplicate rows from the
      765. -
      766. * output. If arguments are provided, uses a DISTINCT ON clause,
      767. -
      768. * in which case it will only be distinct on those columns, instead
      769. -
      770. * of all returned columns.
      771. -
      772. *
      773. -
      774. * @example
      775. -
      776. *
      777. -
      778. * DB.from("items").distinct().sqll
      779. -
      780. * //=> SELECT DISTINCT * FROM items
      781. -
      782. * DB.from("items").order("id").distinct("id").sql;
      783. -
      784. * //=> SELECT DISTINCT ON (id) * FROM items ORDER BY id
      785. -
      786. *
      787. -
      788. * @throws {patio.QueryError} If arguments are given and DISTINCT ON is not supported.
      789. -
      790. * @param {...String|...patio.sql.Identifier} args variable number of arguments used to create
      791. -
      792. * the DISTINCT ON clause.
      793. -
      794. * @return {patio.Dataset} a cloned dataset with the DISTINCT/DISTINCT ON clause added.
      795. -
      796. */
      797. -
      798. distinct: function (args) {
      799. -
      800. 13 args = argsToArray(arguments);
      801. -
      802. 13 if (args.length && !this.supportsDistinctOn) {
      803. -
      804. 1 throw new QueryError("DISTICT ON is not supported");
      805. -
      806. }
      807. -
      808. 12 args = args.map(function (a) {
      809. -
      810. 6 return isString(a) ? new Identifier(a) : a;
      811. -
      812. });
      813. -
      814. 12 return this.mergeOptions({distinct: args});
      815. -
      816. },
      817. -
      818. -
      819. /**
      820. -
      821. * Adds an EXCEPT clause using a second dataset object.
      822. -
      823. * An EXCEPT compound dataset returns all rows in the current dataset
      824. -
      825. * that are not in the given dataset.
      826. -
      827. *
      828. -
      829. * @example
      830. -
      831. *
      832. -
      833. * DB.from("items").except(DB.from("other_items")).sql;
      834. -
      835. * //=> SELECT * FROM items EXCEPT SELECT * FROM other_items
      836. -
      837. *
      838. -
      839. * DB.from("items").except(DB.from("other_items"),
      840. -
      841. * {all : true, fromSelf : false}).sql;
      842. -
      843. * //=> SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
      844. -
      845. *
      846. -
      847. * DB.from("items").except(DB.from("other_items"),
      848. -
      849. * {alias : "i"}).sql;
      850. -
      851. * //=>SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS i
      852. -
      853. *
      854. -
      855. * @throws {patio.QueryError} if the operation is not supported.
      856. -
      857. * @param {patio.Dataset} dataset the dataset to use to create the EXCEPT clause.
      858. -
      859. * @param {Object} [opts] options to use when creating the EXCEPT clause
      860. -
      861. * @param {String|patio.sql.Identifier} [opt.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias.
      862. -
      863. * @param {Boolean} [opts.all] Set to true to use EXCEPT ALL instead of EXCEPT, so duplicate rows can occur
      864. -
      865. * @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}, use with care.
      866. -
      867. *
      868. -
      869. * @return {patio.Dataset} a cloned dataset with the EXCEPT clause added.
      870. -
      871. */
      872. -
      873. except: function (dataset, opts) {
      874. -
      875. 18 opts = isUndefined(opts) ? {} : opts;
      876. -
      877. 18 if (!isHash(opts)) {
      878. -
      879. 5 opts = {all: true};
      880. -
      881. }
      882. -
      883. 18 if (!this.supportsIntersectExcept) {
      884. -
      885. 2 throw new QueryError("EXCEPT not supoorted");
      886. -
      887. 16 } else if (opts.hasOwnProperty("all") && !this.supportsIntersectExceptAll) {
      888. -
      889. 1 throw new QueryError("EXCEPT ALL not supported");
      890. +
      891. isNotIn:function notIn(arr, opts) {
      892. +
      893. 2 if (!isArray(arr)) {
      894. +
      895. 1 throw new Error("notIn requires an array of values");
      896. }
      897. -
      898. 15 return this.compoundClone("except", dataset, opts);
      899. +
      900. 1 return this.__addAction(function (col) {
      901. +
      902. 3 return arr.indexOf(col) === -1;
      903. +
      904. }, merge({message:"{col} cannot be in " + arr.join(",") + " got {val}."}, opts));
      905. },
      906. -
      907. /**
      908. -
      909. * Performs the inverse of {@link patio.Dataset#filter}. Note that if you have multiple filter
      910. -
      911. * conditions, this is not the same as a negation of all conditions. For argument types see
      912. -
      913. * {@link patio.Dataset#filter}
      914. -
      915. *
      916. -
      917. * @example
      918. -
      919. *
      920. -
      921. * DB.from("items").exclude({category : "software").sql;
      922. -
      923. * //=> SELECT * FROM items WHERE (category != 'software')
      924. -
      925. *
      926. -
      927. * DB.from("items").exclude({category : 'software', id : 3}).sql;
      928. -
      929. * //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
      930. -
      931. * @return {patio.Dataset} a cloned dataset with the excluded conditions applied to the HAVING/WHERE clause.
      932. -
      933. */
      934. -
      935. exclude: function () {
      936. -
      937. 69 var cond = argsToArray(arguments), tOpts = this.__opts;
      938. -
      939. 69 var clause = (tOpts["having"] ? "having" : "where"), clauseObj = tOpts[clause];
      940. -
      941. 69 cond = cond.length > 1 ? cond : cond[0];
      942. -
      943. 69 cond = this._filterExpr.call(this, cond);
      944. -
      945. 69 cond = BooleanExpression.invert(cond);
      946. -
      947. 69 if (clauseObj) {
      948. -
      949. 58 cond = new BooleanExpression("AND", clauseObj, cond)
      950. -
      951. }
      952. -
      953. 69 var opts = {};
      954. -
      955. 69 opts[clause] = cond;
      956. -
      957. 69 return this.mergeOptions(opts);
      958. +
      959. isMacAddress:function isMaxAddress(opts) {
      960. +
      961. 1 return this.__addAction(function (col) {
      962. +
      963. 4 return !!col.match(/^([0-9A-F]{2}[:\-]){5}([0-9A-F]{2})$/);
      964. +
      965. }, merge({message:"{col} must be a valid MAC address got {val}."}, opts));
      966. },
      967. -
      968. /**
      969. -
      970. * Returns a copy of the dataset with the given conditions applied to it.
      971. -
      972. * If the query already has a HAVING clause, then the conditions are applied to the
      973. -
      974. * HAVING clause otherwise they are applied to the WHERE clause.
      975. -
      976. *
      977. -
      978. * @example
      979. -
      980. *
      981. -
      982. * DB.from("items").filter({id : 3}).sql;
      983. -
      984. * //=> SELECT * FROM items WHERE (id = 3)
      985. -
      986. *
      987. -
      988. * DB.from("items").filter('price < ?', 100)
      989. -
      990. * //=> SELECT * FROM items WHERE price < 100
      991. -
      992. *
      993. -
      994. * DB.from("items").filter({id, [1,2,3]}).filter({id : {between : [0, 10]}}).sql;
      995. -
      996. * //=> SELECT
      997. -
      998. * *
      999. -
      1000. * FROM
      1001. -
      1002. * items
      1003. -
      1004. * WHERE
      1005. -
      1006. * ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
      1007. -
      1008. *
      1009. -
      1010. * DB.from("items").filter('price < 100');
      1011. -
      1012. * //=> SELECT * FROM items WHERE price < 100
      1013. -
      1014. *
      1015. -
      1016. * DB.from("items").filter("active").sql;
      1017. -
      1018. * //=> SELECT * FROM items WHERE active
      1019. -
      1020. *
      1021. -
      1022. * DB.from("items").filter(function(){
      1023. -
      1024. * return this.price.lt(100);
      1025. -
      1026. * });
      1027. -
      1028. * //=> SELECT * FROM items WHERE (price < 100)
      1029. -
      1030. *
      1031. -
      1032. * //Multiple filter calls can be chained for scoping:
      1033. -
      1034. * DB.from("items").filter(:category => 'software').filter{price < 100}
      1035. -
      1036. * //=> SELECT * FROM items WHERE ((category = 'software') AND (price < 100))
      1037. -
      1038. *
      1039. -
      1040. *
      1041. -
      1042. * @param {Object|Array|String|patio.sql.Identifier|patio.sql.BooleanExpression} args filters to apply to the
      1043. -
      1044. * WHERE/HAVING clause. Description of each:
      1045. -
      1046. * <ul>
      1047. -
      1048. * <li>Hash - list of equality/inclusion expressions</li>
      1049. -
      1050. * <li>Array - depends:
      1051. -
      1052. * <ul>
      1053. -
      1054. * <li>If first member is a string, assumes the rest of the arguments
      1055. -
      1056. * are parameters and interpolates them into the string.</li>
      1057. -
      1058. * <li>If all members are arrays of length two, treats the same way
      1059. -
      1060. * as a hash, except it allows for duplicate keys to be
      1061. -
      1062. * specified.</li>
      1063. -
      1064. * <li>Otherwise, treats each argument as a separate condition.</li>
      1065. -
      1066. * </ul>
      1067. -
      1068. * </li>
      1069. -
      1070. * <li>String - taken literally</li>
      1071. -
      1072. * <li>{@link patio.sql.Identifier} - taken as a boolean column argument (e.g. WHERE active)</li>
      1073. -
      1074. * <li>{@link patio.sql.BooleanExpression} - an existing condition expression,
      1075. -
      1076. * probably created using the patio.sql methods.
      1077. -
      1078. * </li>
      1079. -
      1080. *
      1081. -
      1082. * @param {Function} [cb] filter also takes a cb, which should return one of the above argument
      1083. -
      1084. * types, and is treated the same way. This block is called with an {@link patio.sql} object which can be used to dynaically create expression. For more details
      1085. -
      1086. * on the sql object see {@link patio.sql}
      1087. -
      1088. *
      1089. -
      1090. * <p>
      1091. -
      1092. * <b>NOTE:</b>If both a cb and regular arguments are provided, they get ANDed together.
      1093. -
      1094. * </p>
      1095. -
      1096. *
      1097. -
      1098. * @return {patio.Dataset} a cloned dataset with the filter arumgents applied to the WHERE/HAVING clause.
      1099. -
      1100. **/
      1101. -
      1102. filter: function (args, cb) {
      1103. -
      1104. 3468 args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));
      1105. -
      1106. 3468 return this._filter.apply(this, args);
      1107. +
      1108. isIPAddress:function isIpAddress(opts) {
      1109. +
      1110. 1 return this.__addAction(function (col) {
      1111. +
      1112. 4 return !!isIP(col);
      1113. +
      1114. }, merge({message:"{col} must be a valid IPv4 or IPv6 address got {val}."}, opts));
      1115. },
      1116. -
      1117. /**
      1118. -
      1119. * @see patio.Dataset#filter
      1120. -
      1121. */
      1122. -
      1123. find: function () {
      1124. -
      1125. 30 var args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));
      1126. -
      1127. 30 return this._filter.apply(this, args);
      1128. +
      1129. isIPv4Address:function isIpAddress(opts) {
      1130. +
      1131. 1 return this.__addAction(function (col) {
      1132. +
      1133. 3 return isIPv4(col);
      1134. +
      1135. }, merge({message:"{col} must be a valid IPv4 address got {val}."}, opts));
      1136. },
      1137. -
      1138. /**
      1139. -
      1140. * @example
      1141. -
      1142. * DB.from("table").forUpdate()
      1143. -
      1144. * //=> SELECT * FROM table FOR UPDATE
      1145. -
      1146. * @return {patio.Dataset} a cloned dataset with a "update" lock style.
      1147. -
      1148. */
      1149. -
      1150. forUpdate: function () {
      1151. -
      1152. 1 return this.lockStyle("update");
      1153. +
      1154. isIPv6Address:function isIpAddress(opts) {
      1155. +
      1156. 1 return this.__addAction(function (col) {
      1157. +
      1158. 3 return isIPv6(col);
      1159. +
      1160. }, merge({message:"{col} must be a valid IPv6 address got {val}."}, opts));
      1161. },
      1162. -
      1163. /**
      1164. -
      1165. * Returns a copy of the dataset with the source changed. If no
      1166. -
      1167. * source is given, removes all tables. If multiple sources
      1168. -
      1169. * are given, it is the same as using a CROSS JOIN (cartesian product) between all tables.
      1170. -
      1171. *
      1172. -
      1173. * @example
      1174. -
      1175. * var dataset = DB.from("items");
      1176. -
      1177. *
      1178. -
      1179. * dataset.from().sql;
      1180. -
      1181. * //=> SELECT *
      1182. -
      1183. *
      1184. -
      1185. * dataset.from("blah").sql
      1186. -
      1187. * //=> SELECT * FROM blah
      1188. -
      1189. *
      1190. -
      1191. * dataset.from("blah", "foo")
      1192. -
      1193. * //=> SELECT * FROM blah, foo
      1194. -
      1195. *
      1196. -
      1197. * dataset.from({a:"b"}).sql;
      1198. -
      1199. * //=> SELECT * FROM a AS b
      1200. -
      1201. *
      1202. -
      1203. * dataset.from(dataset.from("a").group("b").as("c")).sql;
      1204. -
      1205. * //=> "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"
      1206. -
      1207. *
      1208. -
      1209. * @param {...String|...patio.sql.Identifier|...patio.Dataset|...Object} [source] tables to select from
      1210. -
      1211. *
      1212. -
      1213. * @return {patio.Dataset} a cloned dataset with the FROM clause overridden.
      1214. -
      1215. */
      1216. -
      1217. from: function (source) {
      1218. -
      1219. 821 source = argsToArray(arguments);
      1220. -
      1221. 821 var tableAliasNum = 0, sources = [];
      1222. -
      1223. 821 source.forEach(function (s) {
      1224. -
      1225. 1003 if (isInstanceOf(s, Dataset)) {
      1226. -
      1227. 86 sources.push(new AliasedExpression(s, this._datasetAlias(++tableAliasNum)));
      1228. -
      1229. 917 } else if (isHash(s)) {
      1230. -
      1231. 3 for (var i in s) {
      1232. -
      1233. 3 sources.push(new AliasedExpression(new Identifier(i), s[i]));
      1234. -
      1235. }
      1236. -
      1237. 914 } else if (isString(s)) {
      1238. -
      1239. 889 sources.push(this.stringToIdentifier(s))
      1240. -
      1241. } else {
      1242. -
      1243. 25 sources.push(s);
      1244. -
      1245. }
      1246. -
      1247. }, this);
      1248. -
      1249. -
      1250. 821 var o = {from: sources.length ? sources : null}
      1251. -
      1252. 821 if (tableAliasNum) {
      1253. -
      1254. 84 o.numDatasetSources = tableAliasNum;
      1255. -
      1256. }
      1257. -
      1258. 821 return this.mergeOptions(o)
      1259. +
      1260. isUUID:function isUUID(opts) {
      1261. +
      1262. 1 return this.__addAction(function (col) {
      1263. +
      1264. 3 return !!col.match(/^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/);
      1265. +
      1266. }, merge({message:"{col} must be a valid UUID got {val}"}, opts));
      1267. },
      1268. -
      1269. /**
      1270. -
      1271. * Returns a dataset selecting from the current dataset.
      1272. -
      1273. * Supplying the alias option controls the alias of the result.
      1274. -
      1275. *
      1276. -
      1277. * @example
      1278. -
      1279. *
      1280. -
      1281. * ds = DB.from("items").order("name").select("id", "name")
      1282. -
      1283. * //=> SELECT id,name FROM items ORDER BY name
      1284. -
      1285. *
      1286. -
      1287. * ds.fromSelf().sql;
      1288. -
      1289. * //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1
      1290. -
      1291. *
      1292. -
      1293. * ds.fromSelf({alias : "foo"}).sql;
      1294. -
      1295. * //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo
      1296. -
      1297. *
      1298. -
      1299. * @param {Object} [opts] options
      1300. -
      1301. * @param {String|patio.sql.Identifier} [opts.alias] alias to use
      1302. -
      1303. *
      1304. -
      1305. * @return {patio.Dataset} a cloned dataset with the FROM clause set as the current dataset.
      1306. -
      1307. */
      1308. -
      1309. fromSelf: function (opts) {
      1310. -
      1311. 84 opts = isUndefined(opts) ? {} : opts;
      1312. -
      1313. 84 var fs = {};
      1314. -
      1315. 84 var nonSqlOptions = this._static.NON_SQL_OPTIONS;
      1316. -
      1317. 84 Object.keys(this.__opts).forEach(function (k) {
      1318. -
      1319. 302 if (nonSqlOptions.indexOf(k) == -1) {
      1320. -
      1321. 302 fs[k] = null;
      1322. -
      1323. }
      1324. -
      1325. });
      1326. -
      1327. 84 return this.mergeOptions(fs).from(opts["alias"] ? this.as(opts["alias"]) : this);
      1328. +
      1329. isEmail:function isEmail(opts) {
      1330. +
      1331. 1 return this.__addAction(function (col) {
      1332. +
      1333. 3 return validatorCheck(col).isEmail();
      1334. +
      1335. }, merge({message:"{col} must be a valid Email Address got {val}"}, opts));
      1336. },
      1337. -
      1338. /**
      1339. -
      1340. * Match any of the columns to any of the patterns. The terms can be
      1341. -
      1342. * strings (which use LIKE) or regular expressions (which are only
      1343. -
      1344. * supported on MySQL and PostgreSQL). Note that the total number of
      1345. -
      1346. * pattern matches will be columns[].length * terms[].length,
      1347. -
      1348. * which could cause performance issues.
      1349. -
      1350. *
      1351. -
      1352. * @example
      1353. -
      1354. *
      1355. -
      1356. * DB.from("items").grep("a", "%test%").sql;
      1357. -
      1358. * //=> SELECT * FROM items WHERE (a LIKE '%test%');
      1359. -
      1360. *
      1361. -
      1362. * DB.from("items").grep(["a", "b"], ["%test%" "foo"]).sql;
      1363. -
      1364. * //=> SELECT * FROM items WHERE ((a LIKE '%test%') OR (a LIKE 'foo') OR (b LIKE '%test%') OR (b LIKE 'foo'))
      1365. -
      1366. *
      1367. -
      1368. * DB.from("items").grep(['a', 'b'], ["%foo%", "%bar%"], {allPatterns : true}).sql;
      1369. -
      1370. * //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (b LIKE '%foo%')) AND ((a LIKE '%bar%') OR (b LIKE '%bar%')))
      1371. -
      1372. *
      1373. -
      1374. * DB.from("items").grep(["a", "b"], ['%foo%", "%bar%", {allColumns : true})sql;
      1375. -
      1376. * //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (a LIKE '%bar%')) AND ((b LIKE '%foo%') OR (b LIKE '%bar%')))
      1377. -
      1378. *
      1379. -
      1380. * DB.from("items").grep(["a", "b"], ["%foo%", "%bar%"], {allPatterns : true, allColumns : true}).sql;
      1381. -
      1382. * //=> SELECT * FROM a WHERE ((a LIKE '%foo%') AND (b LIKE '%foo%') AND (a LIKE '%bar%') AND (b LIKE '%bar%'))
      1383. -
      1384. *
      1385. -
      1386. * @param {String[]|patio.sql.Identifier[]} columns columns to search
      1387. -
      1388. * @param {String|RegExp} patterns patters to search with
      1389. -
      1390. * @param {Object} [opts] options to use when searching. NOTE If both allColumns and allPatterns are true, all columns must match all patterns
      1391. -
      1392. * @param {Boolean} [opts.allColumns] All columns must be matched to any of the given patterns.
      1393. -
      1394. * @param {Boolean} [opts.allPatterns] All patterns must match at least one of the columns.
      1395. -
      1396. * @param {Boolean} [opts.caseInsensitive] Use a case insensitive pattern match (the default is
      1397. -
      1398. * case sensitive if the database supports it).
      1399. -
      1400. * @return {patio.Dataset} a dataset with the LIKE clauses added
      1401. -
      1402. */
      1403. -
      1404. -
      1405. grep: function (columns, patterns, opts) {
      1406. -
      1407. 15 opts = isUndefined(opts) ? {} : opts;
      1408. -
      1409. 15 var conds;
      1410. -
      1411. 15 if (opts.hasOwnProperty("allPatterns")) {
      1412. -
      1413. 4 conds = array.toArray(patterns).map(function (pat) {
      1414. -
      1415. 8 return BooleanExpression.fromArgs(
      1416. -
      1417. [(opts.allColumns ? "AND" : "OR")]
      1418. -
      1419. .concat(array.toArray(columns)
      1420. -
      1421. .map(function (c) {
      1422. -
      1423. 16 return StringExpression.like(c, pat, opts);
      1424. -
      1425. })));
      1426. -
      1427. });
      1428. -
      1429. 4 return this.filter(BooleanExpression.fromArgs([opts.allPatterns ? "AND" : "OR"].concat(conds)));
      1430. -
      1431. } else {
      1432. -
      1433. 11 conds = array.toArray(columns)
      1434. -
      1435. .map(function (c) {
      1436. -
      1437. 16 return BooleanExpression.fromArgs(["OR"].concat(array.toArray(patterns).map(function (pat) {
      1438. -
      1439. 26 return StringExpression.like(c, pat, opts);
      1440. -
      1441. })));
      1442. -
      1443. });
      1444. -
      1445. 11 return this.filter(BooleanExpression.fromArgs([opts.allColumns ? "AND" : "OR"].concat(conds)));
      1446. -
      1447. }
      1448. +
      1449. isUrl:function isUrl(opts) {
      1450. +
      1451. 1 return this.__addAction(function (col) {
      1452. +
      1453. 3 return validatorCheck(col).isUrl();
      1454. +
      1455. }, merge({message:"{col} must be a valid url got {val}"}, opts));
      1456. },
      1457. -
      1458. /**
      1459. -
      1460. * @see patio.Dataset#grep
      1461. -
      1462. */
      1463. -
      1464. like: function () {
      1465. -
      1466. 1 return this.grep.apply(this, arguments);
      1467. +
      1468. isAlpha:function isAlpha(opts) {
      1469. +
      1470. 2 return this.__addAction(function (col) {
      1471. +
      1472. 11 return validatorCheck(col).isAlpha();
      1473. +
      1474. }, merge({message:"{col} must be a only letters got {val}"}, opts));
      1475. },
      1476. -
      1477. -
      1478. /**
      1479. -
      1480. * Returns a copy of the dataset with the results grouped by the value of
      1481. -
      1482. * the given columns.
      1483. -
      1484. * @example
      1485. -
      1486. * DB.from("items").group("id")
      1487. -
      1488. * //=>SELECT * FROM items GROUP BY id
      1489. -
      1490. * DB.from("items").group("id", "name")
      1491. -
      1492. * //=> SELECT * FROM items GROUP BY id, name
      1493. -
      1494. * @param {String...|patio.sql.Identifier...} columns columns to group by.
      1495. -
      1496. *
      1497. -
      1498. * @return {patio.Dataset} a cloned dataset with the GROUP BY clause added.
      1499. -
      1500. **/
      1501. -
      1502. group: function (columns) {
      1503. -
      1504. 32 columns = argsToArray(arguments);
      1505. -
      1506. 32 var stringToIdentifier = this.stringToIdentifier.bind(this)
      1507. -
      1508. 32 return this.mergeOptions({group: (array.compact(columns).length == 0 ? null : columns.map(function (c) {
      1509. -
      1510. 32 return isString(c) ? stringToIdentifier(c) : c;
      1511. -
      1512. }))});
      1513. +
      1514. isAlphaNumeric:function isAlphaNumeric(opts) {
      1515. +
      1516. 1 return this.__addAction(function (col) {
      1517. +
      1518. 3 return validatorCheck(col).isAlphanumeric();
      1519. +
      1520. }, merge({message:"{col} must be a alphanumeric got {val}"}, opts));
      1521. },
      1522. -
      1523. /**
      1524. -
      1525. * @see patio.Dataset#group
      1526. -
      1527. */
      1528. -
      1529. groupBy: function () {
      1530. -
      1531. 10 return this.group.apply(this, arguments);
      1532. +
      1533. hasLength:function hasLength(min, max, opts) {
      1534. +
      1535. 2 return this.__addAction(function (col) {
      1536. +
      1537. 6 return validatorCheck(col).len(min, max);
      1538. +
      1539. }, merge({message:"{col} must have a length between " + min + (max ? " and " + max : "") + "."}, opts));
      1540. },
      1541. +
      1542. isLowercase:function isLowercase(opts) {
      1543. +
      1544. 1 return this.__addAction(function (col) {
      1545. +
      1546. 3 return validatorCheck(col).isLowercase();
      1547. +
      1548. }, merge({message:"{col} must be lowercase got {val}."}, opts));
      1549. +
      1550. },
      1551. -
      1552. /**
      1553. -
      1554. * Returns a dataset grouped by the given column with count by group.
      1555. -
      1556. * Column aliases may be supplied, and will be included in the select clause.
      1557. -
      1558. *
      1559. -
      1560. * @example
      1561. -
      1562. *
      1563. -
      1564. * DB.from("items").groupAndCount("name").all()
      1565. -
      1566. * //=> SELECT name, count(*) AS count FROM items GROUP BY name
      1567. -
      1568. * //=> [{name : 'a', count : 1}, ...]
      1569. -
      1570. *
      1571. -
      1572. * DB.from("items").groupAndCount("first_name", "last_name").all()
      1573. -
      1574. * //SELECT first_name, last_name, count(*) AS count FROM items GROUP BY first_name, last_name
      1575. -
      1576. * //=> [{first_name : 'a', last_name : 'b', count : 1}, ...]
      1577. -
      1578. *
      1579. -
      1580. * DB.from("items").groupAndCount("first_name___name").all()
      1581. -
      1582. * //=> SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
      1583. -
      1584. * //=> [{name : 'a', count:1}, ...]
      1585. -
      1586. * @param {String...|patio.sql.Identifier...} columns columns to croup and count on.
      1587. -
      1588. *
      1589. -
      1590. * @return {patio.Dataset} a cloned dataset with the GROUP clause and count added.
      1591. -
      1592. */
      1593. -
      1594. groupAndCount: function (columns) {
      1595. -
      1596. 8 columns = argsToArray(arguments);
      1597. -
      1598. 8 var group = this.group.apply(this, columns.map(function (c) {
      1599. -
      1600. 9 return this._unaliasedIdentifier(c);
      1601. -
      1602. }, this));
      1603. -
      1604. 8 return group.select.apply(group, columns.concat([this._static.COUNT_OF_ALL_AS_COUNT]));
      1605. -
      1606. +
      1607. isUppercase:function isUppercase(opts) {
      1608. +
      1609. 1 return this.__addAction(function (col) {
      1610. +
      1611. 3 return validatorCheck(col).isUppercase();
      1612. +
      1613. }, merge({message:"{col} must be uppercase got {val}."}, opts));
      1614. },
      1615. -
      1616. /**
      1617. -
      1618. * Returns a copy of the dataset with the HAVING conditions changed. See {@link patio.Dataset#filter} for argument types.
      1619. -
      1620. *
      1621. -
      1622. * @example
      1623. -
      1624. * DB.from("items").group("sum").having({sum : 10}).sql;
      1625. -
      1626. * //=> SELECT * FROM items GROUP BY sum HAVING (sum = 10)
      1627. -
      1628. *
      1629. -
      1630. * @return {patio.Dataset} a cloned dataset with HAVING clause changed or added.
      1631. -
      1632. **/
      1633. -
      1634. having: function () {
      1635. -
      1636. 12 var cond = argsToArray(arguments).map(function (s) {
      1637. -
      1638. 12 return isString(s) && s !== '' ? this.stringToIdentifier(s) : s
      1639. -
      1640. }, this);
      1641. -
      1642. 12 return this._filter.apply(this, ["having"].concat(cond));
      1643. +
      1644. isEmpty:function isEmpty(opts) {
      1645. +
      1646. 1 return this.__addAction(function (col) {
      1647. +
      1648. 3 try {
      1649. +
      1650. 3 validatorCheck(col).notEmpty();
      1651. +
      1652. 2 return false;
      1653. +
      1654. } catch (e) {
      1655. +
      1656. 1 return true;
      1657. +
      1658. }
      1659. +
      1660. }, merge({message:"{col} must be empty got {val}."}, opts));
      1661. },
      1662. -
      1663. /**
      1664. -
      1665. * Adds an INTERSECT clause using a second dataset object.
      1666. -
      1667. * An INTERSECT compound dataset returns all rows in both the current dataset
      1668. -
      1669. * and the given dataset.
      1670. -
      1671. *
      1672. -
      1673. * @example
      1674. -
      1675. *
      1676. -
      1677. * DB.from("items").intersect(DB.from("other_items")).sql;
      1678. -
      1679. * //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS t1
      1680. -
      1681. *
      1682. -
      1683. * DB.from("items").intersect(DB.from("other_items"), {all : true, fromSelf : false}).sql;
      1684. -
      1685. * //=> SELECT * FROM items INTERSECT ALL SELECT * FROM other_items
      1686. -
      1687. *
      1688. -
      1689. * DB.from("items").intersect(DB.from("other_items"), {alias : "i"}).sql;
      1690. -
      1691. * //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS i
      1692. -
      1693. *
      1694. -
      1695. * @throws {patio.QueryError} if the operation is not supported.
      1696. -
      1697. * @param {patio.Dataset} dataset the dataset to intersect
      1698. -
      1699. * @param {Object} [opts] options
      1700. -
      1701. * @param {String|patio.sql.Identifier} [opts.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias
      1702. -
      1703. * @param {Boolean} [opts.all] Set to true to use INTERSECT ALL instead of INTERSECT, so duplicate rows can occur
      1704. -
      1705. * @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}.
      1706. -
      1707. *
      1708. -
      1709. * @return {patio.Dataset} a cloned dataset with the INTERSECT clause.
      1710. -
      1711. **/
      1712. -
      1713. intersect: function (dataset, opts) {
      1714. -
      1715. 18 opts = isUndefined(opts) ? {} : opts;
      1716. -
      1717. 18 if (!isHash(opts)) {
      1718. -
      1719. 5 opts = {all: opts};
      1720. -
      1721. }
      1722. -
      1723. 18 if (!this.supportsIntersectExcept) {
      1724. -
      1725. 2 throw new QueryError("INTERSECT not supported");
      1726. -
      1727. 16 } else if (opts.all && !this.supportsIntersectExceptAll) {
      1728. -
      1729. 1 throw new QueryError("INTERSECT ALL not supported");
      1730. -
      1731. }
      1732. -
      1733. 15 return this.compoundClone("intersect", dataset, opts);
      1734. +
      1735. isNotEmpty:function isNotEmpty(opts) {
      1736. +
      1737. 2 return this.__addAction(function (col) {
      1738. +
      1739. 11 return validatorCheck(col).notEmpty();
      1740. +
      1741. }, merge({message:"{col} must not be empty."}, opts));
      1742. },
      1743. -
      1744. /**
      1745. -
      1746. * Inverts the current filter.
      1747. -
      1748. *
      1749. -
      1750. * @example
      1751. -
      1752. * DB.from("items").filter({category : 'software'}).invert()
      1753. -
      1754. * //=> SELECT * FROM items WHERE (category != 'software')
      1755. -
      1756. *
      1757. -
      1758. * @example
      1759. -
      1760. * DB.from("items").filter({category : 'software', id : 3}).invert()
      1761. -
      1762. * //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
      1763. -
      1764. *
      1765. -
      1766. * @return {patio.Dataset} a cloned dataset with the filter inverted.
      1767. -
      1768. **/
      1769. -
      1770. invert: function () {
      1771. -
      1772. 3 var having = this.__opts.having, where = this.__opts.where;
      1773. -
      1774. 3 if (!(having || where)) {
      1775. -
      1776. 1 throw new QueryError("No current filter");
      1777. -
      1778. }
      1779. -
      1780. 2 var o = {}
      1781. -
      1782. 2 if (having) {
      1783. -
      1784. 1 o.having = BooleanExpression.invert(having);
      1785. -
      1786. }
      1787. -
      1788. 2 if (where) {
      1789. -
      1790. 2 o.where = BooleanExpression.invert(where);
      1791. -
      1792. }
      1793. -
      1794. 2 return this.mergeOptions(o);
      1795. +
      1796. isCreditCard:function isCreditCard(opts) {
      1797. +
      1798. 0 return this.__addAction(function (col) {
      1799. +
      1800. 0 return validatorCheck(col).isCreditCard();
      1801. +
      1802. }, merge({message:"{col} is an invalid credit card"}, opts));
      1803. },
      1804. -
      1805. /**
      1806. -
      1807. * Returns a cloned dataset with an inner join applied.
      1808. -
      1809. *
      1810. -
      1811. * @see patio.Dataset#joinTable
      1812. -
      1813. */
      1814. -
      1815. join: function () {
      1816. -
      1817. 212 return this.innerJoin.apply(this, arguments);
      1818. +
      1819. check:function (fun, opts) {
      1820. +
      1821. 4 return this.__addAction(fun, opts);
      1822. },
      1823. +
      1824. validate:function validate(value) {
      1825. +
      1826. 218 var errOpts = {col:this.col, val:value};
      1827. +
      1828. 218 return compact(this.__actions.map(function (action) {
      1829. +
      1830. 248 var actionOpts = action.opts;
      1831. +
      1832. 248 if (!actionOpts.onlyDefined || (combIsDefined(value) &&
      1833. +
      1834. (!actionOpts.onlyNotNull || !combIsNull(value)) )) {
      1835. +
      1836. 186 var ret = null;
      1837. +
      1838. 186 try {
      1839. +
      1840. 186 if (!action.action(value)) {
      1841. +
      1842. 69 ret = format(actionOpts.message, errOpts);
      1843. +
      1844. }
      1845. +
      1846. } catch (e) {
      1847. +
      1848. 28 ret = format(actionOpts.message, errOpts);
      1849. +
      1850. }
      1851. +
      1852. 186 return ret;
      1853. +
      1854. }
      1855. +
      1856. }, this));
      1857. +
      1858. }
      1859. +
      1860. +
      1861. }
      1862. +
      1863. });
      1864. +
      1865. +
      1866. 1function shouldValidate(opts, def) {
      1867. +
      1868. 115 opts = opts || {};
      1869. +
      1870. 115 return combIsBoolean(opts.validate) ? opts.validate : def;
      1871. +
      1872. }
      1873. +
      1874. +
      1875. 1function validateHook(prop, next, opts) {
      1876. +
      1877. 115 if (shouldValidate(opts, prop) && !this.isValid()) {
      1878. +
      1879. 45 next(flatten(toArray(this.errors).map(function (entry) {
      1880. +
      1881. 64 return entry[1].map(function (err) {
      1882. +
      1883. 48 return new Error(err);
      1884. +
      1885. });
      1886. +
      1887. })));
      1888. +
      1889. } else {
      1890. +
      1891. 70 next();
      1892. +
      1893. }
      1894. +
      1895. }
      1896. +
      1897. +
      1898. 1define(null, {
      1899. +
      1900. +
      1901. instance:{
      1902. +
      1903. /**
      1904. -
      1905. * Returns a joined dataset. Uses the following arguments:
      1906. +
      1907. * A validation plugin for patio models. This plugin adds a <code>validate</code> method to each {@link patio.Model}
      1908. +
      1909. * class that adds it as a plugin. This plugin does not include most typecast checks as <code>patio</code> already checks
      1910. +
      1911. * types upon column assignment.
      1912. *
      1913. -
      1914. * @example
      1915. +
      1916. * To do single col validation
      1917. +
      1918. * {@code
      1919. *
      1920. -
      1921. * DB.from("items").joinTable("leftOuter", "categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;
      1922. -
      1923. * //=>'SELECT
      1924. -
      1925. * *
      1926. -
      1927. * FROM
      1928. -
      1929. * `items`
      1930. -
      1931. * LEFT OUTER JOIN
      1932. -
      1933. * `categories` ON (
      1934. -
      1935. * (`categories`.`categoryId` = `items`.`id`)
      1936. -
      1937. * AND
      1938. -
      1939. * (`categories`.`categoryId` IN (1,2, 3))
      1940. -
      1941. * )
      1942. -
      1943. * DB.from("items").leftOuter("categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;
      1944. -
      1945. * //=>'SELECT
      1946. -
      1947. * *
      1948. -
      1949. * FROM
      1950. -
      1951. * `items`
      1952. -
      1953. * LEFT OUTER JOIN
      1954. -
      1955. * `categories` ON (
      1956. -
      1957. * (`categories`.`categoryId` = `items`.`id`)
      1958. -
      1959. * AND
      1960. -
      1961. * (`categories`.`categoryId` IN (1,2, 3))
      1962. -
      1963. * )
      1964. +
      1965. * var Model = patio.addModel("validator", {
      1966. +
      1967. * plugins:[ValidatorPlugin]
      1968. +
      1969. * });
      1970. +
      1971. * //this ensures column assignment
      1972. +
      1973. * Model.validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);
      1974. +
      1975. * //col2 does not have to be assigned but if it is it must match /hello/ig.
      1976. +
      1977. * Model.validate("col2").like(/hello/ig);
      1978. +
      1979. * //Ensures that the emailAddress column is a valid email address.
      1980. +
      1981. * Model.validate("emailAddress").isEmailAddress();
      1982. +
      1983. * }
      1984. +
      1985. *
      1986. +
      1987. * Or you can do a mass validation through a callback.
      1988. +
      1989. * {@code
      1990. *
      1991. -
      1992. * DB.from("items").leftOuterJoin("categories", {categoryId:"id"}).sql
      1993. -
      1994. * //=> SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      1995. +
      1996. * var Model = patio.addModel("validator", {
      1997. +
      1998. * plugins:[ValidatorPlugin]
      1999. +
      2000. * });
      2001. +
      2002. * Model.validate(function(validate){
      2003. +
      2004. * //this ensures column assignment
      2005. +
      2006. * validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);
      2007. +
      2008. * //col2 does not have to be assigned but if it is it must match /hello/ig.
      2009. +
      2010. * validate("col2").isLike(/hello/ig);
      2011. +
      2012. * //Ensures that the emailAddress column is a valid email address.
      2013. +
      2014. * validate("emailAddress").isEmail();
      2015. +
      2016. * });
      2017. +
      2018. * }
      2019. *
      2020. -
      2021. * DB.from("items").rightOuterJoin("categories", {categoryId:"id"}).sql
      2022. -
      2023. * //=> SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2024. +
      2025. * To check if a {@link patio.Model} is valid you can run the <code>isValid</code> method.
      2026. *
      2027. -
      2028. * DB.from("items").fullOuterJoin("categories", {categoryId:"id"}).sql
      2029. -
      2030. * //=> SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2031. +
      2032. * {@code
      2033. +
      2034. * var model1 = new Model({col2 : 'grape', emailAddress : "test"}),
      2035. +
      2036. * model2 = new Model({col1 : "grape", col2 : "hello", emailAddress : "test@test.com"});
      2037. *
      2038. -
      2039. * DB.from("items").innerJoin("categories", {categoryId:"id"}).sql
      2040. -
      2041. * //=> SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2042. +
      2043. * model1.isValid() //false
      2044. +
      2045. * model2.isValid() //true
      2046. +
      2047. * }
      2048. *
      2049. -
      2050. * DB.from("items").leftJoin("categories", {categoryId:"id"}).sql
      2051. -
      2052. * //=> SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2053. +
      2054. * To get the errors associated with an invalid model you can access the errors property
      2055. *
      2056. -
      2057. * DB.from("items").rightJoin("categories", {categoryId:"id"}).sql
      2058. -
      2059. * //=> SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2060. +
      2061. * {@code
      2062. +
      2063. * model1.errors; //{ col1: [ 'col1 must be defined.' ],
      2064. +
      2065. * // col2: [ 'col2 must be like /hello/gi got grape.' ],
      2066. +
      2067. * // emailAddress: [ 'emailAddress must be a valid Email Address got test' ] }
      2068. +
      2069. * }
      2070. *
      2071. -
      2072. * DB.from("items").fullJoin("categories", {categoryId:"id"}).sql
      2073. -
      2074. * //=> SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."categoryId" = "items"."id")
      2075. +
      2076. * Validation is also run pre save and pre update. To prevent this you can specify the <code>validate</code> option
      2077. *
      2078. -
      2079. * DB.from("items").naturalJoin("categories").sql
      2080. -
      2081. * //=> SELECT * FROM "items" NATURAL JOIN "categories"
      2082. +
      2083. * {@code
      2084. +
      2085. * model1.save(null, {validate : false});
      2086. +
      2087. * model2.save(null, {validate : false});
      2088. +
      2089. * }
      2090. *
      2091. -
      2092. * DB.from("items").naturalLeftJoin("categories").sql
      2093. -
      2094. * //=> SELECT * FROM "items" NATURAL LEFT JOIN "categories"
      2095. +
      2096. * Or you can specify the class level properties <code>validateOnSave</code> and <code>validateOnUpdate</code>
      2097. +
      2098. * to false respectively
      2099. +
      2100. * {@code
      2101. +
      2102. * Model.validateOnSave = false;
      2103. +
      2104. * Model.validateOnUpdate = false;
      2105. +
      2106. * }
      2107. *
      2108. -
      2109. * DB.from("items").naturalRightJoin("categories").sql
      2110. -
      2111. * //=> SELECT * FROM "items" NATURAL RIGHT JOIN "categories"
      2112. +
      2113. * Avaiable validation methods are.
      2114. *
      2115. -
      2116. * DB.from("items").naturalFullJoin("categories").sql
      2117. -
      2118. * //=> SELECT * FROM "items" NATURAL FULL JOIN "categories"'
      2119. +
      2120. * <ul>
      2121. +
      2122. * <li><code>isAfter</code> : check that a date is after a specified date</li>
      2123. +
      2124. * <li><code>isBefore</code> : check that a date is after before a specified date </li>
      2125. +
      2126. * <li><code>isDefined</code> : ensure that a column is defined</li>
      2127. +
      2128. * <li><code>isNotDefined</code> : ensure that a column is not defined</li>
      2129. +
      2130. * <li><code>isNotNull</code> : ensure that a column is defined and not null</li>
      2131. +
      2132. * <li><code>isNull</code> : ensure that a column is not defined or null</li>
      2133. +
      2134. * <li><code>isEq</code> : ensure that a column equals a value <b>this uses deep equal</b></li>
      2135. +
      2136. * <li><code>isNeq</code> : ensure that a column does not equal a value <b>this uses deep equal</b></li>
      2137. +
      2138. * <li><code>isLike</code> : ensure that a column is like a value, can be a regexp or string</li>
      2139. +
      2140. * <li><code>isNotLike</code> : ensure that a column is not like a value, can be a regexp or string</li>
      2141. +
      2142. * <li><code>isLt</code> : ensure that a column is less than a value</li>
      2143. +
      2144. * <li><code>isGt</code> : ensure that a column is greater than a value</li>
      2145. +
      2146. * <li><code>isLte</code> : ensure that a column is less than or equal to a value</li>
      2147. +
      2148. * <li><code>isGte</code> : ensure that a column is greater than or equal to a value</li>
      2149. +
      2150. * <li><code>isIn</code> : ensure that a column is contained in an array of values</li>
      2151. +
      2152. * <li><code>isNotIn</code> : ensure that a column is not contained in an array of values</li>
      2153. +
      2154. * <li><code>isMacAddress</code> : ensure that a column is a valid MAC address</li>
      2155. +
      2156. * <li><code>isIPAddress</code> : ensure that a column is a valid IPv4 or IPv6 address</li>
      2157. +
      2158. * <li><code>isIPv4Address</code> : ensure that a column is a valid IPv4 address</li>
      2159. +
      2160. * <li><code>isIPv6Address</code> : ensure that a column is a valid IPv6 address</li>
      2161. +
      2162. * <li><code>isUUID</code> : ensure that a column is a valid UUID</li>
      2163. +
      2164. * <li><code>isEmail</code> : ensure that a column is a valid email address</li>
      2165. +
      2166. * <li><code>isUrl</code> : ensure than a column is a valid URL</li>
      2167. +
      2168. * <li><code>isAlpha</code> : ensure than a column is all letters</li>
      2169. +
      2170. * <li><code>isAlphaNumeric</code> : ensure than a column is all letters or numbers</li>
      2171. +
      2172. * <li><code>hasLength</code> : ensure than a column is fits within the specified length accepts a min and optional max value</li>
      2173. +
      2174. * <li><code>isLowercase</code> : ensure than a column is lowercase</li>
      2175. +
      2176. * <li><code>isUppercase</code> : ensure than a column is uppercase</li>
      2177. +
      2178. * <li><code>isEmpty</code> : ensure than a column empty (i.e. a blank string)</li>
      2179. +
      2180. * <li><code>isNotEmpty</code> : ensure than a column not empty (i.e. not a blank string)</li>
      2181. +
      2182. * <li><code>isCreditCard</code> : ensure than a is a valid credit card number</li>
      2183. +
      2184. * <li><code>check</code> : accepts a function to perform validation</li>
      2185. +
      2186. * </ul>
      2187. *
      2188. -
      2189. * DB.from("items").crossJoin("categories").sql
      2190. -
      2191. * //=> SELECT * FROM "items" CROSS JOIN "categories"
      2192. +
      2193. * All of the validation methods are chainable, and accept an options argument.
      2194. *
      2195. -
      2196. * @param {String} type the type of join to do.
      2197. -
      2198. * @param {String|patio.sql.Identifier|patio.Dataset|Object} table depends on the type.
      2199. -
      2200. * <ul>
      2201. -
      2202. * <li>{@link patio.Dataset} - a subselect is performed with an alias</li>
      2203. -
      2204. * <li>Object - an object that has a tableName property.</li>
      2205. -
      2206. * <li>String|{@link patio.sql.Identifier} - the name of the table</li>
      2207. -
      2208. * </ul>
      2209. -
      2210. * @param [expr] - depends on type
      2211. +
      2212. * The options include
      2213. * <ul>
      2214. -
      2215. * <li>Object|Array of two element arrays - Assumes key (1st arg) is column of joined table (unless already
      2216. -
      2217. * qualified), and value (2nd arg) is column of the last joined or primary table (or the
      2218. -
      2219. * implicitQualifier option</li>.
      2220. -
      2221. * <li>Array - If all members of the array are string or {@link patio.sql.Identifier}, considers
      2222. -
      2223. * them as columns and uses a JOIN with a USING clause. Most databases will remove duplicate columns from
      2224. -
      2225. * the result set if this is used.</li>
      2226. -
      2227. * <li>null|undefined(not passed in) - If a cb is not given, doesn't use ON or USING, so the JOIN should be a NATURAL
      2228. -
      2229. * or CROSS join. If a block is given, uses an ON clause based on the block, see below.</li>
      2230. -
      2231. * <li>Everything else - pretty much the same as a using the argument in a call to {@link patio.Dataset#filter},
      2232. -
      2233. * so strings are considered literal, {@link patio.sql.Identifiers} specify boolean columns, and patio.sql
      2234. -
      2235. * expressions can be used. Uses a JOIN with an ON clause.</li>
      2236. +
      2237. * <li><code>message</code> : a message to return if a column fails validation. The message can include <code>{val}</code> and <code>{col}</code>
      2238. +
      2239. * replacements which will insert the invalid value and the column name.
      2240. +
      2241. * </li>
      2242. +
      2243. * <li><code>[onlyDefined=true]</code> : set to false to run the method even if the column value is not defined.</li>
      2244. +
      2245. * <li><code>[onlyNotNull=true]</code> : set to false to run the method even if the column value is null.</li>
      2246. * </ul>
      2247. -
      2248. * @param {Object} options an object of options.
      2249. -
      2250. * @param {String|patio.sql.Identifier} [options.tableAlias=undefined] the name of the table's alias when joining, necessary for joining
      2251. -
      2252. * to the same table more than once. No alias is used by default.
      2253. -
      2254. * @param {String|patio.sql.Identifier} [options.implicitQualifier=undefined] The name to use for qualifying implicit conditions. By default,
      2255. -
      2256. * the last joined or primary table is used.
      2257. -
      2258. * @param {Function} [cb] cb - The cb argument should only be given if a JOIN with an ON clause is used,
      2259. -
      2260. * in which case it is called with
      2261. -
      2262. * <ul>
      2263. -
      2264. * <li>table alias/name for the table currently being joined</li>
      2265. -
      2266. * <li> the table alias/name for the last joined (or first table)
      2267. -
      2268. * <li>array of previous</li>
      2269. -
      2270. * </ul>
      2271. -
      2272. * the cb should return an expression to be used in the ON clause.
      2273. *
      2274. -
      2275. * @return {patio.Dataset} a cloned dataset joined using the arguments.
      2276. +
      2277. *
      2278. +
      2279. * @constructs
      2280. +
      2281. * @name ValidatorPlugin
      2282. +
      2283. * @memberOf patio.plugins
      2284. +
      2285. * @property {Object} [errors={}] the validation errors for this model.
      2286. +
      2287. *
      2288. */
      2289. +
      2290. constructor:function () {
      2291. +
      2292. 114 this._super(arguments);
      2293. +
      2294. 114 this.errors = {};
      2295. +
      2296. },
      2297. -
      2298. joinTable: function (type, table, expr, options, cb) {
      2299. -
      2300. 634 var args = argsToArray(arguments);
      2301. -
      2302. 634 if (isFunction(args[args.length - 1])) {
      2303. -
      2304. 12 cb = args[args.length - 1];
      2305. -
      2306. 12 args.pop();
      2307. -
      2308. } else {
      2309. -
      2310. 622 cb = null;
      2311. -
      2312. }
      2313. -
      2314. 634 type = args.shift(), table = args.shift(), expr = args.shift(), options = args.shift();
      2315. -
      2316. 634 expr = isUndefined(expr) ? null : expr, options = isUndefined(options) ? {} : options;
      2317. +
      2318. /**
      2319. +
      2320. * Validates a model, returning an array of error messages for each invalid property.
      2321. +
      2322. * @return {String[]} an array of error messages for each invalid property.
      2323. +
      2324. */
      2325. +
      2326. validate:function () {
      2327. +
      2328. 159 this.errors = {};
      2329. +
      2330. 159 return flatten(this._static.validators.map(function runValidator(validator) {
      2331. +
      2332. 218 var col = validator.col, val = this.__values[validator.col], ret = validator.validate(val);
      2333. +
      2334. 218 this.errors[col] = ret;
      2335. +
      2336. 218 return ret;
      2337. +
      2338. }, this));
      2339. +
      2340. },
      2341. -
      2342. 634 var h;
      2343. -
      2344. 634 var usingJoin = isArray(expr) && expr.length && expr.every(function (x) {
      2345. -
      2346. 223 return isString(x) || isInstanceOf(x, Identifier)
      2347. -
      2348. });
      2349. -
      2350. 634 if (usingJoin && !this.supportsJoinUsing) {
      2351. -
      2352. 1 h = {};
      2353. -
      2354. 1 expr.forEach(function (s) {
      2355. -
      2356. 1 h[s] = s;
      2357. -
      2358. });
      2359. -
      2360. 1 return this.joinTable(type, table, h, options);
      2361. -
      2362. }
      2363. -
      2364. 633 var tableAlias, lastAlias;
      2365. -
      2366. 633 if (isHash(options)) {
      2367. -
      2368. 623 tableAlias = options.tableAlias;
      2369. -
      2370. 623 lastAlias = options.implicitQualifier;
      2371. -
      2372. 10 } else if (isString(options) || isInstanceOf(options, Identifier)) {
      2373. -
      2374. 9 tableAlias = options;
      2375. -
      2376. 9 lastAlias = null;
      2377. -
      2378. } else {
      2379. -
      2380. 1 throw new QueryError("Invalid options format for joinTable %j4", [options]);
      2381. -
      2382. }
      2383. -
      2384. 632 var tableAliasNum, tableName;
      2385. -
      2386. 632 if (isInstanceOf(table, Dataset)) {
      2387. -
      2388. 11 if (!tableAlias) {
      2389. -
      2390. 6 tableAliasNum = (this.__opts.numDatasetSources || 0) + 1;
      2391. -
      2392. 6 tableAlias = this._datasetAlias(tableAliasNum);
      2393. -
      2394. }
      2395. -
      2396. 11 tableName = tableAlias;
      2397. -
      2398. } else {
      2399. -
      2400. 621 if (!isUndefined(table.tableName)) {
      2401. -
      2402. 1 table = table.tableName;
      2403. -
      2404. }
      2405. -
      2406. 621 if (isArray(table)) {
      2407. -
      2408. 2 table = table.map(this.stringToIdentifier, this);
      2409. -
      2410. } else {
      2411. -
      2412. 619 table = isString(table) ? this.stringToIdentifier(table) : table;
      2413. -
      2414. 619 var parts = this._splitAlias(table), implicitTableAlias = parts[1];
      2415. -
      2416. 619 table = parts[0]
      2417. -
      2418. 619 tableAlias = tableAlias || implicitTableAlias;
      2419. -
      2420. 619 tableName = tableAlias || table;
      2421. -
      2422. }
      2423. -
      2424. }
      2425. -
      2426. 632 var join;
      2427. -
      2428. 632 if (!expr && !cb) {
      2429. -
      2430. 22 join = new JoinClause(type, table, tableAlias);
      2431. -
      2432. 610 } else if (usingJoin) {
      2433. -
      2434. 9 if (cb) {
      2435. -
      2436. 1 throw new QueryError("cant use a cb if an array is given");
      2437. -
      2438. }
      2439. -
      2440. 8 join = new JoinUsingClause(expr, type, table, tableAlias);
      2441. -
      2442. } else {
      2443. -
      2444. 601 lastAlias = lastAlias || this.__opts["lastJoinedTable"] || this.firstSourceAlias;
      2445. -
      2446. 600 if (Expression.isConditionSpecifier(expr)) {
      2447. -
      2448. 588 var newExpr = [];
      2449. -
      2450. 588 for (var i in expr) {
      2451. -
      2452. 909 var val = expr[i];
      2453. -
      2454. 909 if (isArray(val) && val.length == 2) {
      2455. -
      2456. 418 i = val[0], val = val[1];
      2457. -
      2458. }
      2459. -
      2460. 909 var k = this.qualifiedColumnName(i, tableName), v;
      2461. -
      2462. 909 if (isInstanceOf(val, Identifier)) {
      2463. -
      2464. 405 v = val.qualify(lastAlias);
      2465. -
      2466. } else {
      2467. -
      2468. 504 v = val;
      2469. -
      2470. }
      2471. -
      2472. 909 newExpr.push([k, v]);
      2473. -
      2474. }
      2475. -
      2476. 588 expr = newExpr;
      2477. -
      2478. }
      2479. -
      2480. 600 if (isFunction(cb)) {
      2481. -
      2482. 11 var expr2 = cb.apply(sql, [tableName, lastAlias, this.__opts.join || []]);
      2483. -
      2484. 11 expr = expr ? new BooleanExpression("AND", expr, expr2) : expr2;
      2485. -
      2486. }
      2487. -
      2488. 600 join = new JoinOnClause(expr, type, table, tableAlias);
      2489. -
      2490. }
      2491. -
      2492. 630 var opts = {join: (this.__opts.join || []).concat([join]), lastJoinedTable: tableName};
      2493. -
      2494. 630 if (tableAliasNum) {
      2495. -
      2496. 6 opts.numDatasetSources = tableAliasNum;
      2497. +
      2498. /**
      2499. +
      2500. * Returns if this model passes validation.
      2501. +
      2502. *
      2503. +
      2504. * @return {Boolean}
      2505. +
      2506. */
      2507. +
      2508. isValid:function () {
      2509. +
      2510. 159 return this.validate().length === 0;
      2511. +
      2512. }
      2513. +
      2514. },
      2515. +
      2516. +
      2517. "static":{
      2518. +
      2519. /**@lends patio.plugins.ValidatorPlugin*/
      2520. +
      2521. +
      2522. /**
      2523. +
      2524. * Set to false to prevent model validation when saving.
      2525. +
      2526. * @default true
      2527. +
      2528. */
      2529. +
      2530. validateOnSave:true,
      2531. +
      2532. +
      2533. /**
      2534. +
      2535. * Set to false to prevent model validation when updating.
      2536. +
      2537. * @default true
      2538. +
      2539. */
      2540. +
      2541. validateOnUpdate:true,
      2542. +
      2543. +
      2544. init:function () {
      2545. +
      2546. 35 this._super(arguments);
      2547. +
      2548. },
      2549. +
      2550. +
      2551. __initValidation:function () {
      2552. +
      2553. 43 if (!this.__isValidationInited) {
      2554. +
      2555. 34 this.validators = [];
      2556. +
      2557. 34 this.pre("save", function preSaveValidate(next, opts) {
      2558. +
      2559. 114 validateHook.call(this, this._static.validateOnSave, next, opts);
      2560. +
      2561. });
      2562. +
      2563. 34 this.pre("update", function preUpdateValidate(next, opts) {
      2564. +
      2565. 1 validateHook.call(this, this._static.validateOnSave, next, opts);
      2566. +
      2567. });
      2568. +
      2569. 34 this.__isValidationInited = true;
      2570. }
      2571. -
      2572. 630 return this.mergeOptions(opts);
      2573. +
      2574. },
      2575. +
      2576. __getValidator:function validator(name) {
      2577. +
      2578. 44 var ret = new Validator(name);
      2579. +
      2580. 44 this.validators.push(ret);
      2581. +
      2582. 44 return ret;
      2583. },
      2584. /**
      2585. -
      2586. * If given an integer, the dataset will contain only the first l results.
      2587. -
      2588. If a second argument is given, it is used as an offset. To use
      2589. -
      2590. * an offset without a limit, pass null as the first argument.
      2591. +
      2592. * Sets up validation for a model.
      2593. *
      2594. -
      2595. * @example
      2596. +
      2597. * To do single col validation
      2598. +
      2599. * {@code
      2600. *
      2601. -
      2602. * DB.from("items").limit(10)
      2603. -
      2604. * //=> SELECT * FROM items LIMIT 10
      2605. -
      2606. * DB.from("items").limit(10, 20)
      2607. -
      2608. * //=> SELECT * FROM items LIMIT 10 OFFSET 20
      2609. -
      2610. * DB.from("items").limit([3, 7]).sql
      2611. -
      2612. * //=> SELECT * FROM items LIMIT 5 OFFSET 3');
      2613. -
      2614. * DB.from("items").limit(null, 20)
      2615. -
      2616. * //=> SELECT * FROM items OFFSET 20
      2617. +
      2618. * var Model = patio.addModel("validator", {
      2619. +
      2620. * plugins:[ValidatorPlugin]
      2621. +
      2622. * });
      2623. +
      2624. * //this ensures column assignment
      2625. +
      2626. * Model.validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);
      2627. +
      2628. * //col2 does not have to be assigned but if it is it must match /hello/ig.
      2629. +
      2630. * Model.validate("col2").like(/hello/ig);
      2631. +
      2632. * //Ensures that the emailAddress column is a valid email address.
      2633. +
      2634. * Model.validate("emailAddress").isEmailAddress();
      2635. +
      2636. * }
      2637. *
      2638. -
      2639. * DB.from("items").limit('6', sql['a() - 1']).sql
      2640. -
      2641. * => 'SELECT * FROM items LIMIT 6 OFFSET a() - 1');
      2642. +
      2643. * Or you can do a mass validation through a callback.
      2644. +
      2645. * {@code
      2646. *
      2647. -
      2648. * @param {Number|String|Number[]} limit the limit to apply
      2649. -
      2650. * @param {Number|String|patio.sql.LiteralString} offset the offset to apply
      2651. +
      2652. * var Model = patio.addModel("validator", {
      2653. +
      2654. * plugins:[ValidatorPlugin]
      2655. +
      2656. * });
      2657. +
      2658. * Model.validate(function(validate){
      2659. +
      2660. * //this ensures column assignment
      2661. +
      2662. * validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);
      2663. +
      2664. * //col2 does not have to be assigned but if it is it must match /hello/ig.
      2665. +
      2666. * validate("col2").isLike(/hello/ig);
      2667. +
      2668. * //Ensures that the emailAddress column is a valid email address.
      2669. +
      2670. * validate("emailAddress").isEmail();
      2671. +
      2672. * });
      2673. +
      2674. * }
      2675. *
      2676. -
      2677. * @return {patio.Dataset} a cloned dataset witht the LIMIT and OFFSET applied.
      2678. -
      2679. **/
      2680. -
      2681. limit: function (limit, offset) {
      2682. -
      2683. 46 if (this.__opts.sql) {
      2684. -
      2685. 7 return this.fromSelf().limit(limit, offset);
      2686. -
      2687. }
      2688. -
      2689. 39 if (isArray(limit) && limit.length == 2) {
      2690. -
      2691. 1 offset = limit[0];
      2692. -
      2693. 1 limit = limit[1] - limit[0] + 1;
      2694. -
      2695. }
      2696. -
      2697. 39 if (isString(limit) || isInstanceOf(limit, LiteralString)) {
      2698. -
      2699. 2 limit = parseInt("" + limit, 10);
      2700. -
      2701. }
      2702. -
      2703. 39 if (isNumber(limit) && limit < 1) {
      2704. -
      2705. 2 throw new QueryError("Limit must be >= 1");
      2706. +
      2707. *
      2708. +
      2709. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
      2710. +
      2711. *
      2712. +
      2713. * @throws {patio.ModelError} if name is not a function or string.
      2714. +
      2715. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
      2716. +
      2717. */
      2718. +
      2719. validate:function (name) {
      2720. +
      2721. 43 this.__initValidation();
      2722. +
      2723. 43 var ret;
      2724. +
      2725. 43 if (isFunction(name)) {
      2726. +
      2727. 1 name.apply(this, [this.__getValidator.bind(this)]);
      2728. +
      2729. 1 ret = this;
      2730. +
      2731. 42 } else if (isString(name)) {
      2732. +
      2733. 42 ret = this.__getValidator(name);
      2734. +
      2735. } else {
      2736. +
      2737. 0 throw new ModelError("name is must be a string or function when validating");
      2738. }
      2739. -
      2740. 37 var opts = {limit: limit};
      2741. -
      2742. 37 if (offset) {
      2743. -
      2744. 9 if (isString(offset) || isInstanceOf(offset, LiteralString)) {
      2745. -
      2746. 1 offset = parseInt("" + offset, 10);
      2747. -
      2748. 1 isNaN(offset) && (offset = 0);
      2749. +
      2750. 43 return ret;
      2751. +
      2752. }
      2753. +
      2754. }
      2755. +
      2756. +
      2757. }).as(module);
      2758. +
      +
    + +
    + + + + + +
    +
    dataset/query.js
    +
    +
    + Coverage97.82 + SLOC2347 + LOC458 + Missed10 +
    +
    +
    1. 1var comb = require("comb"),
    2. +
    3. array = comb.array,
    4. +
    5. flatten = array.flatten,
    6. +
    7. compact = array.compact,
    8. +
    9. define = comb.define,
    10. +
    11. argsToArray = comb.argsToArray,
    12. +
    13. isString = comb.isString,
    14. +
    15. isEmpty = comb.isEmpty,
    16. +
    17. isNull = comb.isNull,
    18. +
    19. isBoolean = comb.isBoolean,
    20. +
    21. isNumber = comb.isNumber,
    22. +
    23. merge = comb.merge,
    24. +
    25. isArray = comb.isArray,
    26. +
    27. isObject = comb.isObject,
    28. +
    29. isFunction = comb.isFunction,
    30. +
    31. isUndefined = comb.isUndefined,
    32. +
    33. isHash = comb.isHash,
    34. +
    35. isInstanceOf = comb.isInstanceOf,
    36. +
    37. sql = require("../sql").sql,
    38. +
    39. LiteralString = sql.LiteralString,
    40. +
    41. Expression = sql.Expression,
    42. +
    43. ComplexExpression = sql.ComplexExpression,
    44. +
    45. BooleanExpression = sql.BooleanExpression,
    46. +
    47. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    48. +
    49. Identifier = sql.Identifier,
    50. +
    51. QualifiedIdentifier = sql.QualifiedIdentifier,
    52. +
    53. AliasedExpression = sql.AliasedExpression,
    54. +
    55. StringExpression = sql.StringExpression,
    56. +
    57. NumericExpression = sql.NumericExpression,
    58. +
    59. OrderedExpression = sql.OrderedExpression,
    60. +
    61. JoinClause = sql.JoinClause,
    62. +
    63. JoinOnClause = sql.JoinOnClause,
    64. +
    65. JoinUsingClause = sql.JoinUsingClause,
    66. +
    67. ColumnAll = sql.ColumnAll,
    68. +
    69. QueryError = require("../errors").QueryError;
    70. +
    71. +
    72. +
    73. 1var Dataset;
    74. +
    75. +
    76. 1function conditionedJoin(type) {
    77. +
    78. 539 var args = argsToArray(arguments, 1);
    79. +
    80. 539 return this.joinTable.apply(this, [type].concat(args));
    81. +
    82. }
    83. +
    84. +
    85. 1function unConditionJoin(type, table) {
    86. +
    87. 6 var args = argsToArray(arguments, 1);
    88. +
    89. 6 return this.joinTable.apply(this, [type, table]);
    90. +
    91. }
    92. +
    93. +
    94. +
    95. 1define(null, {
    96. +
    97. /**@ignore*/
    98. +
    99. instance: {
    100. +
    101. +
    102. /**@lends patio.Dataset.prototype*/
    103. +
    104. +
    105. /**
    106. +
    107. * @ignore
    108. +
    109. */
    110. +
    111. constructor: function () {
    112. +
    113. 25745 !Dataset && (Dataset = require("../index").Dataset);
    114. +
    115. 25745 this._super(arguments);
    116. +
    117. 25745 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
    118. +
    119. 180215 if (!this[type + "Join"]) {
    120. +
    121. 180215 this[type + "Join"] = conditionedJoin.bind(this, type);
    122. }
    123. -
    124. 9 if (isNumber(offset) && offset < 0) {
    125. -
    126. 1 throw new QueryError("Offset must be >= 0");
    127. +
    128. +
    129. }, this);
    130. +
    131. 25745 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
    132. +
    133. 128725 if (!this[type + "Join"]) {
    134. +
    135. 128725 this[type + "Join"] = unConditionJoin.bind(this, type);
    136. }
    137. -
    138. 8 opts.offset = offset;
    139. +
    140. +
    141. }, this);
    142. +
    143. },
    144. +
    145. +
    146. /**
    147. +
    148. * Adds a RETURNING clause, which is not supported by all databases. If returning is
    149. +
    150. * used instead of returning the autogenerated primary key or update/delete returning the number of rows modified.
    151. +
    152. *
    153. +
    154. * @example
    155. +
    156. *
    157. +
    158. * ds.from("items").returning() //"RETURNING *"
    159. +
    160. * ds.from("items").returning(null) //"RETURNING NULL"
    161. +
    162. * ds.from("items").returning("id", "name") //"RETURNING id, name"
    163. +
    164. * ds.from("items").returning(["id", "name"]) //"RETURNING id, name"
    165. +
    166. *
    167. +
    168. * @param values columns to return. If values is an array then the array is assumed to contain the columns to
    169. +
    170. * return. Otherwise the arguments will be used.
    171. +
    172. * @return {patio.Dataset} a new dataset with the retuning option added.
    173. +
    174. */
    175. +
    176. returning: function (values) {
    177. +
    178. 1087 var args;
    179. +
    180. 1087 if (Array.isArray(values)) {
    181. +
    182. 0 args = values;
    183. +
    184. } else {
    185. +
    186. 1087 args = argsToArray(arguments);
    187. }
    188. -
    189. 36 return this.mergeOptions(opts);
    190. +
    191. 1087 return this.mergeOptions({returning: args.map(function (v) {
    192. +
    193. 1005 return isString(v) ? sql.stringToIdentifier(v) : v;
    194. +
    195. })});
    196. },
    197. +
    198. /**
    199. +
    200. * Adds a further filter to an existing filter using AND. This method is identical to {@link patio.Dataset#filter}
    201. +
    202. * except it expects an existing filter.
    203. *
    204. -
    205. * Returns a cloned dataset with a not equal expression added to the WHERE
    206. -
    207. * clause.
    208. +
    209. * <p>
    210. +
    211. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    212. +
    213. * </p>
    214. *
    215. * @example
    216. -
    217. * DB.from("test").neq({x : 1});
    218. -
    219. * //=> SELECT * FROM test WHERE (x != 1)
    220. -
    221. * DB.from("test").neq({x : 1, y : 10});
    222. -
    223. * //=> SELECT * FROM test WHERE ((x != 1) AND (y != 10))
    224. +
    225. * DB.from("table").filter("a").and("b").sql;
    226. +
    227. * //=>SELECT * FROM table WHERE a AND b
    228. *
    229. -
    230. * @param {Object} obj object used to create the not equal expression
    231. +
    232. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
    233. *
    234. -
    235. * @return {patio.Dataset} a cloned dataset with the not equal expression added to the WHERE clause.
    236. +
    237. * @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.
    238. */
    239. -
    240. neq: function (obj) {
    241. -
    242. 2 return this.filter(this.__createBoolExpression("neq", obj));
    243. +
    244. and: function () {
    245. +
    246. 7 var tOpts = this.__opts, clauseObj = tOpts[tOpts.having ? "having" : "where"];
    247. +
    248. 7 if (clauseObj) {
    249. +
    250. 6 return this.filter.apply(this, arguments);
    251. +
    252. } else {
    253. +
    254. 1 throw new QueryError("No existing filter found");
    255. +
    256. }
    257. +
    258. },
    259. +
    260. +
    261. as: function (alias) {
    262. +
    263. 8 return new AliasedExpression(this, alias);
    264. },
    265. /**
    266. +
    267. * Adds an alternate filter to an existing WHERE/HAVING using OR.
    268. *
    269. -
    270. * Returns a cloned dataset with an equal expression added to the WHERE
    271. -
    272. * clause.
    273. +
    274. * <p>
    275. +
    276. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    277. +
    278. * </p>
    279. *
    280. * @example
    281. -
    282. * DB.from("test").eq({x : 1});
    283. -
    284. * //=> SELECT * FROM test WHERE (x = 1)
    285. -
    286. * DB.from("test").eq({x : 1, y : 10});
    287. -
    288. * //=> SELECT * FROM test WHERE ((x = 1) AND (y = 10))
    289. *
    290. -
    291. * @param {Object} obj object used to create the equal expression
    292. +
    293. * DB.from("items").filter("a").or("b")
    294. +
    295. * //=> SELECT * FROM items WHERE a OR b
    296. *
    297. -
    298. * @return {patio.Dataset} a cloned dataset with the equal expression added to the WHERE clause.
    299. +
    300. * @throws {patio.QueryError} If no WHERE?HAVING clause exists.
    301. +
    302. * @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.
    303. */
    304. -
    305. eq: function (obj) {
    306. -
    307. 2 return this.filter(this.__createBoolExpression("eq", obj));
    308. +
    309. or: function () {
    310. +
    311. 10 var tOpts = this.__opts;
    312. +
    313. 10 var clause = (tOpts.having ? "having" : "where"), clauseObj = tOpts[clause];
    314. +
    315. 10 if (clauseObj) {
    316. +
    317. 9 var args = argsToArray(arguments);
    318. +
    319. 9 args = args.length == 1 ? args[0] : args;
    320. +
    321. 9 var opts = {};
    322. +
    323. 9 opts[clause] = new BooleanExpression("OR", clauseObj, this._filterExpr(args));
    324. +
    325. 9 return this.mergeOptions(opts);
    326. +
    327. } else {
    328. +
    329. 1 throw new QueryError("No existing filter found");
    330. +
    331. }
    332. },
    333. /**
    334. +
    335. * Adds a group of ORed conditions wrapped in parens, connected to an existing where/having clause by an AND.
    336. +
    337. * If the where/having clause doesn't yet exist, a where clause is created with the ORed group.
    338. *
    339. -
    340. * Returns a cloned dataset with a greater than expression added to the WHERE
    341. -
    342. * clause.
    343. +
    344. * <p>
    345. +
    346. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    347. +
    348. * </p>
    349. *
    350. * @example
    351. -
    352. * DB.from("test").gt({x : 1});
    353. -
    354. * //=> SELECT * FROM test WHERE (x > 1)
    355. -
    356. * DB.from("test").gt({x : 1, y : 10});
    357. -
    358. * //=> SELECT * FROM test WHERE ((x > 1) AND (y > 10))
    359. *
    360. -
    361. * @param {Object} obj object used to create the greater than expression.
    362. +
    363. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    364. +
    365. * //=> SELECT
    366. +
    367. * *
    368. +
    369. * FROM
    370. +
    371. * items
    372. +
    373. * WHERE
    374. +
    375. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
    376. *
    377. -
    378. * @return {patio.Dataset} a cloned dataset with the greater than expression added to the WHERE clause.
    379. +
    380. * DB.from("items").andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    381. +
    382. * //=> SELECT
    383. +
    384. * *
    385. +
    386. * FROM
    387. +
    388. * items
    389. +
    390. * WHERE
    391. +
    392. * ((price < 0) OR (price > 10))
    393. +
    394. *
    395. +
    396. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
    397. */
    398. -
    399. gt: function (obj) {
    400. -
    401. 2 return this.filter(this.__createBoolExpression("gt", obj));
    402. +
    403. andGroupedOr: function (filterExp) {
    404. +
    405. 2 return this._addGroupedCondition("AND", "OR", filterExp);
    406. },
    407. /**
    408. +
    409. * Adds a group of ANDed conditions wrapped in parens to an existing where/having clause by an AND. If there isn't
    410. +
    411. * yet a clause, a where clause is created with the ANDed conditions
    412. *
    413. -
    414. * Returns a cloned dataset with a less than expression added to the WHERE
    415. -
    416. * clause.
    417. +
    418. * <p>
    419. +
    420. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    421. +
    422. * </p>
    423. *
    424. * @example
    425. -
    426. * DB.from("test").lt({x : 1});
    427. -
    428. * //=> SELECT * FROM test WHERE (x < 1)
    429. -
    430. * DB.from("test").lt({x : 1, y : 10});
    431. -
    432. * //=> SELECT * FROM test WHERE ((x < 1) AND (y < 10))
    433. *
    434. -
    435. * @param {Object} obj object used to create the less than expression.
    436. +
    437. * DB.from("items").filter({id, [1,2,3]}).andGroupedAnd([{price: {gt : 0}}, {price: {lt: 10}]).sql;
    438. +
    439. * //=> SELECT
    440. +
    441. * *
    442. +
    443. * FROM
    444. +
    445. * items
    446. +
    447. * WHERE
    448. +
    449. * ((id IN (1, 2, 3)) AND ((price > 0) AND (price < 10)))
    450. *
    451. -
    452. * @return {patio.Dataset} a cloned dataset with the less than expression added to the WHERE clause.
    453. +
    454. * DB.from("items").andGroupedAnd([{price: {gt : 0}}, {price: {lt: 10}]).sql;
    455. +
    456. * //=> SELECT
    457. +
    458. * *
    459. +
    460. * FROM
    461. +
    462. * items
    463. +
    464. * WHERE
    465. +
    466. * ((price > 0) AND (price < 10))
    467. +
    468. *
    469. +
    470. * @return {patio.Dataset} a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.
    471. */
    472. -
    473. lt: function (obj) {
    474. -
    475. 2 return this.filter(this.__createBoolExpression("lt", obj));
    476. +
    477. andGroupedAnd: function (filterExp) {
    478. +
    479. 2 return this._addGroupedCondition("AND", "AND", filterExp);
    480. },
    481. /**
    482. -
    483. * Returnes a cloned dataset with the IS NOT expression added to the WHERE
    484. -
    485. * clause.
    486. +
    487. * Adds a group of ANDed conditions wrapped in parens to an existing where/having clause by an OR. If there isn't
    488. +
    489. * a where/having clause, a where clause is created with the ANDed conditions.
    490. *
    491. -
    492. * @example
    493. +
    494. * <p>
    495. +
    496. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    497. +
    498. * </p>
    499. *
    500. -
    501. * DB.from("test").isNot({boolFlag : null});
    502. -
    503. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL);
    504. -
    505. * DB.from("test").isNot({boolFlag : false, otherFlag : true, name : null});
    506. -
    507. * => SELECT * FROM test WHERE ((boolFlag IS NOT FALSE) AND (otherFlag IS NOT TRUE) AND (name IS NOT NULL));
    508. +
    509. * @example
    510. *
    511. -
    512. * @param {Object} obj object used to create the IS NOT expression for.
    513. +
    514. * DB.from("items").filter({id, [1,2,3]}).orGroupedAnd([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    515. +
    516. * //=> SELECT
    517. +
    518. * *
    519. +
    520. * FROM
    521. +
    522. * items
    523. +
    524. * WHERE
    525. +
    526. * ((id IN (1, 2, 3)) OR ((price > 0) AND (price < 10)))
    527. *
    528. -
    529. * @return {patio.Dataset} a cloned dataset with the IS NOT expression added to the WHERE clause.
    530. +
    531. * DB.from("items").orGroupedAnd([{price: {gt : 0}}, {price: {gt: 10}]).sql;
    532. +
    533. * //=> SELECT
    534. +
    535. * *
    536. +
    537. * FROM
    538. +
    539. * items
    540. +
    541. * WHERE
    542. +
    543. * ((price > 0) AND (price < 10))
    544. *
    545. +
    546. * @return {patio.Dataset} a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.
    547. */
    548. -
    549. isNot: function (obj) {
    550. -
    551. 4 return this.filter(this.__createBoolExpression("isNot", obj));
    552. +
    553. 2 orGroupedAnd: function () {var tOpts = this.__opts,
    554. +
    555. clause = (tOpts.having ? "having" : "where"),
    556. +
    557. clauseObj = tOpts[clause];
    558. +
    559. 2 if (clauseObj) {
    560. +
    561. 1 return this.or.apply(this, arguments);
    562. +
    563. } else {
    564. +
    565. 1 var args = argsToArray(arguments);
    566. +
    567. 1 args = args.length == 1 ? args[0] : args;
    568. +
    569. 1 var opts = {};
    570. +
    571. 1 opts[clause] = this._filterExpr(args, null, "AND");
    572. +
    573. 1 return this.mergeOptions(opts);
    574. +
    575. }
    576. },
    577. /**
    578. -
    579. * Returnes a cloned dataset with the IS expression added to the WHERE
    580. -
    581. * clause.
    582. +
    583. * Adds a group of ORed conditions wrapped in parens to an existing having/where clause with an OR. If there isn't
    584. +
    585. * already a clause, a where clause is created with the ORed group.
    586. *
    587. -
    588. * @example
    589. +
    590. * <p>
    591. +
    592. * <b>For parameter types see {@link patio.Dataset#filter}.</b>
    593. +
    594. * </p>
    595. *
    596. -
    597. * DB.from("test").is({boolFlag : null});
    598. -
    599. * => SELECT * FROM test WHERE (boolFlag IS NULL);
    600. -
    601. * DB.from("test").is({boolFlag : false, otherFlag : true, name : null});
    602. -
    603. * => SELECT * FROM test WHERE ((boolFlag IS FALSE) AND (otherFlag IS TRUE) AND (name IS NULL));
    604. +
    605. * @example
    606. *
    607. -
    608. * @param {Object} obj object used to create the IS expression for.
    609. +
    610. * DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    611. +
    612. * //=> SELECT
    613. +
    614. * *
    615. +
    616. * FROM
    617. +
    618. * items
    619. +
    620. * WHERE
    621. +
    622. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
    623. *
    624. -
    625. * @return {patio.Dataset} a cloned dataset with the IS expression added to the WHERE clause.
    626. +
    627. * DB.from("items").orGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;
    628. +
    629. * //=> SELECT
    630. +
    631. * *
    632. +
    633. * FROM
    634. +
    635. * items
    636. +
    637. * WHERE
    638. +
    639. * ((price < 0) OR (price > 10))
    640. *
    641. +
    642. * @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
    643. */
    644. -
    645. is: function (obj) {
    646. -
    647. 4 return this.filter(this.__createBoolExpression("is", obj));
    648. +
    649. orGroupedOr: function (filterExp) {
    650. +
    651. 2 return this._addGroupedCondition("OR", "OR", filterExp);
    652. },
    653. /**
    654. -
    655. * Returnes a cloned dataset with the IS NOT NULL boolean expression added to the WHERE
    656. -
    657. * clause.
    658. +
    659. * Returns a copy of the dataset with the SQL DISTINCT clause.
    660. +
    661. * The DISTINCT clause is used to remove duplicate rows from the
    662. +
    663. * output. If arguments are provided, uses a DISTINCT ON clause,
    664. +
    665. * in which case it will only be distinct on those columns, instead
    666. +
    667. * of all returned columns.
    668. *
    669. * @example
    670. *
    671. -
    672. * DB.from("test").isNotNull("boolFlag");
    673. -
    674. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL);
    675. -
    676. * DB.from("test").isNotNull("boolFlag", "otherFlag");
    677. -
    678. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL AND otherFlag IS NOT NULL);
    679. -
    680. *
    681. -
    682. * @param {String...} arr variable number of arguments to create an IS NOT NULL expression for.
    683. -
    684. *
    685. -
    686. * @return {patio.Dataset} a cloned dataset with the IS NOT NULL expression added to the WHERE clause.
    687. +
    688. * DB.from("items").distinct().sqll
    689. +
    690. * //=> SELECT DISTINCT * FROM items
    691. +
    692. * DB.from("items").order("id").distinct("id").sql;
    693. +
    694. * //=> SELECT DISTINCT ON (id) * FROM items ORDER BY id
    695. *
    696. +
    697. * @throws {patio.QueryError} If arguments are given and DISTINCT ON is not supported.
    698. +
    699. * @param {...String|...patio.sql.Identifier} args variable number of arguments used to create
    700. +
    701. * the DISTINCT ON clause.
    702. +
    703. * @return {patio.Dataset} a cloned dataset with the DISTINCT/DISTINCT ON clause added.
    704. */
    705. -
    706. isNotNull: function (arr) {
    707. -
    708. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);
    709. -
    710. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    711. +
    712. distinct: function (args) {
    713. +
    714. 13 args = argsToArray(arguments);
    715. +
    716. 13 if (args.length && !this.supportsDistinctOn) {
    717. +
    718. 1 throw new QueryError("DISTICT ON is not supported");
    719. +
    720. }
    721. +
    722. 12 args = args.map(function (a) {
    723. +
    724. 6 return isString(a) ? new Identifier(a) : a;
    725. +
    726. });
    727. +
    728. 12 return this.mergeOptions({distinct: args});
    729. },
    730. /**
    731. -
    732. * Returnes a cloned dataset with the IS NULL boolean expression added to the WHERE
    733. -
    734. * clause.
    735. +
    736. * Adds an EXCEPT clause using a second dataset object.
    737. +
    738. * An EXCEPT compound dataset returns all rows in the current dataset
    739. +
    740. * that are not in the given dataset.
    741. *
    742. * @example
    743. *
    744. -
    745. * DB.from("test").isNull("boolFlag");
    746. -
    747. * => SELECT * FROM test WHERE (boolFlag IS NULL);
    748. -
    749. * DB.from("test").isNull("boolFlag", "otherFlag");
    750. -
    751. * => SELECT * FROM test WHERE (boolFlag IS NULL AND otherFlag IS NULL);
    752. +
    753. * DB.from("items").except(DB.from("other_items")).sql;
    754. +
    755. * //=> SELECT * FROM items EXCEPT SELECT * FROM other_items
    756. *
    757. -
    758. * @param {String...} arr variable number of arguments to create an IS NULL expression for.
    759. +
    760. * DB.from("items").except(DB.from("other_items"),
    761. +
    762. * {all : true, fromSelf : false}).sql;
    763. +
    764. * //=> SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
    765. *
    766. -
    767. * @return {patio.Dataset} a cloned dataset with the IS NULL expression added to the WHERE clause.
    768. +
    769. * DB.from("items").except(DB.from("other_items"),
    770. +
    771. * {alias : "i"}).sql;
    772. +
    773. * //=>SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS i
    774. +
    775. *
    776. +
    777. * @throws {patio.QueryError} if the operation is not supported.
    778. +
    779. * @param {patio.Dataset} dataset the dataset to use to create the EXCEPT clause.
    780. +
    781. * @param {Object} [opts] options to use when creating the EXCEPT clause
    782. +
    783. * @param {String|patio.sql.Identifier} [opt.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias.
    784. +
    785. * @param {Boolean} [opts.all] Set to true to use EXCEPT ALL instead of EXCEPT, so duplicate rows can occur
    786. +
    787. * @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}, use with care.
    788. *
    789. +
    790. * @return {patio.Dataset} a cloned dataset with the EXCEPT clause added.
    791. */
    792. -
    793. isNull: function (arr) {
    794. -
    795. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);
    796. -
    797. 2 return this.filter(this.__createBoolExpression("is", arr));
    798. +
    799. except: function (dataset, opts) {
    800. +
    801. 18 opts = isUndefined(opts) ? {} : opts;
    802. +
    803. 18 if (!isHash(opts)) {
    804. +
    805. 5 opts = {all: true};
    806. +
    807. }
    808. +
    809. 18 if (!this.supportsIntersectExcept) {
    810. +
    811. 2 throw new QueryError("EXCEPT not supoorted");
    812. +
    813. 16 } else if (opts.hasOwnProperty("all") && !this.supportsIntersectExceptAll) {
    814. +
    815. 1 throw new QueryError("EXCEPT ALL not supported");
    816. +
    817. }
    818. +
    819. 15 return this.compoundClone("except", dataset, opts);
    820. },
    821. /**
    822. -
    823. * Returnes a cloned dataset with the IS NOT TRUE boolean expression added to the WHERE
    824. -
    825. * clause.
    826. +
    827. * Performs the inverse of {@link patio.Dataset#filter}. Note that if you have multiple filter
    828. +
    829. * conditions, this is not the same as a negation of all conditions. For argument types see
    830. +
    831. * {@link patio.Dataset#filter}
    832. *
    833. * @example
    834. *
    835. -
    836. * DB.from("test").isNotTrue("boolFlag");
    837. -
    838. * => SELECT * FROM test WHERE (boolFlag IS NOT TRUE);
    839. -
    840. * DB.from("test").isNotTrue("boolFlag", "otherFlag");
    841. -
    842. * => SELECT * FROM test WHERE (boolFlag IS NOT TRUE AND otherFlag IS NOT TRUE);
    843. -
    844. *
    845. -
    846. * @param {String...} arr variable number of arguments to create an IS NOT TRUE expression for.
    847. -
    848. *
    849. -
    850. * @return {patio.Dataset} a cloned dataset with the IS NOT TRUE expression added to the WHERE clause.
    851. +
    852. * DB.from("items").exclude({category : "software").sql;
    853. +
    854. * //=> SELECT * FROM items WHERE (category != 'software')
    855. *
    856. +
    857. * DB.from("items").exclude({category : 'software', id : 3}).sql;
    858. +
    859. * //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
    860. +
    861. * @return {patio.Dataset} a cloned dataset with the excluded conditions applied to the HAVING/WHERE clause.
    862. */
    863. -
    864. isNotTrue: function (arr) {
    865. -
    866. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);
    867. -
    868. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    869. +
    870. exclude: function () {
    871. +
    872. 69 var cond = argsToArray(arguments), tOpts = this.__opts;
    873. +
    874. 69 var clause = (tOpts["having"] ? "having" : "where"), clauseObj = tOpts[clause];
    875. +
    876. 69 cond = cond.length > 1 ? cond : cond[0];
    877. +
    878. 69 cond = this._filterExpr.call(this, cond);
    879. +
    880. 69 cond = BooleanExpression.invert(cond);
    881. +
    882. 69 if (clauseObj) {
    883. +
    884. 58 cond = new BooleanExpression("AND", clauseObj, cond)
    885. +
    886. }
    887. +
    888. 69 var opts = {};
    889. +
    890. 69 opts[clause] = cond;
    891. +
    892. 69 return this.mergeOptions(opts);
    893. },
    894. /**
    895. -
    896. * Returnes a cloned dataset with the IS TRUE boolean expression added to the WHERE
    897. -
    898. * clause.
    899. +
    900. * Returns a copy of the dataset with the given conditions applied to it.
    901. +
    902. * If the query already has a HAVING clause, then the conditions are applied to the
    903. +
    904. * HAVING clause otherwise they are applied to the WHERE clause.
    905. *
    906. * @example
    907. *
    908. -
    909. * DB.from("test").isTrue("boolFlag");
    910. -
    911. * => SELECT * FROM test WHERE (boolFlag IS TRUE);
    912. -
    913. * DB.from("test").isTrue("boolFlag", "otherFlag");
    914. -
    915. * => SELECT * FROM test WHERE (boolFlag IS TRUE AND otherFlag IS TRUE);
    916. +
    917. * DB.from("items").filter({id : 3}).sql;
    918. +
    919. * //=> SELECT * FROM items WHERE (id = 3)
    920. *
    921. -
    922. * @param {String...} arr variable number of arguments to create an IS TRUE expression for.
    923. +
    924. * DB.from("items").filter('price < ?', 100)
    925. +
    926. * //=> SELECT * FROM items WHERE price < 100
    927. *
    928. -
    929. * @return {patio.Dataset} a cloned dataset with the IS TRUE expression added to the WHERE clause.
    930. +
    931. * DB.from("items").filter({id, [1,2,3]}).filter({id : {between : [0, 10]}}).sql;
    932. +
    933. * //=> SELECT
    934. +
    935. * *
    936. +
    937. * FROM
    938. +
    939. * items
    940. +
    941. * WHERE
    942. +
    943. * ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
    944. *
    945. -
    946. */
    947. -
    948. isTrue: function (arr) {
    949. -
    950. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);
    951. -
    952. 2 return this.filter(this.__createBoolExpression("is", arr));
    953. -
    954. },
    955. -
    956. -
    957. /**
    958. -
    959. * Returnes a cloned dataset with the IS NOT FALSE boolean expression added to the WHERE
    960. -
    961. * clause.
    962. +
    963. * DB.from("items").filter('price < 100');
    964. +
    965. * //=> SELECT * FROM items WHERE price < 100
    966. *
    967. -
    968. * @example
    969. +
    970. * DB.from("items").filter("active").sql;
    971. +
    972. * //=> SELECT * FROM items WHERE active
    973. *
    974. -
    975. * DB.from("test").isNotFalse("boolFlag");
    976. -
    977. * => SELECT * FROM test WHERE (boolFlag IS NOT FALSE);
    978. -
    979. * DB.from("test").isNotFalse("boolFlag", "otherFlag");
    980. -
    981. * => SELECT * FROM test WHERE (boolFlag IS NOT FALSE AND otherFlag IS NOT FALSE);
    982. -
    983. * @param {String...} arr variable number of arguments to create an IS NOT FALSE expression for.
    984. +
    985. * DB.from("items").filter(function(){
    986. +
    987. * return this.price.lt(100);
    988. +
    989. * });
    990. +
    991. * //=> SELECT * FROM items WHERE (price < 100)
    992. *
    993. -
    994. * @return {patio.Dataset} a cloned dataset with the IS NOT FALSE expression added to the WHERE clause.
    995. +
    996. * //Multiple filter calls can be chained for scoping:
    997. +
    998. * DB.from("items").filter(:category => 'software').filter{price < 100}
    999. +
    1000. * //=> SELECT * FROM items WHERE ((category = 'software') AND (price < 100))
    1001. *
    1002. -
    1003. */
    1004. -
    1005. isNotFalse: function (arr) {
    1006. -
    1007. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);
    1008. -
    1009. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    1010. -
    1011. },
    1012. -
    1013. -
    1014. /**
    1015. -
    1016. * Returnes a cloned dataset with the IS FALSE boolean expression added to the WHERE
    1017. -
    1018. * clause.
    1019. *
    1020. -
    1021. * @example
    1022. +
    1023. * @param {Object|Array|String|patio.sql.Identifier|patio.sql.BooleanExpression} args filters to apply to the
    1024. +
    1025. * WHERE/HAVING clause. Description of each:
    1026. +
    1027. * <ul>
    1028. +
    1029. * <li>Hash - list of equality/inclusion expressions</li>
    1030. +
    1031. * <li>Array - depends:
    1032. +
    1033. * <ul>
    1034. +
    1035. * <li>If first member is a string, assumes the rest of the arguments
    1036. +
    1037. * are parameters and interpolates them into the string.</li>
    1038. +
    1039. * <li>If all members are arrays of length two, treats the same way
    1040. +
    1041. * as a hash, except it allows for duplicate keys to be
    1042. +
    1043. * specified.</li>
    1044. +
    1045. * <li>Otherwise, treats each argument as a separate condition.</li>
    1046. +
    1047. * </ul>
    1048. +
    1049. * </li>
    1050. +
    1051. * <li>String - taken literally</li>
    1052. +
    1053. * <li>{@link patio.sql.Identifier} - taken as a boolean column argument (e.g. WHERE active)</li>
    1054. +
    1055. * <li>{@link patio.sql.BooleanExpression} - an existing condition expression,
    1056. +
    1057. * probably created using the patio.sql methods.
    1058. +
    1059. * </li>
    1060. *
    1061. -
    1062. * DB.from("test").isFalse("boolFlag");
    1063. -
    1064. * => SELECT * FROM test WHERE (boolFlag IS FALSE);
    1065. -
    1066. * DB.from("test").isFalse("boolFlag", "otherFlag");
    1067. -
    1068. * => SELECT * FROM test WHERE (boolFlag IS FALSE AND otherFlag IS FALSE);
    1069. -
    1070. * @param {String...} arr variable number of arguments to create an IS FALSE expression for.
    1071. +
    1072. * @param {Function} [cb] filter also takes a cb, which should return one of the above argument
    1073. +
    1074. * types, and is treated the same way. This block is called with an {@link patio.sql} object which can be used to dynaically create expression. For more details
    1075. +
    1076. * on the sql object see {@link patio.sql}
    1077. *
    1078. -
    1079. * @return {patio.Dataset} a cloned dataset with the IS FALSE expression added to the WHERE clause.
    1080. +
    1081. * <p>
    1082. +
    1083. * <b>NOTE:</b>If both a cb and regular arguments are provided, they get ANDed together.
    1084. +
    1085. * </p>
    1086. *
    1087. -
    1088. */
    1089. -
    1090. isFalse: function (arr) {
    1091. -
    1092. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);
    1093. -
    1094. 2 return this.filter(this.__createBoolExpression("is", arr));
    1095. +
    1096. * @return {patio.Dataset} a cloned dataset with the filter arumgents applied to the WHERE/HAVING clause.
    1097. +
    1098. **/
    1099. +
    1100. filter: function (args, cb) {
    1101. +
    1102. 3468 args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));
    1103. +
    1104. 3468 return this._filter.apply(this, args);
    1105. },
    1106. /**
    1107. -
    1108. *
    1109. -
    1110. * Returns a cloned dataset with a greater than or equal to expression added to the WHERE
    1111. -
    1112. * clause.
    1113. -
    1114. *
    1115. -
    1116. * @example
    1117. -
    1118. * DB.from("test").gte({x : 1});
    1119. -
    1120. * //=> SELECT * FROM test WHERE (x >= 1)
    1121. -
    1122. * DB.from("test").gte({x : 1, y : 10});
    1123. -
    1124. * //=> SELECT * FROM test WHERE ((x >= 1) AND (y >= 10))
    1125. -
    1126. *
    1127. -
    1128. * @param {Object} obj object used to create the greater than or equal to expression.
    1129. -
    1130. *
    1131. -
    1132. * @return {patio.Dataset} a cloned dataset with the greater than or equal to expression added to the WHERE clause.
    1133. +
    1134. * @see patio.Dataset#filter
    1135. */
    1136. -
    1137. gte: function (arr) {
    1138. -
    1139. 2 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "gte");
    1140. -
    1141. 2 return this.filter(this.__createBoolExpression("gte", arr));
    1142. +
    1143. find: function () {
    1144. +
    1145. 30 var args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));
    1146. +
    1147. 30 return this._filter.apply(this, args);
    1148. },
    1149. /**
    1150. -
    1151. *
    1152. -
    1153. * Returns a cloned dataset with a less than or equal to expression added to the WHERE
    1154. -
    1155. * clause.
    1156. -
    1157. *
    1158. * @example
    1159. -
    1160. * DB.from("test").gte({x : 1});
    1161. -
    1162. * //=> SELECT * FROM test WHERE (x <= 1)
    1163. -
    1164. * DB.from("test").gte({x : 1, y : 10});
    1165. -
    1166. * //=> SELECT * FROM test WHERE ((x <= 1) AND (y <= 10))
    1167. -
    1168. *
    1169. -
    1170. * @param {Object} obj object used to create the less than or equal to expression.
    1171. -
    1172. *
    1173. -
    1174. * @return {patio.Dataset} a cloned dataset with the less than or equal to expression added to the WHERE clause.
    1175. +
    1176. * DB.from("table").forUpdate()
    1177. +
    1178. * //=> SELECT * FROM table FOR UPDATE
    1179. +
    1180. * @return {patio.Dataset} a cloned dataset with a "update" lock style.
    1181. */
    1182. -
    1183. lte: function (obj) {
    1184. -
    1185. 2 var arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "lte");
    1186. -
    1187. 2 return this.filter(this.__createBoolExpression("lte", obj));
    1188. +
    1189. forUpdate: function () {
    1190. +
    1191. 1 return this.lockStyle("update");
    1192. },
    1193. /**
    1194. -
    1195. * Returns a cloned dataset with a between clause added
    1196. -
    1197. * to the where clause.
    1198. +
    1199. * Returns a copy of the dataset with the source changed. If no
    1200. +
    1201. * source is given, removes all tables. If multiple sources
    1202. +
    1203. * are given, it is the same as using a CROSS JOIN (cartesian product) between all tables.
    1204. *
    1205. * @example
    1206. -
    1207. * ds.notBetween({x:[1, 2]}).sql;
    1208. -
    1209. * //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))
    1210. +
    1211. * var dataset = DB.from("items");
    1212. *
    1213. -
    1214. * ds.find({x:{notBetween:[1, 2]}}).sql;
    1215. -
    1216. * //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))
    1217. -
    1218. * @param {Object} obj object where the key is the column and the value is an array where the first element
    1219. -
    1220. * is the item to be greater than or equal to than and the second item is less than or equal to than.
    1221. +
    1222. * dataset.from().sql;
    1223. +
    1224. * //=> SELECT *
    1225. *
    1226. -
    1227. * @return {patio.Dataset} a cloned dataset with a between clause added
    1228. -
    1229. * to the where clause.
    1230. -
    1231. */
    1232. -
    1233. between: function (obj) {
    1234. -
    1235. 2 return this.filter(this.__createBetweenExpression(obj));
    1236. -
    1237. },
    1238. -
    1239. -
    1240. /**
    1241. -
    1242. * Returns a cloned dataset with a not between clause added
    1243. -
    1244. * to the where clause.
    1245. +
    1246. * dataset.from("blah").sql
    1247. +
    1248. * //=> SELECT * FROM blah
    1249. *
    1250. -
    1251. * @example
    1252. -
    1253. * ds.notBetween({x:[1, 2]}).sql;
    1254. -
    1255. * //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))
    1256. +
    1257. * dataset.from("blah", "foo")
    1258. +
    1259. * //=> SELECT * FROM blah, foo
    1260. *
    1261. -
    1262. * ds.find({x:{notBetween:[1, 2]}}).sql;
    1263. -
    1264. * //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))
    1265. -
    1266. * @param {Object} obj object where the key is the column and the value is an array where the first element
    1267. -
    1268. * is the item to be less than and the second item is greater than.
    1269. +
    1270. * dataset.from({a:"b"}).sql;
    1271. +
    1272. * //=> SELECT * FROM a AS b
    1273. *
    1274. -
    1275. * @return {patio.Dataset} a cloned dataset with a not between clause added
    1276. -
    1277. * to the where clause.
    1278. +
    1279. * dataset.from(dataset.from("a").group("b").as("c")).sql;
    1280. +
    1281. * //=> "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"
    1282. +
    1283. *
    1284. +
    1285. * @param {...String|...patio.sql.Identifier|...patio.Dataset|...Object} [source] tables to select from
    1286. +
    1287. *
    1288. +
    1289. * @return {patio.Dataset} a cloned dataset with the FROM clause overridden.
    1290. */
    1291. -
    1292. notBetween: function (obj) {
    1293. -
    1294. 2 return this.filter(this.__createBetweenExpression(obj, true));
    1295. +
    1296. from: function (source) {
    1297. +
    1298. 821 source = argsToArray(arguments);
    1299. +
    1300. 821 var tableAliasNum = 0, sources = [];
    1301. +
    1302. 821 source.forEach(function (s) {
    1303. +
    1304. 1003 if (isInstanceOf(s, Dataset)) {
    1305. +
    1306. 86 sources.push(new AliasedExpression(s, this._datasetAlias(++tableAliasNum)));
    1307. +
    1308. 917 } else if (isHash(s)) {
    1309. +
    1310. 3 for (var i in s) {
    1311. +
    1312. 3 sources.push(new AliasedExpression(new Identifier(i), s[i]));
    1313. +
    1314. }
    1315. +
    1316. 914 } else if (isString(s)) {
    1317. +
    1318. 889 sources.push(this.stringToIdentifier(s))
    1319. +
    1320. } else {
    1321. +
    1322. 25 sources.push(s);
    1323. +
    1324. }
    1325. +
    1326. }, this);
    1327. +
    1328. +
    1329. 821 var o = {from: sources.length ? sources : null}
    1330. +
    1331. 821 if (tableAliasNum) {
    1332. +
    1333. 84 o.numDatasetSources = tableAliasNum;
    1334. +
    1335. }
    1336. +
    1337. 821 return this.mergeOptions(o)
    1338. },
    1339. /**
    1340. -
    1341. * Returns a cloned dataset with the given lock style. If style is a
    1342. -
    1343. * string, it will be used directly.Currently "update" is respected
    1344. -
    1345. * by most databases, and "share" is supported by some.
    1346. +
    1347. * Returns a dataset selecting from the current dataset.
    1348. +
    1349. * Supplying the alias option controls the alias of the result.
    1350. *
    1351. * @example
    1352. -
    1353. * DB.from("items").lockStyle('FOR SHARE') # SELECT * FROM items FOR SHARE
    1354. *
    1355. -
    1356. * @param {String} style the lock style to use.
    1357. +
    1358. * ds = DB.from("items").order("name").select("id", "name")
    1359. +
    1360. * //=> SELECT id,name FROM items ORDER BY name
    1361. *
    1362. -
    1363. * @return {patio.Dataset} a cloned datase with the given lock style.
    1364. -
    1365. **/
    1366. -
    1367. lockStyle: function (style) {
    1368. -
    1369. 4 return this.mergeOptions({lock: style});
    1370. -
    1371. },
    1372. -
    1373. -
    1374. /**
    1375. -
    1376. * Returns a copy of the dataset with the order changed. If the dataset has an
    1377. -
    1378. * existing order, it is ignored and overwritten with this order. If null is given
    1379. -
    1380. * the returned dataset has no order. This can accept multiple arguments
    1381. -
    1382. * of varying kinds, such as SQL functions. This also takes a function similar
    1383. -
    1384. * to {@link patio.Dataset#filter}
    1385. +
    1386. * ds.fromSelf().sql;
    1387. +
    1388. * //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1
    1389. *
    1390. -
    1391. * @example
    1392. +
    1393. * ds.fromSelf({alias : "foo"}).sql;
    1394. +
    1395. * //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo
    1396. *
    1397. -
    1398. * DB.from("items").order("name")
    1399. -
    1400. * //=> SELECT * FROM items ORDER BY name
    1401. +
    1402. * @param {Object} [opts] options
    1403. +
    1404. * @param {String|patio.sql.Identifier} [opts.alias] alias to use
    1405. *
    1406. -
    1407. * DB.from("items").order("a", "b")
    1408. -
    1409. * //=> SELECT * FROM items ORDER BY a, b
    1410. +
    1411. * @return {patio.Dataset} a cloned dataset with the FROM clause set as the current dataset.
    1412. +
    1413. */
    1414. +
    1415. fromSelf: function (opts) {
    1416. +
    1417. 84 opts = isUndefined(opts) ? {} : opts;
    1418. +
    1419. 84 var fs = {};
    1420. +
    1421. 84 var nonSqlOptions = this._static.NON_SQL_OPTIONS;
    1422. +
    1423. 84 Object.keys(this.__opts).forEach(function (k) {
    1424. +
    1425. 302 if (nonSqlOptions.indexOf(k) == -1) {
    1426. +
    1427. 302 fs[k] = null;
    1428. +
    1429. }
    1430. +
    1431. });
    1432. +
    1433. 84 return this.mergeOptions(fs).from(opts["alias"] ? this.as(opts["alias"]) : this);
    1434. +
    1435. },
    1436. +
    1437. +
    1438. /**
    1439. +
    1440. * Match any of the columns to any of the patterns. The terms can be
    1441. +
    1442. * strings (which use LIKE) or regular expressions (which are only
    1443. +
    1444. * supported on MySQL and PostgreSQL). Note that the total number of
    1445. +
    1446. * pattern matches will be columns[].length * terms[].length,
    1447. +
    1448. * which could cause performance issues.
    1449. *
    1450. -
    1451. * DB.from("items").order(sql.literal('a + b'))
    1452. -
    1453. * //=> SELECT * FROM items ORDER BY a + b
    1454. +
    1455. * @example
    1456. *
    1457. -
    1458. * DB.from("items").order(sql.identifier("a").plus("b"))
    1459. -
    1460. * //=> SELECT * FROM items ORDER BY (a + b)
    1461. +
    1462. * DB.from("items").grep("a", "%test%").sql;
    1463. +
    1464. * //=> SELECT * FROM items WHERE (a LIKE '%test%');
    1465. *
    1466. -
    1467. * DB.from("items").order(sql.identifier("name").desc())
    1468. -
    1469. * //=> SELECT * FROM items ORDER BY name DESC
    1470. +
    1471. * DB.from("items").grep(["a", "b"], ["%test%" "foo"]).sql;
    1472. +
    1473. * //=> SELECT * FROM items WHERE ((a LIKE '%test%') OR (a LIKE 'foo') OR (b LIKE '%test%') OR (b LIKE 'foo'))
    1474. *
    1475. -
    1476. * DB.from("items").order(sql.identifier("name").asc({nulls : "last"))
    1477. -
    1478. * //=> SELECT * FROM items ORDER BY name ASC NULLS LAST
    1479. +
    1480. * DB.from("items").grep(['a', 'b'], ["%foo%", "%bar%"], {allPatterns : true}).sql;
    1481. +
    1482. * //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (b LIKE '%foo%')) AND ((a LIKE '%bar%') OR (b LIKE '%bar%')))
    1483. *
    1484. -
    1485. * DB.from("items").order(function(){
    1486. -
    1487. * return this.sum("name").desc();
    1488. -
    1489. * }); //=> SELECT * FROM items ORDER BY sum(name) DESC
    1490. +
    1491. * DB.from("items").grep(["a", "b"], ['%foo%", "%bar%", {allColumns : true})sql;
    1492. +
    1493. * //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (a LIKE '%bar%')) AND ((b LIKE '%foo%') OR (b LIKE '%bar%')))
    1494. *
    1495. -
    1496. * DB.from("items").order(null)
    1497. -
    1498. * //=>SELECT * FROM items
    1499. -
    1500. * @param arg variable number of arguments similar to {@link patio.Dataset#filter}
    1501. +
    1502. * DB.from("items").grep(["a", "b"], ["%foo%", "%bar%"], {allPatterns : true, allColumns : true}).sql;
    1503. +
    1504. * //=> SELECT * FROM a WHERE ((a LIKE '%foo%') AND (b LIKE '%foo%') AND (a LIKE '%bar%') AND (b LIKE '%bar%'))
    1505. *
    1506. -
    1507. * @return {patio.Dataset} a cloned dataset with the order changed.
    1508. -
    1509. * */
    1510. -
    1511. order: function (args) {
    1512. -
    1513. 381 args = argsToArray(arguments);
    1514. -
    1515. 381 var order = [];
    1516. -
    1517. 381 args = compact(args).length ? args : null;
    1518. -
    1519. 381 if (args) {
    1520. -
    1521. 263 args.forEach(function (a) {
    1522. -
    1523. 326 if (isString(a)) {
    1524. -
    1525. 204 order.push(this.stringToIdentifier(a));
    1526. -
    1527. 122 } else if (isFunction(a)) {
    1528. -
    1529. 16 var res = a.apply(sql, [sql]);
    1530. -
    1531. 16 order = order.concat(isArray(res) ? res : [res]);
    1532. -
    1533. } else {
    1534. -
    1535. 106 order.push(a);
    1536. -
    1537. }
    1538. -
    1539. }, this);
    1540. +
    1541. * @param {String[]|patio.sql.Identifier[]} columns columns to search
    1542. +
    1543. * @param {String|RegExp} patterns patters to search with
    1544. +
    1545. * @param {Object} [opts] options to use when searching. NOTE If both allColumns and allPatterns are true, all columns must match all patterns
    1546. +
    1547. * @param {Boolean} [opts.allColumns] All columns must be matched to any of the given patterns.
    1548. +
    1549. * @param {Boolean} [opts.allPatterns] All patterns must match at least one of the columns.
    1550. +
    1551. * @param {Boolean} [opts.caseInsensitive] Use a case insensitive pattern match (the default is
    1552. +
    1553. * case sensitive if the database supports it).
    1554. +
    1555. * @return {patio.Dataset} a dataset with the LIKE clauses added
    1556. +
    1557. */
    1558. +
    1559. +
    1560. grep: function (columns, patterns, opts) {
    1561. +
    1562. 15 opts = isUndefined(opts) ? {} : opts;
    1563. +
    1564. 15 var conds;
    1565. +
    1566. 15 if (opts.hasOwnProperty("allPatterns")) {
    1567. +
    1568. 4 conds = array.toArray(patterns).map(function (pat) {
    1569. +
    1570. 8 return BooleanExpression.fromArgs(
    1571. +
    1572. [(opts.allColumns ? "AND" : "OR")]
    1573. +
    1574. .concat(array.toArray(columns)
    1575. +
    1576. .map(function (c) {
    1577. +
    1578. 16 return StringExpression.like(c, pat, opts);
    1579. +
    1580. })));
    1581. +
    1582. });
    1583. +
    1584. 4 return this.filter(BooleanExpression.fromArgs([opts.allPatterns ? "AND" : "OR"].concat(conds)));
    1585. } else {
    1586. -
    1587. 118 order = null;
    1588. +
    1589. 11 conds = array.toArray(columns)
    1590. +
    1591. .map(function (c) {
    1592. +
    1593. 16 return BooleanExpression.fromArgs(["OR"].concat(array.toArray(patterns).map(function (pat) {
    1594. +
    1595. 26 return StringExpression.like(c, pat, opts);
    1596. +
    1597. })));
    1598. +
    1599. });
    1600. +
    1601. 11 return this.filter(BooleanExpression.fromArgs([opts.allColumns ? "AND" : "OR"].concat(conds)));
    1602. }
    1603. -
    1604. 381 return this.mergeOptions({order: order});
    1605. },
    1606. /**
    1607. -
    1608. * Alias of {@link patio.Dataset#orderMore};
    1609. +
    1610. * @see patio.Dataset#grep
    1611. */
    1612. -
    1613. orderAppend: function () {
    1614. -
    1615. 4 return this.orderMore.apply(this, arguments);
    1616. +
    1617. like: function () {
    1618. +
    1619. 1 return this.grep.apply(this, arguments);
    1620. },
    1621. -
    1622. /**
    1623. -
    1624. * @see patio.Dataset#order
    1625. -
    1626. */
    1627. -
    1628. orderBy: function () {
    1629. -
    1630. 6 return this.order.apply(this, arguments);
    1631. -
    1632. },
    1633. /**
    1634. -
    1635. * Returns a copy of the dataset with the order columns added
    1636. -
    1637. * to the end of the existing order. For more detail
    1638. -
    1639. * @see patio.Dataset#order
    1640. -
    1641. *
    1642. +
    1643. * Returns a copy of the dataset with the results grouped by the value of
    1644. +
    1645. * the given columns.
    1646. * @example
    1647. +
    1648. * DB.from("items").group("id")
    1649. +
    1650. * //=>SELECT * FROM items GROUP BY id
    1651. +
    1652. * DB.from("items").group("id", "name")
    1653. +
    1654. * //=> SELECT * FROM items GROUP BY id, name
    1655. +
    1656. * @param {String...|patio.sql.Identifier...} columns columns to group by.
    1657. *
    1658. -
    1659. * DB.from("items").order("a").order("b");
    1660. -
    1661. * //=> SELECT * FROM items ORDER BY b
    1662. -
    1663. *
    1664. -
    1665. * DB.from("items").order("a").orderMore("b");
    1666. -
    1667. * //=>SELECT * FROM items ORDER BY a, b
    1668. -
    1669. */
    1670. -
    1671. orderMore: function () {
    1672. -
    1673. 11 var args = argsToArray(arguments);
    1674. -
    1675. 11 if (this.__opts.order) {
    1676. -
    1677. 9 args = this.__opts.order.concat(args);
    1678. -
    1679. }
    1680. -
    1681. 11 return this.order.apply(this, args);
    1682. +
    1683. * @return {patio.Dataset} a cloned dataset with the GROUP BY clause added.
    1684. +
    1685. **/
    1686. +
    1687. group: function (columns) {
    1688. +
    1689. 32 columns = argsToArray(arguments);
    1690. +
    1691. 32 var stringToIdentifier = this.stringToIdentifier.bind(this)
    1692. +
    1693. 32 return this.mergeOptions({group: (array.compact(columns).length == 0 ? null : columns.map(function (c) {
    1694. +
    1695. 32 return isString(c) ? stringToIdentifier(c) : c;
    1696. +
    1697. }))});
    1698. },
    1699. /**
    1700. -
    1701. * Returns a copy of the dataset with the order columns added
    1702. -
    1703. * to the beginning of the existing order. For more detail
    1704. -
    1705. * @see patio.Dataset#order
    1706. -
    1707. *
    1708. -
    1709. * @example
    1710. -
    1711. * DB.from("items").order("a").order("b");
    1712. -
    1713. * //=> SELECT * FROM items ORDER BY b
    1714. -
    1715. *
    1716. -
    1717. * DB.from("items").order("a").orderPrepend("b");
    1718. -
    1719. * //=>SELECT * FROM items ORDER BY b, a
    1720. -
    1721. *
    1722. -
    1723. *
    1724. -
    1725. **/
    1726. -
    1727. orderPrepend: function () {
    1728. -
    1729. 4 var ds = this.order.apply(this, arguments);
    1730. -
    1731. 4 return this.__opts.order ? ds.orderMore.apply(ds, this.__opts.order) : ds;
    1732. +
    1733. * @see patio.Dataset#group
    1734. +
    1735. */
    1736. +
    1737. groupBy: function () {
    1738. +
    1739. 10 return this.group.apply(this, arguments);
    1740. },
    1741. +
    1742. /**
    1743. -
    1744. * Qualify to the given table, or {@link patio.Dataset#firstSourceAlias} if not table is given.
    1745. +
    1746. * Returns a dataset grouped by the given column with count by group.
    1747. +
    1748. * Column aliases may be supplied, and will be included in the select clause.
    1749. *
    1750. * @example
    1751. -
    1752. * DB.from("items").filter({id : 1}).qualify();
    1753. -
    1754. * //=> SELECT items.* FROM items WHERE (items.id = 1)
    1755. *
    1756. -
    1757. * DB.from("items").filter({id : 1}).qualify("i");
    1758. -
    1759. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    1760. -
    1761. *
    1762. -
    1763. * @param {String|patio.sql.Identifier} [table={@link patio.Dataset#firstSourceAlias}] the table name to qualify to.
    1764. -
    1765. *
    1766. -
    1767. * @return {patio.Dataset} a cloned dataset qualified to the table or {@link patio.Dataset#firstSourceAlias}
    1768. -
    1769. **/
    1770. -
    1771. qualify: function (table) {
    1772. -
    1773. 19 table = table || this.firstSourceAlias;
    1774. -
    1775. 19 return this.qualifyTo(table);
    1776. -
    1777. },
    1778. -
    1779. -
    1780. /**
    1781. -
    1782. * Qualify the dataset to its current first source(first from clause). This is useful
    1783. -
    1784. * if you have unqualified identifiers in the query that all refer to
    1785. -
    1786. * the first source, and you want to join to another table which
    1787. -
    1788. * has columns with the same name as columns in the current dataset.
    1789. -
    1790. * See {@link patio.Dataset#qualifyTo}
    1791. +
    1792. * DB.from("items").groupAndCount("name").all()
    1793. +
    1794. * //=> SELECT name, count(*) AS count FROM items GROUP BY name
    1795. +
    1796. * //=> [{name : 'a', count : 1}, ...]
    1797. *
    1798. -
    1799. * @example
    1800. +
    1801. * DB.from("items").groupAndCount("first_name", "last_name").all()
    1802. +
    1803. * //SELECT first_name, last_name, count(*) AS count FROM items GROUP BY first_name, last_name
    1804. +
    1805. * //=> [{first_name : 'a', last_name : 'b', count : 1}, ...]
    1806. *
    1807. -
    1808. * DB.from("items").filter({id : 1}).qualifyToFirstSource();
    1809. -
    1810. * //=> SELECT items.* FROM items WHERE (items.id = 1)
    1811. +
    1812. * DB.from("items").groupAndCount("first_name___name").all()
    1813. +
    1814. * //=> SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
    1815. +
    1816. * //=> [{name : 'a', count:1}, ...]
    1817. +
    1818. * @param {String...|patio.sql.Identifier...} columns columns to croup and count on.
    1819. *
    1820. -
    1821. * @return {patio.Dataset} a cloned dataset that is qualified with the first source.
    1822. -
    1823. * */
    1824. -
    1825. qualifyToFirstSource: function () {
    1826. -
    1827. 18 return this.qualifyTo(this.firstSourceAlias);
    1828. +
    1829. * @return {patio.Dataset} a cloned dataset with the GROUP clause and count added.
    1830. +
    1831. */
    1832. +
    1833. groupAndCount: function (columns) {
    1834. +
    1835. 8 columns = argsToArray(arguments);
    1836. +
    1837. 8 var group = this.group.apply(this, columns.map(function (c) {
    1838. +
    1839. 9 return this._unaliasedIdentifier(c);
    1840. +
    1841. }, this));
    1842. +
    1843. 8 return group.select.apply(group, columns.concat([this._static.COUNT_OF_ALL_AS_COUNT]));
    1844. +
    1845. },
    1846. /**
    1847. -
    1848. * Return a copy of the dataset with unqualified identifiers in the
    1849. -
    1850. * SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the
    1851. -
    1852. * given table. If no columns are currently selected, select all
    1853. -
    1854. * columns of the given table.
    1855. +
    1856. * Returns a copy of the dataset with the HAVING conditions changed. See {@link patio.Dataset#filter} for argument types.
    1857. *
    1858. * @example
    1859. -
    1860. * DB.from("items").filter({id : 1}).qualifyTo("i");
    1861. -
    1862. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    1863. -
    1864. *
    1865. -
    1866. * @param {String} table the name to qualify identifier to.
    1867. +
    1868. * DB.from("items").group("sum").having({sum : 10}).sql;
    1869. +
    1870. * //=> SELECT * FROM items GROUP BY sum HAVING (sum = 10)
    1871. *
    1872. -
    1873. * @return {patio.Dataset} a cloned dataset with unqualified identifiers qualified.
    1874. -
    1875. */
    1876. -
    1877. qualifyTo: function (table) {
    1878. -
    1879. 40 var o = this.__opts;
    1880. -
    1881. 40 if (o.sql) {
    1882. -
    1883. 2 return this.mergeOptions();
    1884. -
    1885. }
    1886. -
    1887. 38 var h = {};
    1888. -
    1889. 38 array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) {
    1890. -
    1891. 65 h[k] = this._qualifiedExpression(o[k], table);
    1892. +
    1893. * @return {patio.Dataset} a cloned dataset with HAVING clause changed or added.
    1894. +
    1895. **/
    1896. +
    1897. having: function () {
    1898. +
    1899. 12 var cond = argsToArray(arguments).map(function (s) {
    1900. +
    1901. 12 return isString(s) && s !== '' ? this.stringToIdentifier(s) : s
    1902. }, this);
    1903. -
    1904. 38 if (!o.select || isEmpty(o.select)) {
    1905. -
    1906. 14 h.select = [new ColumnAll(table)];
    1907. -
    1908. }
    1909. -
    1910. 38 return this.mergeOptions(h);
    1911. +
    1912. 12 return this._filter.apply(this, ["having"].concat(cond));
    1913. },
    1914. /**
    1915. -
    1916. * Same as {@link patio.Dataset@qualifyTo} except that it forces qualification on methods called
    1917. -
    1918. * after it has been called.
    1919. +
    1920. * Adds an INTERSECT clause using a second dataset object.
    1921. +
    1922. * An INTERSECT compound dataset returns all rows in both the current dataset
    1923. +
    1924. * and the given dataset.
    1925. *
    1926. * @example
    1927. *
    1928. -
    1929. * //qualfyTo would generate
    1930. -
    1931. * DB.from("items").qualifyTo("i").filter({id : 1});
    1932. -
    1933. * //=> SELECT i.* FROM items WHERE (id = 1)
    1934. +
    1935. * DB.from("items").intersect(DB.from("other_items")).sql;
    1936. +
    1937. * //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS t1
    1938. *
    1939. -
    1940. * //alwaysQualify qualifies filter also.
    1941. -
    1942. * DB.from("items").alwaysQualify("i").filter({id : 1});
    1943. -
    1944. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    1945. +
    1946. * DB.from("items").intersect(DB.from("other_items"), {all : true, fromSelf : false}).sql;
    1947. +
    1948. * //=> SELECT * FROM items INTERSECT ALL SELECT * FROM other_items
    1949. *
    1950. +
    1951. * DB.from("items").intersect(DB.from("other_items"), {alias : "i"}).sql;
    1952. +
    1953. * //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS i
    1954. *
    1955. -
    1956. * @param {String|patio.sql.Identifier} [table=this.firstSourceAlias] the table to qualify to.
    1957. -
    1958. * @return {patio.Dataset} a cloned dataset that will always qualify.
    1959. -
    1960. */
    1961. -
    1962. alwaysQualify: function (table) {
    1963. -
    1964. 3 return this.mergeOptions({alwaysQualify: table || this.firstSourceAlias});
    1965. +
    1966. * @throws {patio.QueryError} if the operation is not supported.
    1967. +
    1968. * @param {patio.Dataset} dataset the dataset to intersect
    1969. +
    1970. * @param {Object} [opts] options
    1971. +
    1972. * @param {String|patio.sql.Identifier} [opts.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias
    1973. +
    1974. * @param {Boolean} [opts.all] Set to true to use INTERSECT ALL instead of INTERSECT, so duplicate rows can occur
    1975. +
    1976. * @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}.
    1977. +
    1978. *
    1979. +
    1980. * @return {patio.Dataset} a cloned dataset with the INTERSECT clause.
    1981. +
    1982. **/
    1983. +
    1984. intersect: function (dataset, opts) {
    1985. +
    1986. 18 opts = isUndefined(opts) ? {} : opts;
    1987. +
    1988. 18 if (!isHash(opts)) {
    1989. +
    1990. 5 opts = {all: opts};
    1991. +
    1992. }
    1993. +
    1994. 18 if (!this.supportsIntersectExcept) {
    1995. +
    1996. 2 throw new QueryError("INTERSECT not supported");
    1997. +
    1998. 16 } else if (opts.all && !this.supportsIntersectExceptAll) {
    1999. +
    2000. 1 throw new QueryError("INTERSECT ALL not supported");
    2001. +
    2002. }
    2003. +
    2004. 15 return this.compoundClone("intersect", dataset, opts);
    2005. },
    2006. -
    2007. /**
    2008. -
    2009. * Returns a copy of the dataset with the order reversed. If no order is
    2010. -
    2011. * given, the existing order is inverted.
    2012. +
    2013. * Inverts the current filter.
    2014. *
    2015. * @example
    2016. -
    2017. * DB.from("items").reverse("id");
    2018. -
    2019. * //=> SELECT * FROM items ORDER BY id DESC
    2020. -
    2021. *
    2022. -
    2023. * DB.from("items").order("id").reverse();
    2024. -
    2025. * //=> SELECT * FROM items ORDER BY id DESC
    2026. -
    2027. *
    2028. -
    2029. * DB.from("items").order("id").reverse(sql.identifier("name").asc);
    2030. -
    2031. * //=> SELECT * FROM items ORDER BY name ASC
    2032. -
    2033. *
    2034. -
    2035. * @param {String|patio.sql.Identifier|Function} args variable number of columns add to order before reversing.
    2036. +
    2037. * DB.from("items").filter({category : 'software'}).invert()
    2038. +
    2039. * //=> SELECT * FROM items WHERE (category != 'software')
    2040. *
    2041. -
    2042. * @return {patio.Dataset} a cloned dataset with the order reversed.
    2043. +
    2044. * @example
    2045. +
    2046. * DB.from("items").filter({category : 'software', id : 3}).invert()
    2047. +
    2048. * //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
    2049. *
    2050. +
    2051. * @return {patio.Dataset} a cloned dataset with the filter inverted.
    2052. **/
    2053. -
    2054. reverse: function (args) {
    2055. -
    2056. 46 args = argsToArray(arguments);
    2057. -
    2058. 46 return this.order.apply(this, this._invertOrder(args.length ? args : this.__opts.order));
    2059. +
    2060. invert: function () {
    2061. +
    2062. 3 var having = this.__opts.having, where = this.__opts.where;
    2063. +
    2064. 3 if (!(having || where)) {
    2065. +
    2066. 1 throw new QueryError("No current filter");
    2067. +
    2068. }
    2069. +
    2070. 2 var o = {}
    2071. +
    2072. 2 if (having) {
    2073. +
    2074. 1 o.having = BooleanExpression.invert(having);
    2075. +
    2076. }
    2077. +
    2078. 2 if (where) {
    2079. +
    2080. 2 o.where = BooleanExpression.invert(where);
    2081. +
    2082. }
    2083. +
    2084. 2 return this.mergeOptions(o);
    2085. },
    2086. /**
    2087. -
    2088. * @see patio.Dataset#reverse
    2089. +
    2090. * Returns a cloned dataset with an inner join applied.
    2091. +
    2092. *
    2093. +
    2094. * @see patio.Dataset#joinTable
    2095. */
    2096. -
    2097. reverseOrder: function () {
    2098. -
    2099. 16 return this.reverse.apply(this, arguments);
    2100. +
    2101. join: function () {
    2102. +
    2103. 212 return this.innerJoin.apply(this, arguments);
    2104. },
    2105. /**
    2106. -
    2107. * Returns a copy of the dataset with the columns selected changed
    2108. -
    2109. * to the given columns. This also takes a function similar to {@link patio.Dataset#filter}
    2110. +
    2111. * Returns a joined dataset. Uses the following arguments:
    2112. *
    2113. * @example
    2114. -
    2115. * DB.from("items").select("a");
    2116. -
    2117. * //=> SELECT a FROM items
    2118. *
    2119. -
    2120. * DB.from("items").select("a", "b");
    2121. -
    2122. * //=> SELECT a, b FROM items
    2123. +
    2124. * DB.from("items").joinTable("leftOuter", "categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;
    2125. +
    2126. * //=>'SELECT
    2127. +
    2128. * *
    2129. +
    2130. * FROM
    2131. +
    2132. * `items`
    2133. +
    2134. * LEFT OUTER JOIN
    2135. +
    2136. * `categories` ON (
    2137. +
    2138. * (`categories`.`categoryId` = `items`.`id`)
    2139. +
    2140. * AND
    2141. +
    2142. * (`categories`.`categoryId` IN (1,2, 3))
    2143. +
    2144. * )
    2145. +
    2146. * DB.from("items").leftOuter("categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;
    2147. +
    2148. * //=>'SELECT
    2149. +
    2150. * *
    2151. +
    2152. * FROM
    2153. +
    2154. * `items`
    2155. +
    2156. * LEFT OUTER JOIN
    2157. +
    2158. * `categories` ON (
    2159. +
    2160. * (`categories`.`categoryId` = `items`.`id`)
    2161. +
    2162. * AND
    2163. +
    2164. * (`categories`.`categoryId` IN (1,2, 3))
    2165. +
    2166. * )
    2167. *
    2168. -
    2169. * DB.from("items").select("a", function(){
    2170. -
    2171. * return this.sum("b")
    2172. -
    2173. * }).sql; //=> SELECT a, sum(b) FROM items
    2174. +
    2175. * DB.from("items").leftOuterJoin("categories", {categoryId:"id"}).sql
    2176. +
    2177. * //=> SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2178. *
    2179. -
    2180. * @param {String|patio.sql.Identifier|Function} args variable number of colums to select
    2181. -
    2182. * @return {patio.Dataset} a cloned dataset with the columns selected changed.
    2183. -
    2184. */
    2185. -
    2186. select: function (args) {
    2187. -
    2188. 663 args = flatten(argsToArray(arguments));
    2189. -
    2190. 663 var columns = [];
    2191. -
    2192. 663 args.forEach(function (c) {
    2193. -
    2194. 1043 if (isFunction(c)) {
    2195. -
    2196. 23 var res = c.apply(sql, [sql]);
    2197. -
    2198. 23 columns = columns.concat(isArray(res) ? res : [res]);
    2199. -
    2200. } else {
    2201. -
    2202. 1020 columns.push(c);
    2203. -
    2204. }
    2205. -
    2206. });
    2207. -
    2208. 663 var select = [];
    2209. -
    2210. 663 columns.forEach(function (c) {
    2211. -
    2212. 1046 if (isHash(c)) {
    2213. -
    2214. 3 for (var i in c) {
    2215. -
    2216. 4 select.push(new AliasedExpression(new Identifier(i), c[i]));
    2217. -
    2218. }
    2219. -
    2220. 1043 } else if (isString(c)) {
    2221. -
    2222. 344 select.push(this.stringToIdentifier(c));
    2223. -
    2224. } else {
    2225. -
    2226. 699 select.push(c);
    2227. -
    2228. }
    2229. -
    2230. }, this);
    2231. -
    2232. 663 return this.mergeOptions({select: select});
    2233. -
    2234. -
    2235. },
    2236. -
    2237. -
    2238. /**
    2239. -
    2240. * Returns a cloned dataset that selects *.
    2241. +
    2242. * DB.from("items").rightOuterJoin("categories", {categoryId:"id"}).sql
    2243. +
    2244. * //=> SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2245. *
    2246. -
    2247. * @return {patio.Dataset} a cloned dataset that selects *.
    2248. -
    2249. */
    2250. -
    2251. selectAll: function () {
    2252. -
    2253. 6 return this.mergeOptions({select: null});
    2254. -
    2255. },
    2256. -
    2257. -
    2258. /**
    2259. -
    2260. * Selects the columns if only if there is not already select sources.
    2261. +
    2262. * DB.from("items").fullOuterJoin("categories", {categoryId:"id"}).sql
    2263. +
    2264. * //=> SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2265. *
    2266. -
    2267. * @example
    2268. +
    2269. * DB.from("items").innerJoin("categories", {categoryId:"id"}).sql
    2270. +
    2271. * //=> SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2272. *
    2273. -
    2274. * var ds = DB.from("items"); //SELECT * FROM items
    2275. +
    2276. * DB.from("items").leftJoin("categories", {categoryId:"id"}).sql
    2277. +
    2278. * //=> SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2279. *
    2280. -
    2281. * ds.select("a"); //SELECT a FROM items;
    2282. -
    2283. * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
    2284. -
    2285. * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
    2286. +
    2287. * DB.from("items").rightJoin("categories", {categoryId:"id"}).sql
    2288. +
    2289. * //=> SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2290. *
    2291. -
    2292. * @param cols columns to select if there is not already select sources.
    2293. -
    2294. * @return {patio.Dataset} a cloned dataset with the appropriate select sources.
    2295. +
    2296. * DB.from("items").fullJoin("categories", {categoryId:"id"}).sql
    2297. +
    2298. * //=> SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."categoryId" = "items"."id")
    2299. +
    2300. *
    2301. +
    2302. * DB.from("items").naturalJoin("categories").sql
    2303. +
    2304. * //=> SELECT * FROM "items" NATURAL JOIN "categories"
    2305. +
    2306. *
    2307. +
    2308. * DB.from("items").naturalLeftJoin("categories").sql
    2309. +
    2310. * //=> SELECT * FROM "items" NATURAL LEFT JOIN "categories"
    2311. +
    2312. *
    2313. +
    2314. * DB.from("items").naturalRightJoin("categories").sql
    2315. +
    2316. * //=> SELECT * FROM "items" NATURAL RIGHT JOIN "categories"
    2317. +
    2318. *
    2319. +
    2320. * DB.from("items").naturalFullJoin("categories").sql
    2321. +
    2322. * //=> SELECT * FROM "items" NATURAL FULL JOIN "categories"'
    2323. +
    2324. *
    2325. +
    2326. * DB.from("items").crossJoin("categories").sql
    2327. +
    2328. * //=> SELECT * FROM "items" CROSS JOIN "categories"
    2329. +
    2330. *
    2331. +
    2332. * @param {String} type the type of join to do.
    2333. +
    2334. * @param {String|patio.sql.Identifier|patio.Dataset|Object} table depends on the type.
    2335. +
    2336. * <ul>
    2337. +
    2338. * <li>{@link patio.Dataset} - a subselect is performed with an alias</li>
    2339. +
    2340. * <li>Object - an object that has a tableName property.</li>
    2341. +
    2342. * <li>String|{@link patio.sql.Identifier} - the name of the table</li>
    2343. +
    2344. * </ul>
    2345. +
    2346. * @param [expr] - depends on type
    2347. +
    2348. * <ul>
    2349. +
    2350. * <li>Object|Array of two element arrays - Assumes key (1st arg) is column of joined table (unless already
    2351. +
    2352. * qualified), and value (2nd arg) is column of the last joined or primary table (or the
    2353. +
    2354. * implicitQualifier option</li>.
    2355. +
    2356. * <li>Array - If all members of the array are string or {@link patio.sql.Identifier}, considers
    2357. +
    2358. * them as columns and uses a JOIN with a USING clause. Most databases will remove duplicate columns from
    2359. +
    2360. * the result set if this is used.</li>
    2361. +
    2362. * <li>null|undefined(not passed in) - If a cb is not given, doesn't use ON or USING, so the JOIN should be a NATURAL
    2363. +
    2364. * or CROSS join. If a block is given, uses an ON clause based on the block, see below.</li>
    2365. +
    2366. * <li>Everything else - pretty much the same as a using the argument in a call to {@link patio.Dataset#filter},
    2367. +
    2368. * so strings are considered literal, {@link patio.sql.Identifiers} specify boolean columns, and patio.sql
    2369. +
    2370. * expressions can be used. Uses a JOIN with an ON clause.</li>
    2371. +
    2372. * </ul>
    2373. +
    2374. * @param {Object} options an object of options.
    2375. +
    2376. * @param {String|patio.sql.Identifier} [options.tableAlias=undefined] the name of the table's alias when joining, necessary for joining
    2377. +
    2378. * to the same table more than once. No alias is used by default.
    2379. +
    2380. * @param {String|patio.sql.Identifier} [options.implicitQualifier=undefined] The name to use for qualifying implicit conditions. By default,
    2381. +
    2382. * the last joined or primary table is used.
    2383. +
    2384. * @param {Function} [cb] cb - The cb argument should only be given if a JOIN with an ON clause is used,
    2385. +
    2386. * in which case it is called with
    2387. +
    2388. * <ul>
    2389. +
    2390. * <li>table alias/name for the table currently being joined</li>
    2391. +
    2392. * <li> the table alias/name for the last joined (or first table)
    2393. +
    2394. * <li>array of previous</li>
    2395. +
    2396. * </ul>
    2397. +
    2398. * the cb should return an expression to be used in the ON clause.
    2399. +
    2400. *
    2401. +
    2402. * @return {patio.Dataset} a cloned dataset joined using the arguments.
    2403. */
    2404. -
    2405. selectIfNoSource: function (cols) {
    2406. -
    2407. 0 var ret;
    2408. -
    2409. 0 if (!this.hasSelectSource) {
    2410. -
    2411. 0 ret = this.select.apply(this, arguments);
    2412. +
    2413. +
    2414. joinTable: function (type, table, expr, options, cb) {
    2415. +
    2416. 634 var args = argsToArray(arguments);
    2417. +
    2418. 634 if (isFunction(args[args.length - 1])) {
    2419. +
    2420. 12 cb = args[args.length - 1];
    2421. +
    2422. 12 args.pop();
    2423. +
    2424. } else {
    2425. +
    2426. 622 cb = null;
    2427. +
    2428. }
    2429. +
    2430. 634 type = args.shift(), table = args.shift(), expr = args.shift(), options = args.shift();
    2431. +
    2432. 634 expr = isUndefined(expr) ? null : expr, options = isUndefined(options) ? {} : options;
    2433. +
    2434. +
    2435. 634 var h;
    2436. +
    2437. 634 var usingJoin = isArray(expr) && expr.length && expr.every(function (x) {
    2438. +
    2439. 223 return isString(x) || isInstanceOf(x, Identifier)
    2440. +
    2441. });
    2442. +
    2443. 634 if (usingJoin && !this.supportsJoinUsing) {
    2444. +
    2445. 1 h = {};
    2446. +
    2447. 1 expr.forEach(function (s) {
    2448. +
    2449. 1 h[s] = s;
    2450. +
    2451. });
    2452. +
    2453. 1 return this.joinTable(type, table, h, options);
    2454. +
    2455. }
    2456. +
    2457. 633 var tableAlias, lastAlias;
    2458. +
    2459. 633 if (isHash(options)) {
    2460. +
    2461. 623 tableAlias = options.tableAlias;
    2462. +
    2463. 623 lastAlias = options.implicitQualifier;
    2464. +
    2465. 10 } else if (isString(options) || isInstanceOf(options, Identifier)) {
    2466. +
    2467. 9 tableAlias = options;
    2468. +
    2469. 9 lastAlias = null;
    2470. +
    2471. } else {
    2472. +
    2473. 1 throw new QueryError("Invalid options format for joinTable %j4", [options]);
    2474. +
    2475. }
    2476. +
    2477. 632 var tableAliasNum, tableName;
    2478. +
    2479. 632 if (isInstanceOf(table, Dataset)) {
    2480. +
    2481. 11 if (!tableAlias) {
    2482. +
    2483. 6 tableAliasNum = (this.__opts.numDatasetSources || 0) + 1;
    2484. +
    2485. 6 tableAlias = this._datasetAlias(tableAliasNum);
    2486. +
    2487. }
    2488. +
    2489. 11 tableName = tableAlias;
    2490. +
    2491. } else {
    2492. +
    2493. 621 if (!isUndefined(table.tableName)) {
    2494. +
    2495. 1 table = table.tableName;
    2496. +
    2497. }
    2498. +
    2499. 621 if (isArray(table)) {
    2500. +
    2501. 2 table = table.map(this.stringToIdentifier, this);
    2502. +
    2503. } else {
    2504. +
    2505. 619 table = isString(table) ? this.stringToIdentifier(table) : table;
    2506. +
    2507. 619 var parts = this._splitAlias(table), implicitTableAlias = parts[1];
    2508. +
    2509. 619 table = parts[0]
    2510. +
    2511. 619 tableAlias = tableAlias || implicitTableAlias;
    2512. +
    2513. 619 tableName = tableAlias || table;
    2514. +
    2515. }
    2516. +
    2517. }
    2518. +
    2519. 632 var join;
    2520. +
    2521. 632 if (!expr && !cb) {
    2522. +
    2523. 22 join = new JoinClause(type, table, tableAlias);
    2524. +
    2525. 610 } else if (usingJoin) {
    2526. +
    2527. 9 if (cb) {
    2528. +
    2529. 1 throw new QueryError("cant use a cb if an array is given");
    2530. +
    2531. }
    2532. +
    2533. 8 join = new JoinUsingClause(expr, type, table, tableAlias);
    2534. } else {
    2535. -
    2536. 0 ret = this.mergeOptions();
    2537. +
    2538. 601 lastAlias = lastAlias || this.__opts["lastJoinedTable"] || this.firstSourceAlias;
    2539. +
    2540. 600 if (Expression.isConditionSpecifier(expr)) {
    2541. +
    2542. 588 var newExpr = [];
    2543. +
    2544. 588 for (var i in expr) {
    2545. +
    2546. 909 var val = expr[i];
    2547. +
    2548. 909 if (isArray(val) && val.length == 2) {
    2549. +
    2550. 418 i = val[0], val = val[1];
    2551. +
    2552. }
    2553. +
    2554. 909 var k = this.qualifiedColumnName(i, tableName), v;
    2555. +
    2556. 909 if (isInstanceOf(val, Identifier)) {
    2557. +
    2558. 405 v = val.qualify(lastAlias);
    2559. +
    2560. } else {
    2561. +
    2562. 504 v = val;
    2563. +
    2564. }
    2565. +
    2566. 909 newExpr.push([k, v]);
    2567. +
    2568. }
    2569. +
    2570. 588 expr = newExpr;
    2571. +
    2572. }
    2573. +
    2574. 600 if (isFunction(cb)) {
    2575. +
    2576. 11 var expr2 = cb.apply(sql, [tableName, lastAlias, this.__opts.join || []]);
    2577. +
    2578. 11 expr = expr ? new BooleanExpression("AND", expr, expr2) : expr2;
    2579. +
    2580. }
    2581. +
    2582. 600 join = new JoinOnClause(expr, type, table, tableAlias);
    2583. }
    2584. -
    2585. 0 return ret;
    2586. +
    2587. 630 var opts = {join: (this.__opts.join || []).concat([join]), lastJoinedTable: tableName};
    2588. +
    2589. 630 if (tableAliasNum) {
    2590. +
    2591. 6 opts.numDatasetSources = tableAliasNum;
    2592. +
    2593. }
    2594. +
    2595. 630 return this.mergeOptions(opts);
    2596. +
    2597. },
    2598. /**
    2599. -
    2600. * Returns a copy of the dataset with the given columns added
    2601. -
    2602. * to the existing selected columns. If no columns are currently selected,
    2603. -
    2604. * it will select the columns given in addition to *.
    2605. +
    2606. * If given an integer, the dataset will contain only the first l results.
    2607. +
    2608. If a second argument is given, it is used as an offset. To use
    2609. +
    2610. * an offset without a limit, pass null as the first argument.
    2611. *
    2612. * @example
    2613. -
    2614. * DB.from("items").select("a").selectAppend("b").sql;
    2615. -
    2616. * //=> SELECT b FROM items
    2617. *
    2618. -
    2619. * DB.from("items").select("a").selectAppend("b", "c", "d").sql
    2620. -
    2621. * //=> SELECT a, b, c, d FROM items
    2622. +
    2623. * DB.from("items").limit(10)
    2624. +
    2625. * //=> SELECT * FROM items LIMIT 10
    2626. +
    2627. * DB.from("items").limit(10, 20)
    2628. +
    2629. * //=> SELECT * FROM items LIMIT 10 OFFSET 20
    2630. +
    2631. * DB.from("items").limit([3, 7]).sql
    2632. +
    2633. * //=> SELECT * FROM items LIMIT 5 OFFSET 3');
    2634. +
    2635. * DB.from("items").limit(null, 20)
    2636. +
    2637. * //=> SELECT * FROM items OFFSET 20
    2638. *
    2639. -
    2640. * DB.from("items").selectAppend("b").sql
    2641. -
    2642. * //=> SELECT *, b FROM items
    2643. +
    2644. * DB.from("items").limit('6', sql['a() - 1']).sql
    2645. +
    2646. * => 'SELECT * FROM items LIMIT 6 OFFSET a() - 1');
    2647. *
    2648. -
    2649. * @param [...] cols variable number of columns to add to the select statement
    2650. +
    2651. * @param {Number|String|Number[]} limit the limit to apply
    2652. +
    2653. * @param {Number|String|patio.sql.LiteralString} offset the offset to apply
    2654. *
    2655. -
    2656. * @return {patio.Dataset} returns a cloned dataset with the new select columns appended.
    2657. -
    2658. */
    2659. -
    2660. selectAppend: function (cols) {
    2661. -
    2662. 7 cols = argsToArray(arguments);
    2663. -
    2664. 7 var currentSelect = this.__opts.select;
    2665. -
    2666. 7 if (!currentSelect || !currentSelect.length) {
    2667. -
    2668. 3 currentSelect = [this._static.WILDCARD];
    2669. +
    2670. * @return {patio.Dataset} a cloned dataset witht the LIMIT and OFFSET applied.
    2671. +
    2672. **/
    2673. +
    2674. limit: function (limit, offset) {
    2675. +
    2676. 46 if (this.__opts.sql) {
    2677. +
    2678. 7 return this.fromSelf().limit(limit, offset);
    2679. }
    2680. -
    2681. 7 return this.select.apply(this, currentSelect.concat(cols));
    2682. +
    2683. 39 if (isArray(limit) && limit.length == 2) {
    2684. +
    2685. 1 offset = limit[0];
    2686. +
    2687. 1 limit = limit[1] - limit[0] + 1;
    2688. +
    2689. }
    2690. +
    2691. 39 if (isString(limit) || isInstanceOf(limit, LiteralString)) {
    2692. +
    2693. 2 limit = parseInt("" + limit, 10);
    2694. +
    2695. }
    2696. +
    2697. 39 if (isNumber(limit) && limit < 1) {
    2698. +
    2699. 2 throw new QueryError("Limit must be >= 1");
    2700. +
    2701. }
    2702. +
    2703. 37 var opts = {limit: limit};
    2704. +
    2705. 37 if (offset) {
    2706. +
    2707. 9 if (isString(offset) || isInstanceOf(offset, LiteralString)) {
    2708. +
    2709. 1 offset = parseInt("" + offset, 10);
    2710. +
    2711. 1 isNaN(offset) && (offset = 0);
    2712. +
    2713. }
    2714. +
    2715. 9 if (isNumber(offset) && offset < 0) {
    2716. +
    2717. 1 throw new QueryError("Offset must be >= 0");
    2718. +
    2719. }
    2720. +
    2721. 8 opts.offset = offset;
    2722. +
    2723. }
    2724. +
    2725. 36 return this.mergeOptions(opts);
    2726. },
    2727. /**
    2728. -
    2729. * Returns a copy of the dataset with the given columns added
    2730. -
    2731. * to the existing selected columns. If no columns are currently selected
    2732. -
    2733. * it will just select the columns given.
    2734. -
    2735. *
    2736. -
    2737. * @example
    2738. -
    2739. * DB.from("items").select("a").select("b").sql;
    2740. -
    2741. * //=> SELECT b FROM items
    2742. *
    2743. -
    2744. * DB.from("items").select("a").selectMore("b", "c", "d").sql
    2745. -
    2746. * //=> SELECT a, b, c, d FROM items
    2747. +
    2748. * Returns a cloned dataset with a not equal expression added to the WHERE
    2749. +
    2750. * clause.
    2751. *
    2752. -
    2753. * DB.from("items").selectMore("b").sql
    2754. -
    2755. * //=> SELECT b FROM items
    2756. +
    2757. * @example
    2758. +
    2759. * DB.from("test").neq({x : 1});
    2760. +
    2761. * //=> SELECT * FROM test WHERE (x != 1)
    2762. +
    2763. * DB.from("test").neq({x : 1, y : 10});
    2764. +
    2765. * //=> SELECT * FROM test WHERE ((x != 1) AND (y != 10))
    2766. *
    2767. -
    2768. * @param [...] cols variable number of columns to add to the select statement
    2769. +
    2770. * @param {Object} obj object used to create the not equal expression
    2771. *
    2772. -
    2773. * @return {patio.Dataset} returns a cloned dataset with the new select columns appended.
    2774. +
    2775. * @return {patio.Dataset} a cloned dataset with the not equal expression added to the WHERE clause.
    2776. */
    2777. -
    2778. selectMore: function (cols) {
    2779. -
    2780. 10 cols = argsToArray(arguments);
    2781. -
    2782. 10 var currentSelect = this.__opts.select;
    2783. -
    2784. 10 return this.select.apply(this, (currentSelect || []).concat(cols));
    2785. +
    2786. neq: function (obj) {
    2787. +
    2788. 2 return this.filter(this.__createBoolExpression("neq", obj));
    2789. },
    2790. /**
    2791. -
    2792. * Set the default values for insert and update statements. The values hash passed
    2793. -
    2794. * to insert or update are merged into this hash, so any values in the hash passed
    2795. -
    2796. * to insert or update will override values passed to this method.
    2797. -
    2798. *
    2799. -
    2800. * @example
    2801. -
    2802. * DB.from("items").setDefaults({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();
    2803. -
    2804. * //=> INSERT INTO items (a, c, b) VALUES ('d', 'c', 'b')
    2805. -
    2806. *
    2807. -
    2808. * @param {Object} hash object with key value pairs to use as override values
    2809. *
    2810. -
    2811. * @return {patio.Dataset} a cloned dataset with the defaults added to the current datasets defaults.
    2812. -
    2813. */
    2814. -
    2815. setDefaults: function (hash) {
    2816. -
    2817. 5 return this.mergeOptions({defaults: merge({}, this.__opts.defaults || {}, hash)});
    2818. -
    2819. },
    2820. -
    2821. -
    2822. /**
    2823. -
    2824. * Set values that override hash arguments given to insert and update statements.
    2825. -
    2826. * This hash is merged into the hash provided to insert or update, so values
    2827. -
    2828. * will override any values given in the insert/update hashes.
    2829. +
    2830. * Returns a cloned dataset with an equal expression added to the WHERE
    2831. +
    2832. * clause.
    2833. *
    2834. * @example
    2835. -
    2836. * DB.from("items").setOverrides({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();
    2837. -
    2838. * //=> INSERT INTO items (a, c, b) VALUES ('a', 'c', 'b')
    2839. +
    2840. * DB.from("test").eq({x : 1});
    2841. +
    2842. * //=> SELECT * FROM test WHERE (x = 1)
    2843. +
    2844. * DB.from("test").eq({x : 1, y : 10});
    2845. +
    2846. * //=> SELECT * FROM test WHERE ((x = 1) AND (y = 10))
    2847. *
    2848. -
    2849. * @param {Object} hash object with key value pairs to use as override values
    2850. +
    2851. * @param {Object} obj object used to create the equal expression
    2852. *
    2853. -
    2854. * @return {patio.Dataset} a cloned dataset with the overrides added to the current datasets overrides.
    2855. +
    2856. * @return {patio.Dataset} a cloned dataset with the equal expression added to the WHERE clause.
    2857. */
    2858. -
    2859. setOverrides: function (hash) {
    2860. -
    2861. 5 return this.mergeOptions({overrides: merge({}, this.__opts.overrides || {}, hash)});
    2862. +
    2863. eq: function (obj) {
    2864. +
    2865. 2 return this.filter(this.__createBoolExpression("eq", obj));
    2866. },
    2867. /**
    2868. -
    2869. * Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
    2870. -
    2871. * @example
    2872. -
    2873. * DB.from("items").group("a").having({a : 1}).where("b").unfiltered().sql;
    2874. -
    2875. * //=> SELECT * FROM items GROUP BY a
    2876. *
    2877. -
    2878. * @return {patio.Dataset} a cloned dataset with no HAVING or WHERE clause.
    2879. -
    2880. */
    2881. -
    2882. unfiltered: function () {
    2883. -
    2884. 14 return this.mergeOptions({where: null, having: null});
    2885. -
    2886. },
    2887. -
    2888. -
    2889. /**
    2890. -
    2891. * Returns a copy of the dataset with no GROUP or HAVING clause.
    2892. +
    2893. * Returns a cloned dataset with a greater than expression added to the WHERE
    2894. +
    2895. * clause.
    2896. *
    2897. * @example
    2898. -
    2899. * DB.from("t").group("a").having({a : 1}).where("b").ungrouped().sql;
    2900. -
    2901. * //=> SELECT * FROM t WHERE b
    2902. +
    2903. * DB.from("test").gt({x : 1});
    2904. +
    2905. * //=> SELECT * FROM test WHERE (x > 1)
    2906. +
    2907. * DB.from("test").gt({x : 1, y : 10});
    2908. +
    2909. * //=> SELECT * FROM test WHERE ((x > 1) AND (y > 10))
    2910. *
    2911. -
    2912. * @return {patio.Dataset} a cloned dataset with no GROUP or HAVING clause.
    2913. +
    2914. * @param {Object} obj object used to create the greater than expression.
    2915. +
    2916. *
    2917. +
    2918. * @return {patio.Dataset} a cloned dataset with the greater than expression added to the WHERE clause.
    2919. */
    2920. -
    2921. ungrouped: function () {
    2922. -
    2923. 1 return this.mergeOptions({group: null, having: null});
    2924. +
    2925. gt: function (obj) {
    2926. +
    2927. 2 return this.filter(this.__createBoolExpression("gt", obj));
    2928. },
    2929. /**
    2930. -
    2931. * Adds a UNION clause using a second dataset object.
    2932. -
    2933. * A UNION compound dataset returns all rows in either the current dataset
    2934. -
    2935. * or the given dataset.
    2936. -
    2937. * Options:
    2938. -
    2939. * :alias :: Use the given value as the from_self alias
    2940. -
    2941. * :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
    2942. -
    2943. * :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
    2944. -
    2945. *
    2946. -
    2947. * @example
    2948. -
    2949. * DB.from("items").union(DB.from("otherItems")).sql;
    2950. -
    2951. * //=> SELECT * FROM items UNION SELECT * FROM other_items
    2952. -
    2953. *
    2954. -
    2955. * DB.from("items").union(DB.from("otherItems"), {all : true, fromSelf : false}).sql;
    2956. -
    2957. * //=> SELECT * FROM items UNION ALL SELECT * FROM other_items
    2958. *
    2959. -
    2960. * DB.from("items").union(DB.from("otherItems"), {alias : "i"})
    2961. -
    2962. * //=> SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS i
    2963. +
    2964. * Returns a cloned dataset with a less than expression added to the WHERE
    2965. +
    2966. * clause.
    2967. *
    2968. -
    2969. * @param {patio.Dataset} dataset dataset to union with
    2970. -
    2971. * @param {Object} opts addional options
    2972. -
    2973. * @param {String|patio.sql.Identifier} [opts.alias] Alias to use as the fromSelf alias.
    2974. -
    2975. * @param {Boolean} [opt.all=false] Set to true to use UNION ALL instead of UNION so duplicate rows can occur
    2976. -
    2977. * @param {Boolean} [opts.fromSelf=true] Set to false to not wrap the returned dataset in a fromSelf.
    2978. +
    2979. * @example
    2980. +
    2981. * DB.from("test").lt({x : 1});
    2982. +
    2983. * //=> SELECT * FROM test WHERE (x < 1)
    2984. +
    2985. * DB.from("test").lt({x : 1, y : 10});
    2986. +
    2987. * //=> SELECT * FROM test WHERE ((x < 1) AND (y < 10))
    2988. *
    2989. -
    2990. * @return {patio.Dataset} a cloned dataset with the union.
    2991. +
    2992. * @param {Object} obj object used to create the less than expression.
    2993. *
    2994. +
    2995. * @return {patio.Dataset} a cloned dataset with the less than expression added to the WHERE clause.
    2996. */
    2997. -
    2998. union: function (dataset, opts) {
    2999. -
    3000. 21 opts = isUndefined(opts) ? {} : opts;
    3001. -
    3002. 21 if (!isHash(opts)) {
    3003. -
    3004. 3 opts = {all: opts};
    3005. -
    3006. }
    3007. -
    3008. 21 return this.compoundClone("union", dataset, opts);
    3009. +
    3010. lt: function (obj) {
    3011. +
    3012. 2 return this.filter(this.__createBoolExpression("lt", obj));
    3013. },
    3014. /**
    3015. -
    3016. * Returns a copy of the dataset with no limit or offset.
    3017. +
    3018. * Returnes a cloned dataset with the IS NOT expression added to the WHERE
    3019. +
    3020. * clause.
    3021. *
    3022. * @example
    3023. -
    3024. * DB.from("t").limit(10, 20).unlimited().sql;
    3025. -
    3026. * //=> SELECT * FROM t
    3027. -
    3028. *
    3029. -
    3030. * @return {patio.Dataset} a cloned dataset with no limit or offset.
    3031. -
    3032. */
    3033. -
    3034. unlimited: function () {
    3035. -
    3036. 1 return this.mergeOptions({limit: null, offset: null});
    3037. -
    3038. },
    3039. -
    3040. -
    3041. /**
    3042. -
    3043. * Returns a copy of the dataset with no order.
    3044. *
    3045. -
    3046. * @example
    3047. -
    3048. * DB.from("t").order("a", sql.identifier("b").desc()).unordered().sql;
    3049. -
    3050. * //=> SELECT * FROM t
    3051. +
    3052. * DB.from("test").isNot({boolFlag : null});
    3053. +
    3054. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL);
    3055. +
    3056. * DB.from("test").isNot({boolFlag : false, otherFlag : true, name : null});
    3057. +
    3058. * => SELECT * FROM test WHERE ((boolFlag IS NOT FALSE) AND (otherFlag IS NOT TRUE) AND (name IS NOT NULL));
    3059. *
    3060. -
    3061. * @return {patio.Dataset} a cloned dataset with no order.
    3062. -
    3063. */
    3064. -
    3065. unordered: function () {
    3066. -
    3067. 115 return this.order(null);
    3068. -
    3069. },
    3070. -
    3071. -
    3072. /**
    3073. -
    3074. * Add a condition to the WHERE clause. See {@link patio.Dataset#filter} for argument types.
    3075. +
    3076. * @param {Object} obj object used to create the IS NOT expression for.
    3077. *
    3078. -
    3079. * @example
    3080. -
    3081. * DB.from("test").where('price < ? AND id in ?', 100, [1, 2, 3]).sql;
    3082. -
    3083. * //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
    3084. -
    3085. * DB.from("test").where('price < {price} AND id in {ids}', {price:100, ids:[1, 2, 3]}).sql;
    3086. -
    3087. * //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))")
    3088. +
    3089. * @return {patio.Dataset} a cloned dataset with the IS NOT expression added to the WHERE clause.
    3090. *
    3091. */
    3092. -
    3093. where: function () {
    3094. -
    3095. 252 return this._filter.apply(this, ["where"].concat(argsToArray(arguments)));
    3096. +
    3097. isNot: function (obj) {
    3098. +
    3099. 4 return this.filter(this.__createBoolExpression("isNot", obj));
    3100. },
    3101. /**
    3102. -
    3103. * Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
    3104. -
    3105. * A common table expression acts as an inline view for the query.
    3106. +
    3107. * Returnes a cloned dataset with the IS expression added to the WHERE
    3108. +
    3109. * clause.
    3110. *
    3111. -
    3112. * @name with
    3113. * @example
    3114. *
    3115. -
    3116. * DB.from("t")["with"]("t", db.from("x"))["with"]("j", db.from("y")).sql;
    3117. -
    3118. * //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y) SELECT * FROM t'
    3119. -
    3120. *
    3121. -
    3122. * DB.from("t")["with"]("t", db.from("x")).withRecursive("j", db.from("y"), db.from("j")).sql;
    3123. -
    3124. * //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t'
    3125. +
    3126. * DB.from("test").is({boolFlag : null});
    3127. +
    3128. * => SELECT * FROM test WHERE (boolFlag IS NULL);
    3129. +
    3130. * DB.from("test").is({boolFlag : false, otherFlag : true, name : null});
    3131. +
    3132. * => SELECT * FROM test WHERE ((boolFlag IS FALSE) AND (otherFlag IS TRUE) AND (name IS NULL));
    3133. *
    3134. -
    3135. * DB.from("t")["with"]("t", db.from("x"), {args:["b"]}).sql;
    3136. -
    3137. * //=> 'WITH t(b) AS (SELECT * FROM x) SELECT * FROM t'
    3138. +
    3139. * @param {Object} obj object used to create the IS expression for.
    3140. *
    3141. -
    3142. * @param {String} name the name of the to assign to the CTE.
    3143. -
    3144. * @param {patio.Dataset} dataset the dataset to use for the CTE.
    3145. -
    3146. * @param {Object} opts extra options.
    3147. -
    3148. * @param {String[]} [opts.args] colums/args for the CTE.
    3149. -
    3150. * @param {Boolean} [opts.recursive] set to true that the CTE is recursive.
    3151. +
    3152. * @return {patio.Dataset} a cloned dataset with the IS expression added to the WHERE clause.
    3153. *
    3154. -
    3155. * @return {patio.Dataset} a cloned dataset with the CTE.
    3156. */
    3157. -
    3158. "with": function (name, dataset, opts) {
    3159. -
    3160. 6 if (!this.supportsCte) {
    3161. -
    3162. 1 throw new QueryError("this dataset does not support common table expressions");
    3163. -
    3164. }
    3165. -
    3166. 5 return this.mergeOptions({
    3167. -
    3168. "with": (this.__opts["with"] || []).concat([merge(opts || {}, {name: this.stringToIdentifier(name), dataset: dataset})])
    3169. -
    3170. });
    3171. +
    3172. is: function (obj) {
    3173. +
    3174. 4 return this.filter(this.__createBoolExpression("is", obj));
    3175. },
    3176. /**
    3177. -
    3178. * Add a recursive common table expression (CTE) with the given name, a dataset that
    3179. -
    3180. * defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
    3181. -
    3182. * of the CTE.
    3183. -
    3184. *
    3185. -
    3186. * @example
    3187. -
    3188. *
    3189. -
    3190. * //Sing withRecursive call.
    3191. -
    3192. * DB.from("t").withRecursive("t", db.from("x"), db.from("t")).sql;
    3193. -
    3194. * //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
    3195. +
    3196. * Returnes a cloned dataset with the IS NOT NULL boolean expression added to the WHERE
    3197. +
    3198. * clause.
    3199. *
    3200. -
    3201. * //Multiple withRecursive calls.
    3202. -
    3203. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"))
    3204. -
    3205. * .withRecursive("j", db.from("y"), db.from("j")).sql;
    3206. -
    3207. * //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t),
    3208. -
    3209. * j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t';
    3210. +
    3211. * @example
    3212. *
    3213. -
    3214. * //Adding args
    3215. -
    3216. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {args:["b", "c"]}).sql;
    3217. -
    3218. * //=> 'WITH t(b, c) AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
    3219. +
    3220. * DB.from("test").isNotNull("boolFlag");
    3221. +
    3222. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL);
    3223. +
    3224. * DB.from("test").isNotNull("boolFlag", "otherFlag");
    3225. +
    3226. * => SELECT * FROM test WHERE (boolFlag IS NOT NULL AND otherFlag IS NOT NULL);
    3227. *
    3228. -
    3229. * //Setting union all to false
    3230. -
    3231. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {unionAll:false}).sql;
    3232. -
    3233. * //=> 'WITH t AS (SELECT * FROM x UNION SELECT * FROM t) SELECT * FROM t');
    3234. +
    3235. * @param {String...} arr variable number of arguments to create an IS NOT NULL expression for.
    3236. *
    3237. -
    3238. * @param {String} name the name to assign to the CTE
    3239. -
    3240. * @param {patio.Dataset} nonRecursive the non-recursive part of the CTE
    3241. -
    3242. * @param {patio.Dataset} recursive the recursive part of the CTE
    3243. -
    3244. * @param {Object} [opts={}] extra options
    3245. -
    3246. * @param {String[]} [opts.args] columns to include with the CTE
    3247. -
    3248. * @param {Boolena} [opts.unionAll] set to false to use UNION instead of UNION ALL when combining non recursive
    3249. -
    3250. * with recursive.
    3251. +
    3252. * @return {patio.Dataset} a cloned dataset with the IS NOT NULL expression added to the WHERE clause.
    3253. *
    3254. -
    3255. * @return {patio.Dataset} a cloned dataset with the CTE.
    3256. */
    3257. -
    3258. withRecursive: function (name, nonRecursive, recursive, opts) {
    3259. -
    3260. 7 if (!this.supportsCte) {
    3261. -
    3262. 1 throw new QueryError("This dataset does not support common table expressions");
    3263. -
    3264. }
    3265. -
    3266. 6 opts = opts || {};
    3267. -
    3268. 6 var wit = (this.__opts["with"] || []).concat([merge(opts, {recursive: true, name: this.stringToIdentifier(name), dataset: nonRecursive.union(recursive, {all: opts.unionAll != false, fromSelf: false})})]);
    3269. -
    3270. 6 return this.mergeOptions({"with": wit});
    3271. +
    3272. isNotNull: function (arr) {
    3273. +
    3274. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);
    3275. +
    3276. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    3277. },
    3278. /**
    3279. -
    3280. * Returns a copy of the dataset with the static SQL used. This is useful if you want
    3281. -
    3282. * to keep the same {@link patio.Dataset#rowCb}/{@link patio.Dataset#graph},
    3283. -
    3284. * but change the SQL used to custom SQL.
    3285. +
    3286. * Returnes a cloned dataset with the IS NULL boolean expression added to the WHERE
    3287. +
    3288. * clause.
    3289. *
    3290. * @example
    3291. -
    3292. * DB.from("items").withSql('SELECT * FROM foo')
    3293. -
    3294. * //=> SELECT * FROM foo
    3295. *
    3296. -
    3297. * @param {String} sql sql for the dataset to use.
    3298. +
    3299. * DB.from("test").isNull("boolFlag");
    3300. +
    3301. * => SELECT * FROM test WHERE (boolFlag IS NULL);
    3302. +
    3303. * DB.from("test").isNull("boolFlag", "otherFlag");
    3304. +
    3305. * => SELECT * FROM test WHERE (boolFlag IS NULL AND otherFlag IS NULL);
    3306. *
    3307. -
    3308. * @return {patio.Dataset} a cloned dataset with the static sql set.
    3309. -
    3310. */
    3311. -
    3312. withSql: function (sql) {
    3313. -
    3314. 46 var args = argsToArray(arguments).slice(1);
    3315. -
    3316. 46 if (args.length) {
    3317. -
    3318. 23 sql = new PlaceHolderLiteralString(sql, args)
    3319. -
    3320. }
    3321. -
    3322. 46 return this.mergeOptions({sql: sql});
    3323. -
    3324. },
    3325. -
    3326. -
    3327. -
    3328. /**
    3329. -
    3330. * Add the dataset to the list of compounds
    3331. +
    3332. * @param {String...} arr variable number of arguments to create an IS NULL expression for.
    3333. *
    3334. -
    3335. * @param {String} type the type of compound (i.e. "union", "intersect")
    3336. -
    3337. * @param {patio.Dataset} dataset the dataset to add to
    3338. -
    3339. * @param [Object] [options={}] compound option to use (i.e {all : true})
    3340. +
    3341. * @return {patio.Dataset} a cloned dataset with the IS NULL expression added to the WHERE clause.
    3342. *
    3343. -
    3344. * @return {patio.Dataset} ds with the dataset added to the compounds.
    3345. */
    3346. -
    3347. compoundClone: function (type, dataset, options) {
    3348. -
    3349. 51 var ds = this._compoundFromSelf().mergeOptions({compounds: (array.toArray(this.__opts.compounds || [])).concat([
    3350. -
    3351. [type, dataset._compoundFromSelf(), options.all]
    3352. -
    3353. ])});
    3354. -
    3355. 51 return options.fromSelf === false ? ds : ds.fromSelf(options);
    3356. +
    3357. isNull: function (arr) {
    3358. +
    3359. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);
    3360. +
    3361. 2 return this.filter(this.__createBoolExpression("is", arr));
    3362. },
    3363. /**
    3364. -
    3365. * Returns the a cloned dataset with out the {@link patio.Dataset#rowCb}
    3366. +
    3367. * Returnes a cloned dataset with the IS NOT TRUE boolean expression added to the WHERE
    3368. +
    3369. * clause.
    3370. *
    3371. * @example
    3372. -
    3373. * var ds = DB.from("test");
    3374. -
    3375. * ds.rowCb = function(r){
    3376. -
    3377. * r.a = r.a * 2;
    3378. -
    3379. * }
    3380. *
    3381. -
    3382. * ds.all().then(function(ret){
    3383. -
    3384. * //ret === [{a : 4}, {a : 6}]
    3385. -
    3386. * });
    3387. -
    3388. * ds.naked().all().then(function(ret){
    3389. -
    3390. * //ret === [{a : 2}, {a : 3}];
    3391. -
    3392. * });
    3393. +
    3394. * DB.from("test").isNotTrue("boolFlag");
    3395. +
    3396. * => SELECT * FROM test WHERE (boolFlag IS NOT TRUE);
    3397. +
    3398. * DB.from("test").isNotTrue("boolFlag", "otherFlag");
    3399. +
    3400. * => SELECT * FROM test WHERE (boolFlag IS NOT TRUE AND otherFlag IS NOT TRUE);
    3401. *
    3402. -
    3403. * @return {patio.Dataset} a cloned dataset with out the {@link patio.Dataset#rowCb}
    3404. -
    3405. */
    3406. -
    3407. naked: function () {
    3408. -
    3409. 2266 var ds = this.mergeOptions({});
    3410. -
    3411. 2266 ds.rowCb = null;
    3412. -
    3413. 2266 return ds;
    3414. -
    3415. },
    3416. -
    3417. -
    3418. /**
    3419. -
    3420. * @private
    3421. -
    3422. * Protected
    3423. +
    3424. * @param {String...} arr variable number of arguments to create an IS NOT TRUE expression for.
    3425. *
    3426. -
    3427. * Internal filter method so it works on either the having or where clauses.
    3428. -
    3429. */
    3430. -
    3431. _filter: function (clause) {
    3432. -
    3433. 3765 var cond = argsToArray(arguments).slice(1), cb;
    3434. -
    3435. 3765 if (cond.length && isFunction(cond[cond.length - 1])) {
    3436. -
    3437. 59 cb = cond.pop();
    3438. -
    3439. }
    3440. -
    3441. 3765 cond = cond.length == 1 ? cond[0] : cond
    3442. -
    3443. 3765 if ((cond == null || cond == undefined || cond === "") || (isArray(cond) && cond.length == 0 && !cb) || (isObject(cond) && isEmpty(cond) && !cb)) {
    3444. -
    3445. 293 return this.mergeOptions();
    3446. -
    3447. } else {
    3448. -
    3449. 3472 cond = this._filterExpr(cond, cb);
    3450. -
    3451. 3469 var cl = this.__opts[clause];
    3452. -
    3453. 3469 cl && (cond = new BooleanExpression("AND", cl, cond));
    3454. -
    3455. 3469 var opts = {};
    3456. -
    3457. 3469 opts[clause] = cond;
    3458. -
    3459. 3469 return this.mergeOptions(opts);
    3460. -
    3461. }
    3462. -
    3463. },
    3464. -
    3465. -
    3466. /**
    3467. -
    3468. * Splits a possible implicit alias, handling both {@link patio.sql.AliasedExpression}s
    3469. -
    3470. * and strings. Returns an array of two elements, with the first being the
    3471. -
    3472. * main expression, and the second being the alias. Alias may be null if it is a
    3473. -
    3474. * string that does not contain an alias {table}___{alias}.
    3475. -
    3476. */
    3477. -
    3478. _splitAlias: function (c) {
    3479. -
    3480. 619 var ret;
    3481. -
    3482. 619 if (isInstanceOf(c, AliasedExpression)) {
    3483. -
    3484. 5 ret = [c.expression, c.alias];
    3485. -
    3486. 614 } else if (isString(c)) {
    3487. -
    3488. 0 var parts = this._splitString(c), cTable = parts[0], column = parts[1], alias = parts[2];
    3489. -
    3490. 0 if (alias) {
    3491. -
    3492. 0 ret = [cTable ? new QualifiedIdentifier(cTable, column) : column, alias];
    3493. -
    3494. } else {
    3495. -
    3496. 0 ret = [c, null];
    3497. -
    3498. }
    3499. -
    3500. } else {
    3501. -
    3502. 614 ret = [c, null];
    3503. -
    3504. }
    3505. -
    3506. 619 return ret;
    3507. -
    3508. },
    3509. -
    3510. -
    3511. -
    3512. /**
    3513. -
    3514. * @private
    3515. -
    3516. * Inverts the given order by breaking it into a list of column references
    3517. -
    3518. * and inverting them.
    3519. +
    3520. * @return {patio.Dataset} a cloned dataset with the IS NOT TRUE expression added to the WHERE clause.
    3521. *
    3522. -
    3523. * ds.invertOrder([sql.identifier("id").desc()]]) //=> [id]
    3524. -
    3525. * ds.invertOrder("category", sql.identifier("price").desc()]) #=> [category.desc(), price]
    3526. */
    3527. -
    3528. -
    3529. _invertOrder: function (order) {
    3530. -
    3531. 46 var ret = order;
    3532. -
    3533. 46 if (order) {
    3534. -
    3535. 45 ret = order.map(function (o) {
    3536. -
    3537. 57 if (isInstanceOf(o, OrderedExpression)) {
    3538. -
    3539. 17 return o.invert();
    3540. -
    3541. } else {
    3542. -
    3543. 40 return new OrderedExpression(isString(o) ? new Identifier(o) : o);
    3544. -
    3545. }
    3546. -
    3547. }, this);
    3548. -
    3549. }
    3550. -
    3551. 46 return ret;
    3552. +
    3553. isNotTrue: function (arr) {
    3554. +
    3555. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);
    3556. +
    3557. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    3558. },
    3559. /**
    3560. -
    3561. * Creates a boolean expression that each key is compared to its value using the provided operator.
    3562. +
    3563. * Returnes a cloned dataset with the IS TRUE boolean expression added to the WHERE
    3564. +
    3565. * clause.
    3566. *
    3567. * @example
    3568. *
    3569. -
    3570. * ds.__createBoolExpression("gt", {x : 1, y:2, z : 5}) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))
    3571. -
    3572. * ds.__createBoolExpression("gt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))
    3573. -
    3574. * ds.__createBoolExpression("lt", {x : 1, y:2, z : 5}) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))
    3575. -
    3576. * ds.__createBoolExpression("lt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))
    3577. +
    3578. * DB.from("test").isTrue("boolFlag");
    3579. +
    3580. * => SELECT * FROM test WHERE (boolFlag IS TRUE);
    3581. +
    3582. * DB.from("test").isTrue("boolFlag", "otherFlag");
    3583. +
    3584. * => SELECT * FROM test WHERE (boolFlag IS TRUE AND otherFlag IS TRUE);
    3585. *
    3586. -
    3587. * @param {String} op valid boolean expression operator to capare each K,V pair with
    3588. -
    3589. * @param {Object| Array } obj object or two dimensional array containing key value pairs
    3590. +
    3591. * @param {String...} arr variable number of arguments to create an IS TRUE expression for.
    3592. +
    3593. *
    3594. +
    3595. * @return {patio.Dataset} a cloned dataset with the IS TRUE expression added to the WHERE clause.
    3596. *
    3597. -
    3598. * @return {patio.sql.BooleanExpression} boolean expression joined by a AND of each key value pair compared by the op
    3599. */
    3600. -
    3601. __createBoolExpression: function (op, obj) {
    3602. -
    3603. 32 var pairs = [];
    3604. -
    3605. 32 if (Expression.isConditionSpecifier(obj)) {
    3606. -
    3607. 32 if (isHash(obj)) {
    3608. -
    3609. 18 obj = array.toArray(obj);
    3610. -
    3611. }
    3612. -
    3613. 32 obj.forEach(function (pair) {
    3614. -
    3615. 42 pairs.push(new BooleanExpression(op, new Identifier(pair[0]), pair[1]));
    3616. -
    3617. });
    3618. -
    3619. }
    3620. -
    3621. 32 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
    3622. +
    3623. isTrue: function (arr) {
    3624. +
    3625. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);
    3626. +
    3627. 2 return this.filter(this.__createBoolExpression("is", arr));
    3628. },
    3629. /**
    3630. -
    3631. * @private
    3632. -
    3633. *
    3634. -
    3635. * Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
    3636. +
    3637. * Returnes a cloned dataset with the IS NOT FALSE boolean expression added to the WHERE
    3638. +
    3639. * clause.
    3640. *
    3641. * @example
    3642. *
    3643. -
    3644. * ds.__createBetweenExpression({x : [1,2]}) => //=> WHERE ((x >= 1) AND (x <= 10))
    3645. -
    3646. * ds.__createBetweenExpression({x : [1,2]}, true) => //=> WHERE ((x < 1) OR (x > 10))
    3647. +
    3648. * DB.from("test").isNotFalse("boolFlag");
    3649. +
    3650. * => SELECT * FROM test WHERE (boolFlag IS NOT FALSE);
    3651. +
    3652. * DB.from("test").isNotFalse("boolFlag", "otherFlag");
    3653. +
    3654. * => SELECT * FROM test WHERE (boolFlag IS NOT FALSE AND otherFlag IS NOT FALSE);
    3655. +
    3656. * @param {String...} arr variable number of arguments to create an IS NOT FALSE expression for.
    3657. *
    3658. -
    3659. * @param {Object} obj object where the keys are columns and the values are two element arrays.
    3660. -
    3661. * @param {Boolean} [invert] if set to true it inverts the between to make it not between the two values
    3662. +
    3663. * @return {patio.Dataset} a cloned dataset with the IS NOT FALSE expression added to the WHERE clause.
    3664. *
    3665. -
    3666. * @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
    3667. */
    3668. -
    3669. __createBetweenExpression: function (obj, invert) {
    3670. -
    3671. 4 var pairs = [];
    3672. -
    3673. 4 for (var i in obj) {
    3674. -
    3675. 4 var v = obj[i];
    3676. -
    3677. 4 if (isArray(v) && v.length) {
    3678. -
    3679. 2 var ident = this.stringToIdentifier(i);
    3680. -
    3681. 2 pairs.push(new BooleanExpression("AND", new BooleanExpression("gte", ident, v[0]), new BooleanExpression("lte", ident, v[1])));
    3682. -
    3683. } else {
    3684. -
    3685. 2 throw new QueryError("Between requires an array for the value");
    3686. -
    3687. }
    3688. -
    3689. }
    3690. -
    3691. 2 var ret = pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs))
    3692. -
    3693. 2 return invert ? BooleanExpression.invert(ret) : ret;
    3694. +
    3695. isNotFalse: function (arr) {
    3696. +
    3697. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);
    3698. +
    3699. 2 return this.filter(this.__createBoolExpression("isNot", arr));
    3700. },
    3701. -
    3702. /**
    3703. -
    3704. * @private
    3705. -
    3706. * Converts an array to a two dimensional array where the first element
    3707. -
    3708. * is the identifier and the second argument is the value that the value should equal
    3709. -
    3710. * used by is{Null|NotNull|True|NotTrue|False|notFalse} functions to join all the values passed in.
    3711. -
    3712. * @param {String[]|patio.sql.Identifier} arr array of elements to make a condition specifier out of
    3713. -
    3714. * @param defaultOp the value to assign a value if one is not provided.
    3715. +
    3716. * Returnes a cloned dataset with the IS FALSE boolean expression added to the WHERE
    3717. +
    3718. * clause.
    3719. +
    3720. *
    3721. +
    3722. * @example
    3723. +
    3724. *
    3725. +
    3726. * DB.from("test").isFalse("boolFlag");
    3727. +
    3728. * => SELECT * FROM test WHERE (boolFlag IS FALSE);
    3729. +
    3730. * DB.from("test").isFalse("boolFlag", "otherFlag");
    3731. +
    3732. * => SELECT * FROM test WHERE (boolFlag IS FALSE AND otherFlag IS FALSE);
    3733. +
    3734. * @param {String...} arr variable number of arguments to create an IS FALSE expression for.
    3735. +
    3736. *
    3737. +
    3738. * @return {patio.Dataset} a cloned dataset with the IS FALSE expression added to the WHERE clause.
    3739. *
    3740. -
    3741. * @return { [[]] } an array of two element arrays.
    3742. */
    3743. -
    3744. __arrayToConditionSpecifier: function (arr, defaultOp) {
    3745. -
    3746. 22 var ret = [];
    3747. -
    3748. 22 arr.forEach(function (a) {
    3749. -
    3750. 28 if (isString(a)) {
    3751. -
    3752. 18 a = this.stringToIdentifier(a);
    3753. -
    3754. }
    3755. -
    3756. 28 if (isInstanceOf(a, Identifier)) {
    3757. -
    3758. 18 ret.push([a, defaultOp]);
    3759. -
    3760. 10 } else if (isHash(a)) {
    3761. -
    3762. 4 ret = ret.concat(array.toArray(a));
    3763. -
    3764. } else {
    3765. -
    3766. 6 throw new QueryError("Invalid condition specifier " + a);
    3767. -
    3768. }
    3769. -
    3770. }, this);
    3771. -
    3772. 16 return ret;
    3773. +
    3774. isFalse: function (arr) {
    3775. +
    3776. 3 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);
    3777. +
    3778. 2 return this.filter(this.__createBoolExpression("is", arr));
    3779. },
    3780. /**
    3781. -
    3782. * @private
    3783. *
    3784. -
    3785. * SQL expression object based on the expr type. See {@link patio.Dataset#filter}
    3786. +
    3787. * Returns a cloned dataset with a greater than or equal to expression added to the WHERE
    3788. +
    3789. * clause.
    3790. +
    3791. *
    3792. +
    3793. * @example
    3794. +
    3795. * DB.from("test").gte({x : 1});
    3796. +
    3797. * //=> SELECT * FROM test WHERE (x >= 1)
    3798. +
    3799. * DB.from("test").gte({x : 1, y : 10});
    3800. +
    3801. * //=> SELECT * FROM test WHERE ((x >= 1) AND (y >= 10))
    3802. +
    3803. *
    3804. +
    3805. * @param {Object} obj object used to create the greater than or equal to expression.
    3806. +
    3807. *
    3808. +
    3809. * @return {patio.Dataset} a cloned dataset with the greater than or equal to expression added to the WHERE clause.
    3810. */
    3811. -
    3812. _filterExpr: function (expr, cb, joinCond) {
    3813. -
    3814. 4283 expr = (isUndefined(expr) || isNull(expr) || (isArray(expr) && !expr.length)) ? null : expr;
    3815. -
    3816. 4283 if (expr && cb) {
    3817. -
    3818. 1 return new BooleanExpression(joinCond || "AND", this._filterExpr(expr, null, joinCond), this._filterExpr(cb, null, joinCond))
    3819. -
    3820. 4282 } else if (cb) {
    3821. -
    3822. 58 expr = cb
    3823. -
    3824. }
    3825. -
    3826. 4282 if (isInstanceOf(expr, Expression)) {
    3827. -
    3828. 459 if (isInstanceOf(expr, NumericExpression, StringExpression)) {
    3829. -
    3830. 2 throw new QueryError("Invalid SQL Expression type : " + expr);
    3831. -
    3832. }
    3833. -
    3834. 457 return expr;
    3835. -
    3836. 3823 } else if (isArray(expr)) {
    3837. -
    3838. 740 if (expr.length) {
    3839. -
    3840. 740 var first = expr[0];
    3841. -
    3842. 740 if (isString(first)) {
    3843. -
    3844. 25 return new PlaceHolderLiteralString(first, expr.slice(1), true);
    3845. -
    3846. 715 } else if (Expression.isConditionSpecifier(expr)) {
    3847. -
    3848. 708 return BooleanExpression.fromValuePairs(expr, joinCond)
    3849. -
    3850. } else {
    3851. -
    3852. 7 return BooleanExpression.fromArgs([joinCond || "AND"].concat(expr.map(function (e) {
    3853. -
    3854. 15 return this._filterExpr(e, null, joinCond);
    3855. -
    3856. }, this)));
    3857. -
    3858. }
    3859. -
    3860. }
    3861. -
    3862. 3083 } else if (isFunction(expr)) {
    3863. -
    3864. 59 return this._filterExpr(expr.call(sql, sql), null, joinCond);
    3865. -
    3866. 3024 } else if (isBoolean(expr)) {
    3867. -
    3868. 7 return new BooleanExpression("NOOP", expr);
    3869. -
    3870. 3017 } else if (isString(expr)) {
    3871. -
    3872. 1 return this.stringToIdentifier(expr);
    3873. -
    3874. 3016 } else if (isInstanceOf(expr, LiteralString)) {
    3875. -
    3876. 16 return new LiteralString("(" + expr + ")");
    3877. -
    3878. 3000 } else if (isHash(expr)) {
    3879. -
    3880. 2999 return BooleanExpression.fromValuePairs(expr, joinCond);
    3881. -
    3882. } else {
    3883. -
    3884. 1 throw new QueryError("Invalid filter argument");
    3885. -
    3886. }
    3887. +
    3888. gte: function (arr) {
    3889. +
    3890. 2 arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "gte");
    3891. +
    3892. 2 return this.filter(this.__createBoolExpression("gte", arr));
    3893. },
    3894. -
    3895. -
    3896. /**@ignore*/
    3897. -
    3898. getters: {
    3899. -
    3900. -
    3901. /**
    3902. -
    3903. * @ignore
    3904. -
    3905. * Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
    3906. -
    3907. */
    3908. -
    3909. isSimpleSelectAll: function () {
    3910. -
    3911. 33 var o = {}, opts = this.__opts, count = 0;
    3912. -
    3913. 33 for (var i in opts) {
    3914. -
    3915. 70 if (opts[i] != null && this._static.NON_SQL_OPTIONS.indexOf(i) == -1) {
    3916. -
    3917. 37 o[i] = opts[i];
    3918. -
    3919. 37 count++;
    3920. -
    3921. }
    3922. -
    3923. }
    3924. -
    3925. 33 var f = o.from
    3926. -
    3927. 33 return count == 1 && f.length == 1 && (isString(f[0]) || isInstanceOf(f[0], AliasedExpression, Identifier));
    3928. -
    3929. }
    3930. -
    3931. }
    3932. -
    3933. },
    3934. -
    3935. -
    3936. static: {
    3937. -
    3938. /**@lends patio.Dataset*/
    3939. -
    3940. -
    3941. /**
    3942. -
    3943. * These strings have {name}Join methods created (e.g. {@link patio.Dataset#innerJoin}) that
    3944. -
    3945. * call {@link patio.Dataset#joinTable} with the string, passing along the arguments and
    3946. -
    3947. * block from the method call.
    3948. -
    3949. **/
    3950. -
    3951. CONDITIONED_JOIN_TYPES: ["inner", "fullOuter", "rightOuter", "leftOuter", "full", "right", "left"],
    3952. -
    3953. /**
    3954. *
    3955. -
    3956. * These strings have {name}Join methods created (e.g. naturalJoin) that
    3957. -
    3958. * call {@link patio.Dataset#joinTable}. They only accept a single table
    3959. -
    3960. * argument which is passed to {@link patio.Dataset#joinTable}, and they throw an error
    3961. -
    3962. * if called with a block.
    3963. -
    3964. **/
    3965. -
    3966. UNCONDITIONED_JOIN_TYPES: ["natural", "naturalLeft", "naturalRight", "naturalFull", "cross"],
    3967. -
    3968. -
    3969. /**
    3970. -
    3971. * The dataset options that require the removal of cached columns
    3972. -
    3973. * if changed.
    3974. -
    3975. */
    3976. -
    3977. COLUMN_CHANGE_OPTS: ["select", "sql", "from", "join"],
    3978. -
    3979. -
    3980. /**
    3981. -
    3982. * Which options don't affect the SQL generation. Used by {@link patio.Dataset#simpleSelectAll}
    3983. -
    3984. * to determine if this is a simple SELECT * FROM table.
    3985. -
    3986. */
    3987. -
    3988. NON_SQL_OPTIONS: ["server", "defaults", "overrides", "graph", "eagerGraph", "graphAliases"],
    3989. -
    3990. -
    3991. -
    3992. /**
    3993. -
    3994. * All methods that return modified datasets with a joined table added.
    3995. +
    3996. * Returns a cloned dataset with a less than or equal to expression added to the WHERE
    3997. +
    3998. * clause.
    3999. +
    4000. *
    4001. +
    4002. * @example
    4003. +
    4004. * DB.from("test").gte({x : 1});
    4005. +
    4006. * //=> SELECT * FROM test WHERE (x <= 1)
    4007. +
    4008. * DB.from("test").gte({x : 1, y : 10});
    4009. +
    4010. * //=> SELECT * FROM test WHERE ((x <= 1) AND (y <= 10))
    4011. +
    4012. *
    4013. +
    4014. * @param {Object} obj object used to create the less than or equal to expression.
    4015. +
    4016. *
    4017. +
    4018. * @return {patio.Dataset} a cloned dataset with the less than or equal to expression added to the WHERE clause.
    4019. */
    4020. -
    4021. JOIN_METHODS: ["join", "joinTable"],
    4022. +
    4023. lte: function (obj) {
    4024. +
    4025. 2 var arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "lte");
    4026. +
    4027. 2 return this.filter(this.__createBoolExpression("lte", obj));
    4028. +
    4029. },
    4030. /**
    4031. -
    4032. * Methods that return modified datasets
    4033. -
    4034. */
    4035. -
    4036. QUERY_METHODS: ['addGraphAliases', "and", "distinct", "except", "exclude", "filter", "find", "is", "isNot",
    4037. -
    4038. "eq", "neq", "lt", "lte", "gt", "gte", "forUpdate", "from", "fromSelf", "graph", "grep", "group",
    4039. -
    4040. "groupAndCount", "groupBy", "having", "intersect", "invert", "limit", "lockStyle", "naked", "or", "order",
    4041. -
    4042. "orderAppend", "orderBy", "orderMore", "orderPrepend", "qualify", "reverse",
    4043. -
    4044. "reverseOrder", "select", "selectAll", "selectAppend", "selectMore", "setDefaults",
    4045. -
    4046. "setGraphAliases", "setOverrides", "unfiltered", "ungraphed", "ungrouped", "union", "unlimited",
    4047. -
    4048. "unordered", "where", "with", "withRecursive", "withSql"],
    4049. -
    4050. -
    4051. init: function () {
    4052. -
    4053. 35 this._super(arguments);
    4054. -
    4055. //initialize our join methods array
    4056. -
    4057. 35 var joinMethods = this.JOIN_METHODS;
    4058. -
    4059. 35 var queryMethods = this.QUERY_METHODS;
    4060. -
    4061. 35 this.UNCONDITIONED_JOIN_TYPES.forEach(function (joinType) {
    4062. -
    4063. 175 var m = joinType + "Join";
    4064. -
    4065. 175 joinMethods.push(m);
    4066. -
    4067. 175 queryMethods.push(m);
    4068. -
    4069. });
    4070. -
    4071. 35 this.CONDITIONED_JOIN_TYPES.forEach(function (joinType) {
    4072. -
    4073. 245 var m = joinType + "Join";
    4074. -
    4075. 245 joinMethods.push(m);
    4076. -
    4077. 245 queryMethods.push(m);
    4078. -
    4079. });
    4080. -
    4081. 35 this.QUERY_METHODS = queryMethods.concat(joinMethods);
    4082. -
    4083. }
    4084. -
    4085. }
    4086. -
    4087. }).
    4088. -
    4089. as(module);
    -
    - -
    - - - - - -
    -
    plugins/validation.js
    -
    -
    - Coverage97.66 - SLOC535 - LOC128 - Missed3 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. array = comb.array,
    4. -
    5. compact = array.compact,
    6. -
    7. flatten = array.flatten,
    8. -
    9. toArray = array.toArray,
    10. -
    11. net = require("net"),
    12. -
    13. isIP = net.isIP,
    14. -
    15. isIPv4 = net.isIPv4,
    16. -
    17. isIPv6 = net.isIPv6,
    18. -
    19. validator = require("validator"),
    20. -
    21. validatorCheck = validator.check,
    22. -
    23. dateCmp = comb.date.compare,
    24. -
    25. isArray = comb.isArray,
    26. -
    27. combDeepEqual = comb.deepEqual,
    28. -
    29. combIsBoolean = comb.isBoolean,
    30. -
    31. isString = comb.isString,
    32. -
    33. combIsDefined = comb.isDefined,
    34. -
    35. combIsNull = comb.isNull,
    36. -
    37. ModelError = require("../errors.js").ModelError,
    38. -
    39. isFunction = comb.isFunction,
    40. -
    41. format = comb.string.format,
    42. -
    43. Promise = comb.Promise,
    44. -
    45. serial = comb.serial,
    46. -
    47. when = comb.when,
    48. -
    49. merge = comb.merge,
    50. -
    51. define = comb.define;
    52. -
    53. -
    54. 1var Validator = define(null, {
    55. -
    56. instance:{
    57. +
    58. * Returns a cloned dataset with a between clause added
    59. +
    60. * to the where clause.
    61. +
    62. *
    63. +
    64. * @example
    65. +
    66. * ds.notBetween({x:[1, 2]}).sql;
    67. +
    68. * //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))
    69. +
    70. *
    71. +
    72. * ds.find({x:{notBetween:[1, 2]}}).sql;
    73. +
    74. * //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))
    75. +
    76. * @param {Object} obj object where the key is the column and the value is an array where the first element
    77. +
    78. * is the item to be greater than or equal to than and the second item is less than or equal to than.
    79. +
    80. *
    81. +
    82. * @return {patio.Dataset} a cloned dataset with a between clause added
    83. +
    84. * to the where clause.
    85. +
    86. */
    87. +
    88. between: function (obj) {
    89. +
    90. 2 return this.filter(this.__createBetweenExpression(obj));
    91. +
    92. },
    93. -
    94. constructor:function validator(col) {
    95. -
    96. 44 this.col = col;
    97. -
    98. 44 this.__actions = [];
    99. +
    100. /**
    101. +
    102. * Returns a cloned dataset with a not between clause added
    103. +
    104. * to the where clause.
    105. +
    106. *
    107. +
    108. * @example
    109. +
    110. * ds.notBetween({x:[1, 2]}).sql;
    111. +
    112. * //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))
    113. +
    114. *
    115. +
    116. * ds.find({x:{notBetween:[1, 2]}}).sql;
    117. +
    118. * //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))
    119. +
    120. * @param {Object} obj object where the key is the column and the value is an array where the first element
    121. +
    122. * is the item to be less than and the second item is greater than.
    123. +
    124. *
    125. +
    126. * @return {patio.Dataset} a cloned dataset with a not between clause added
    127. +
    128. * to the where clause.
    129. +
    130. */
    131. +
    132. notBetween: function (obj) {
    133. +
    134. 2 return this.filter(this.__createBetweenExpression(obj, true));
    135. },
    136. -
    137. __addAction:function __addAction(action, opts) {
    138. -
    139. 46 this.__actions.push({
    140. -
    141. action:action,
    142. -
    143. opts:merge({onlyDefined:true, onlyNotNull:false}, opts)
    144. -
    145. });
    146. -
    147. 46 return this;
    148. +
    149. /**
    150. +
    151. * Returns a cloned dataset with the given lock style. If style is a
    152. +
    153. * string, it will be used directly.Currently "update" is respected
    154. +
    155. * by most databases, and "share" is supported by some.
    156. +
    157. *
    158. +
    159. * @example
    160. +
    161. * DB.from("items").lockStyle('FOR SHARE') # SELECT * FROM items FOR SHARE
    162. +
    163. *
    164. +
    165. * @param {String} style the lock style to use.
    166. +
    167. *
    168. +
    169. * @return {patio.Dataset} a cloned datase with the given lock style.
    170. +
    171. **/
    172. +
    173. lockStyle: function (style) {
    174. +
    175. 4 return this.mergeOptions({lock: style});
    176. },
    177. -
    178. isAfter:function (date, opts) {
    179. -
    180. 1 opts = opts || {};
    181. -
    182. 1 var cmpDate = combIsBoolean(opts.date) ? opts.date : true;
    183. -
    184. 1 return this.__addAction(function (col) {
    185. -
    186. 3 return dateCmp(col, date, cmpDate ? "date" : "datetime") > 0;
    187. -
    188. }, merge({message:"{col} must be after " + date + " got {val}."}, opts));
    189. +
    190. /**
    191. +
    192. * Returns a copy of the dataset with the order changed. If the dataset has an
    193. +
    194. * existing order, it is ignored and overwritten with this order. If null is given
    195. +
    196. * the returned dataset has no order. This can accept multiple arguments
    197. +
    198. * of varying kinds, such as SQL functions. This also takes a function similar
    199. +
    200. * to {@link patio.Dataset#filter}
    201. +
    202. *
    203. +
    204. * @example
    205. +
    206. *
    207. +
    208. * DB.from("items").order("name")
    209. +
    210. * //=> SELECT * FROM items ORDER BY name
    211. +
    212. *
    213. +
    214. * DB.from("items").order("a", "b")
    215. +
    216. * //=> SELECT * FROM items ORDER BY a, b
    217. +
    218. *
    219. +
    220. * DB.from("items").order(sql.literal('a + b'))
    221. +
    222. * //=> SELECT * FROM items ORDER BY a + b
    223. +
    224. *
    225. +
    226. * DB.from("items").order(sql.identifier("a").plus("b"))
    227. +
    228. * //=> SELECT * FROM items ORDER BY (a + b)
    229. +
    230. *
    231. +
    232. * DB.from("items").order(sql.identifier("name").desc())
    233. +
    234. * //=> SELECT * FROM items ORDER BY name DESC
    235. +
    236. *
    237. +
    238. * DB.from("items").order(sql.identifier("name").asc({nulls : "last"))
    239. +
    240. * //=> SELECT * FROM items ORDER BY name ASC NULLS LAST
    241. +
    242. *
    243. +
    244. * DB.from("items").order(function(){
    245. +
    246. * return this.sum("name").desc();
    247. +
    248. * }); //=> SELECT * FROM items ORDER BY sum(name) DESC
    249. +
    250. *
    251. +
    252. * DB.from("items").order(null)
    253. +
    254. * //=>SELECT * FROM items
    255. +
    256. * @param arg variable number of arguments similar to {@link patio.Dataset#filter}
    257. +
    258. *
    259. +
    260. * @return {patio.Dataset} a cloned dataset with the order changed.
    261. +
    262. * */
    263. +
    264. order: function (args) {
    265. +
    266. 381 args = argsToArray(arguments);
    267. +
    268. 381 var order = [];
    269. +
    270. 381 args = compact(args).length ? args : null;
    271. +
    272. 381 if (args) {
    273. +
    274. 263 args.forEach(function (a) {
    275. +
    276. 326 if (isString(a)) {
    277. +
    278. 204 order.push(this.stringToIdentifier(a));
    279. +
    280. 122 } else if (isFunction(a)) {
    281. +
    282. 16 var res = a.apply(sql, [sql]);
    283. +
    284. 16 order = order.concat(isArray(res) ? res : [res]);
    285. +
    286. } else {
    287. +
    288. 106 order.push(a);
    289. +
    290. }
    291. +
    292. }, this);
    293. +
    294. } else {
    295. +
    296. 118 order = null;
    297. +
    298. }
    299. +
    300. 381 return this.mergeOptions({order: order});
    301. },
    302. -
    303. isBefore:function (date, opts) {
    304. -
    305. 1 opts = opts || {};
    306. -
    307. 1 var cmpDate = combIsBoolean(opts.date) ? opts.date : true;
    308. -
    309. 1 return this.__addAction(function (col) {
    310. -
    311. 3 return dateCmp(col, date, cmpDate ? "date" : "datetime") === -1;
    312. -
    313. }, merge({message:"{col} must be before " + date + " got {val}."}, opts));
    314. +
    315. /**
    316. +
    317. * Alias of {@link patio.Dataset#orderMore};
    318. +
    319. */
    320. +
    321. orderAppend: function () {
    322. +
    323. 4 return this.orderMore.apply(this, arguments);
    324. },
    325. -
    326. isDefined:function isDefined(opts) {
    327. -
    328. 1 return this.__addAction(function (col) {
    329. -
    330. 3 return combIsDefined(col);
    331. -
    332. }, merge({message:"{col} must be defined.", onlyDefined:false, onlyNotNull:false}, opts));
    333. +
    334. /**
    335. +
    336. * @see patio.Dataset#order
    337. +
    338. */
    339. +
    340. orderBy: function () {
    341. +
    342. 6 return this.order.apply(this, arguments);
    343. },
    344. -
    345. isNotDefined:function isNotDefined(opts) {
    346. -
    347. 1 return this.__addAction(function (col) {
    348. -
    349. 3 return !combIsDefined(col);
    350. -
    351. }, merge({message:"{col} cannot be defined.", onlyDefined:false, onlyNotNull:false}, opts));
    352. +
    353. /**
    354. +
    355. * Returns a copy of the dataset with the order columns added
    356. +
    357. * to the end of the existing order. For more detail
    358. +
    359. * @see patio.Dataset#order
    360. +
    361. *
    362. +
    363. * @example
    364. +
    365. *
    366. +
    367. * DB.from("items").order("a").order("b");
    368. +
    369. * //=> SELECT * FROM items ORDER BY b
    370. +
    371. *
    372. +
    373. * DB.from("items").order("a").orderMore("b");
    374. +
    375. * //=>SELECT * FROM items ORDER BY a, b
    376. +
    377. */
    378. +
    379. orderMore: function () {
    380. +
    381. 11 var args = argsToArray(arguments);
    382. +
    383. 11 if (this.__opts.order) {
    384. +
    385. 9 args = this.__opts.order.concat(args);
    386. +
    387. }
    388. +
    389. 11 return this.order.apply(this, args);
    390. },
    391. -
    392. isNotNull:function isNotNull(opts) {
    393. -
    394. 3 return this.__addAction(function (col) {
    395. -
    396. 21 return combIsDefined(col) && !combIsNull(col);
    397. -
    398. }, merge({message:"{col} cannot be null.", onlyDefined:false, onlyNotNull:false}, opts));
    399. +
    400. /**
    401. +
    402. * Returns a copy of the dataset with the order columns added
    403. +
    404. * to the beginning of the existing order. For more detail
    405. +
    406. * @see patio.Dataset#order
    407. +
    408. *
    409. +
    410. * @example
    411. +
    412. * DB.from("items").order("a").order("b");
    413. +
    414. * //=> SELECT * FROM items ORDER BY b
    415. +
    416. *
    417. +
    418. * DB.from("items").order("a").orderPrepend("b");
    419. +
    420. * //=>SELECT * FROM items ORDER BY b, a
    421. +
    422. *
    423. +
    424. *
    425. +
    426. **/
    427. +
    428. orderPrepend: function () {
    429. +
    430. 4 var ds = this.order.apply(this, arguments);
    431. +
    432. 4 return this.__opts.order ? ds.orderMore.apply(ds, this.__opts.order) : ds;
    433. },
    434. -
    435. isNull:function isNull(opts) {
    436. -
    437. 1 return this.__addAction(function (col) {
    438. -
    439. 3 return !combIsDefined(col) || combIsNull(col);
    440. -
    441. }, merge({message:"{col} must be null got {val}.", onlyDefined:false, onlyNotNull:false}, opts));
    442. +
    443. /**
    444. +
    445. * Qualify to the given table, or {@link patio.Dataset#firstSourceAlias} if not table is given.
    446. +
    447. *
    448. +
    449. * @example
    450. +
    451. * DB.from("items").filter({id : 1}).qualify();
    452. +
    453. * //=> SELECT items.* FROM items WHERE (items.id = 1)
    454. +
    455. *
    456. +
    457. * DB.from("items").filter({id : 1}).qualify("i");
    458. +
    459. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    460. +
    461. *
    462. +
    463. * @param {String|patio.sql.Identifier} [table={@link patio.Dataset#firstSourceAlias}] the table name to qualify to.
    464. +
    465. *
    466. +
    467. * @return {patio.Dataset} a cloned dataset qualified to the table or {@link patio.Dataset#firstSourceAlias}
    468. +
    469. **/
    470. +
    471. qualify: function (table) {
    472. +
    473. 19 table = table || this.firstSourceAlias;
    474. +
    475. 19 return this.qualifyTo(table);
    476. },
    477. -
    478. isEq:function eq(val, opts) {
    479. -
    480. 4 return this.__addAction(function (col) {
    481. -
    482. 15 return combDeepEqual(col, val);
    483. -
    484. }, merge({message:"{col} must === " + val + " got {val}."}, opts));
    485. +
    486. /**
    487. +
    488. * Qualify the dataset to its current first source(first from clause). This is useful
    489. +
    490. * if you have unqualified identifiers in the query that all refer to
    491. +
    492. * the first source, and you want to join to another table which
    493. +
    494. * has columns with the same name as columns in the current dataset.
    495. +
    496. * See {@link patio.Dataset#qualifyTo}
    497. +
    498. *
    499. +
    500. * @example
    501. +
    502. *
    503. +
    504. * DB.from("items").filter({id : 1}).qualifyToFirstSource();
    505. +
    506. * //=> SELECT items.* FROM items WHERE (items.id = 1)
    507. +
    508. *
    509. +
    510. * @return {patio.Dataset} a cloned dataset that is qualified with the first source.
    511. +
    512. * */
    513. +
    514. qualifyToFirstSource: function () {
    515. +
    516. 18 return this.qualifyTo(this.firstSourceAlias);
    517. },
    518. -
    519. isNeq:function neq(val, opts) {
    520. -
    521. 2 return this.__addAction(function (col) {
    522. -
    523. 8 return !combDeepEqual(col, val);
    524. -
    525. }, merge({message:"{col} must !== " + val + "."}, opts));
    526. +
    527. /**
    528. +
    529. * Return a copy of the dataset with unqualified identifiers in the
    530. +
    531. * SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the
    532. +
    533. * given table. If no columns are currently selected, select all
    534. +
    535. * columns of the given table.
    536. +
    537. *
    538. +
    539. * @example
    540. +
    541. * DB.from("items").filter({id : 1}).qualifyTo("i");
    542. +
    543. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    544. +
    545. *
    546. +
    547. * @param {String} table the name to qualify identifier to.
    548. +
    549. *
    550. +
    551. * @return {patio.Dataset} a cloned dataset with unqualified identifiers qualified.
    552. +
    553. */
    554. +
    555. qualifyTo: function (table) {
    556. +
    557. 40 var o = this.__opts;
    558. +
    559. 40 if (o.sql) {
    560. +
    561. 2 return this.mergeOptions();
    562. +
    563. }
    564. +
    565. 38 var h = {};
    566. +
    567. 38 array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) {
    568. +
    569. 65 h[k] = this._qualifiedExpression(o[k], table);
    570. +
    571. }, this);
    572. +
    573. 38 if (!o.select || isEmpty(o.select)) {
    574. +
    575. 14 h.select = [new ColumnAll(table)];
    576. +
    577. }
    578. +
    579. 38 return this.mergeOptions(h);
    580. },
    581. -
    582. isLike:function like(val, opts) {
    583. -
    584. 3 return this.__addAction(function (col) {
    585. -
    586. 14 return !!col.match(val);
    587. -
    588. }, merge({message:"{col} must be like " + val + " got {val}."}, opts));
    589. +
    590. /**
    591. +
    592. * Same as {@link patio.Dataset@qualifyTo} except that it forces qualification on methods called
    593. +
    594. * after it has been called.
    595. +
    596. *
    597. +
    598. * @example
    599. +
    600. *
    601. +
    602. * //qualfyTo would generate
    603. +
    604. * DB.from("items").qualifyTo("i").filter({id : 1});
    605. +
    606. * //=> SELECT i.* FROM items WHERE (id = 1)
    607. +
    608. *
    609. +
    610. * //alwaysQualify qualifies filter also.
    611. +
    612. * DB.from("items").alwaysQualify("i").filter({id : 1});
    613. +
    614. * //=> SELECT i.* FROM items WHERE (i.id = 1)
    615. +
    616. *
    617. +
    618. *
    619. +
    620. * @param {String|patio.sql.Identifier} [table=this.firstSourceAlias] the table to qualify to.
    621. +
    622. * @return {patio.Dataset} a cloned dataset that will always qualify.
    623. +
    624. */
    625. +
    626. alwaysQualify: function (table) {
    627. +
    628. 3 return this.mergeOptions({alwaysQualify: table || this.firstSourceAlias});
    629. },
    630. -
    631. isNotLike:function notLike(val, opts) {
    632. -
    633. 2 return this.__addAction(function (col) {
    634. -
    635. 6 return !(!!col.match(val));
    636. -
    637. }, merge({message:"{col} must not be like " + val + "."}, opts));
    638. -
    639. },
    640. -
    641. isLt:function lt(num, opts) {
    642. -
    643. 1 return this.__addAction(function (col) {
    644. -
    645. 3 return col < num;
    646. -
    647. }, merge({message:"{col} must be < " + num + " got {val}."}, opts));
    648. +
    649. /**
    650. +
    651. * Returns a copy of the dataset with the order reversed. If no order is
    652. +
    653. * given, the existing order is inverted.
    654. +
    655. *
    656. +
    657. * @example
    658. +
    659. * DB.from("items").reverse("id");
    660. +
    661. * //=> SELECT * FROM items ORDER BY id DESC
    662. +
    663. *
    664. +
    665. * DB.from("items").order("id").reverse();
    666. +
    667. * //=> SELECT * FROM items ORDER BY id DESC
    668. +
    669. *
    670. +
    671. * DB.from("items").order("id").reverse(sql.identifier("name").asc);
    672. +
    673. * //=> SELECT * FROM items ORDER BY name ASC
    674. +
    675. *
    676. +
    677. * @param {String|patio.sql.Identifier|Function} args variable number of columns add to order before reversing.
    678. +
    679. *
    680. +
    681. * @return {patio.Dataset} a cloned dataset with the order reversed.
    682. +
    683. *
    684. +
    685. **/
    686. +
    687. reverse: function (args) {
    688. +
    689. 46 args = argsToArray(arguments);
    690. +
    691. 46 return this.order.apply(this, this._invertOrder(args.length ? args : this.__opts.order));
    692. },
    693. -
    694. isGt:function gt(num, opts) {
    695. -
    696. 1 return this.__addAction(function (col) {
    697. -
    698. 3 return col > num;
    699. -
    700. }, merge({message:"{col} must be > " + num + " got {val}."}, opts));
    701. +
    702. /**
    703. +
    704. * @see patio.Dataset#reverse
    705. +
    706. */
    707. +
    708. reverseOrder: function () {
    709. +
    710. 16 return this.reverse.apply(this, arguments);
    711. },
    712. -
    713. isLte:function lte(num, opts) {
    714. -
    715. 1 return this.__addAction(function (col) {
    716. -
    717. 3 return col <= num;
    718. -
    719. }, merge({message:"{col} must be <= " + num + " got {val}."}, opts));
    720. -
    721. },
    722. +
    723. /**
    724. +
    725. * Returns a copy of the dataset with the columns selected changed
    726. +
    727. * to the given columns. This also takes a function similar to {@link patio.Dataset#filter}
    728. +
    729. *
    730. +
    731. * @example
    732. +
    733. * DB.from("items").select("a");
    734. +
    735. * //=> SELECT a FROM items
    736. +
    737. *
    738. +
    739. * DB.from("items").select("a", "b");
    740. +
    741. * //=> SELECT a, b FROM items
    742. +
    743. *
    744. +
    745. * DB.from("items").select("a", function(){
    746. +
    747. * return this.sum("b")
    748. +
    749. * }).sql; //=> SELECT a, sum(b) FROM items
    750. +
    751. *
    752. +
    753. * @param {String|patio.sql.Identifier|Function} args variable number of colums to select
    754. +
    755. * @return {patio.Dataset} a cloned dataset with the columns selected changed.
    756. +
    757. */
    758. +
    759. select: function (args) {
    760. +
    761. 663 args = flatten(argsToArray(arguments));
    762. +
    763. 663 var columns = [];
    764. +
    765. 663 args.forEach(function (c) {
    766. +
    767. 1043 if (isFunction(c)) {
    768. +
    769. 23 var res = c.apply(sql, [sql]);
    770. +
    771. 23 columns = columns.concat(isArray(res) ? res : [res]);
    772. +
    773. } else {
    774. +
    775. 1020 columns.push(c);
    776. +
    777. }
    778. +
    779. });
    780. +
    781. 663 var select = [];
    782. +
    783. 663 columns.forEach(function (c) {
    784. +
    785. 1046 if (isHash(c)) {
    786. +
    787. 3 for (var i in c) {
    788. +
    789. 4 select.push(new AliasedExpression(new Identifier(i), c[i]));
    790. +
    791. }
    792. +
    793. 1043 } else if (isString(c)) {
    794. +
    795. 344 select.push(this.stringToIdentifier(c));
    796. +
    797. } else {
    798. +
    799. 699 select.push(c);
    800. +
    801. }
    802. +
    803. }, this);
    804. +
    805. 663 return this.mergeOptions({select: select});
    806. -
    807. isGte:function gte(num, opts) {
    808. -
    809. 1 return this.__addAction(function (col) {
    810. -
    811. 3 return col >= num;
    812. -
    813. }, merge({message:"{col} must be >= " + num + " got {val}."}, opts));
    814. },
    815. -
    816. isIn:function isIn(arr, opts) {
    817. -
    818. 2 if (!isArray(arr)) {
    819. -
    820. 1 throw new Error("isIn requires an array of values");
    821. -
    822. }
    823. -
    824. 1 return this.__addAction(function (col) {
    825. -
    826. 3 return arr.indexOf(col) !== -1;
    827. -
    828. }, merge({message:"{col} must be in " + arr.join(",") + " got {val}."}, opts));
    829. +
    830. /**
    831. +
    832. * Returns a cloned dataset that selects *.
    833. +
    834. *
    835. +
    836. * @return {patio.Dataset} a cloned dataset that selects *.
    837. +
    838. */
    839. +
    840. selectAll: function () {
    841. +
    842. 6 return this.mergeOptions({select: null});
    843. },
    844. -
    845. isNotIn:function notIn(arr, opts) {
    846. -
    847. 2 if (!isArray(arr)) {
    848. -
    849. 1 throw new Error("notIn requires an array of values");
    850. +
    851. /**
    852. +
    853. * Selects the columns if only if there is not already select sources.
    854. +
    855. *
    856. +
    857. * @example
    858. +
    859. *
    860. +
    861. * var ds = DB.from("items"); //SELECT * FROM items
    862. +
    863. *
    864. +
    865. * ds.select("a"); //SELECT a FROM items;
    866. +
    867. * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
    868. +
    869. * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
    870. +
    871. *
    872. +
    873. * @param cols columns to select if there is not already select sources.
    874. +
    875. * @return {patio.Dataset} a cloned dataset with the appropriate select sources.
    876. +
    877. */
    878. +
    879. selectIfNoSource: function (cols) {
    880. +
    881. 0 var ret;
    882. +
    883. 0 if (!this.hasSelectSource) {
    884. +
    885. 0 ret = this.select.apply(this, arguments);
    886. +
    887. } else {
    888. +
    889. 0 ret = this.mergeOptions();
    890. }
    891. -
    892. 1 return this.__addAction(function (col) {
    893. -
    894. 3 return arr.indexOf(col) === -1;
    895. -
    896. }, merge({message:"{col} cannot be in " + arr.join(",") + " got {val}."}, opts));
    897. -
    898. },
    899. -
    900. -
    901. isMacAddress:function isMaxAddress(opts) {
    902. -
    903. 1 return this.__addAction(function (col) {
    904. -
    905. 4 return !!col.match(/^([0-9A-F]{2}[:\-]){5}([0-9A-F]{2})$/);
    906. -
    907. }, merge({message:"{col} must be a valid MAC address got {val}."}, opts));
    908. -
    909. },
    910. -
    911. -
    912. isIPAddress:function isIpAddress(opts) {
    913. -
    914. 1 return this.__addAction(function (col) {
    915. -
    916. 4 return !!isIP(col);
    917. -
    918. }, merge({message:"{col} must be a valid IPv4 or IPv6 address got {val}."}, opts));
    919. -
    920. },
    921. -
    922. -
    923. isIPv4Address:function isIpAddress(opts) {
    924. -
    925. 1 return this.__addAction(function (col) {
    926. -
    927. 3 return isIPv4(col);
    928. -
    929. }, merge({message:"{col} must be a valid IPv4 address got {val}."}, opts));
    930. -
    931. },
    932. -
    933. -
    934. isIPv6Address:function isIpAddress(opts) {
    935. -
    936. 1 return this.__addAction(function (col) {
    937. -
    938. 3 return isIPv6(col);
    939. -
    940. }, merge({message:"{col} must be a valid IPv6 address got {val}."}, opts));
    941. +
    942. 0 return ret;
    943. },
    944. -
    945. isUUID:function isUUID(opts) {
    946. -
    947. 1 return this.__addAction(function (col) {
    948. -
    949. 3 return !!col.match(/^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/);
    950. -
    951. }, merge({message:"{col} must be a valid UUID got {val}"}, opts));
    952. +
    953. /**
    954. +
    955. * Returns a copy of the dataset with the given columns added
    956. +
    957. * to the existing selected columns. If no columns are currently selected,
    958. +
    959. * it will select the columns given in addition to *.
    960. +
    961. *
    962. +
    963. * @example
    964. +
    965. * DB.from("items").select("a").selectAppend("b").sql;
    966. +
    967. * //=> SELECT b FROM items
    968. +
    969. *
    970. +
    971. * DB.from("items").select("a").selectAppend("b", "c", "d").sql
    972. +
    973. * //=> SELECT a, b, c, d FROM items
    974. +
    975. *
    976. +
    977. * DB.from("items").selectAppend("b").sql
    978. +
    979. * //=> SELECT *, b FROM items
    980. +
    981. *
    982. +
    983. * @param [...] cols variable number of columns to add to the select statement
    984. +
    985. *
    986. +
    987. * @return {patio.Dataset} returns a cloned dataset with the new select columns appended.
    988. +
    989. */
    990. +
    991. selectAppend: function (cols) {
    992. +
    993. 7 cols = argsToArray(arguments);
    994. +
    995. 7 var currentSelect = this.__opts.select;
    996. +
    997. 7 if (!currentSelect || !currentSelect.length) {
    998. +
    999. 3 currentSelect = [this._static.WILDCARD];
    1000. +
    1001. }
    1002. +
    1003. 7 return this.select.apply(this, currentSelect.concat(cols));
    1004. },
    1005. -
    1006. isEmail:function isEmail(opts) {
    1007. -
    1008. 1 return this.__addAction(function (col) {
    1009. -
    1010. 3 return validatorCheck(col).isEmail();
    1011. -
    1012. }, merge({message:"{col} must be a valid Email Address got {val}"}, opts));
    1013. +
    1014. /**
    1015. +
    1016. * Returns a copy of the dataset with the given columns added
    1017. +
    1018. * to the existing selected columns. If no columns are currently selected
    1019. +
    1020. * it will just select the columns given.
    1021. +
    1022. *
    1023. +
    1024. * @example
    1025. +
    1026. * DB.from("items").select("a").select("b").sql;
    1027. +
    1028. * //=> SELECT b FROM items
    1029. +
    1030. *
    1031. +
    1032. * DB.from("items").select("a").selectMore("b", "c", "d").sql
    1033. +
    1034. * //=> SELECT a, b, c, d FROM items
    1035. +
    1036. *
    1037. +
    1038. * DB.from("items").selectMore("b").sql
    1039. +
    1040. * //=> SELECT b FROM items
    1041. +
    1042. *
    1043. +
    1044. * @param [...] cols variable number of columns to add to the select statement
    1045. +
    1046. *
    1047. +
    1048. * @return {patio.Dataset} returns a cloned dataset with the new select columns appended.
    1049. +
    1050. */
    1051. +
    1052. selectMore: function (cols) {
    1053. +
    1054. 10 cols = argsToArray(arguments);
    1055. +
    1056. 10 var currentSelect = this.__opts.select;
    1057. +
    1058. 10 return this.select.apply(this, (currentSelect || []).concat(cols));
    1059. },
    1060. -
    1061. isUrl:function isUrl(opts) {
    1062. -
    1063. 1 return this.__addAction(function (col) {
    1064. -
    1065. 3 return validatorCheck(col).isUrl();
    1066. -
    1067. }, merge({message:"{col} must be a valid url got {val}"}, opts));
    1068. +
    1069. /**
    1070. +
    1071. * Set the default values for insert and update statements. The values hash passed
    1072. +
    1073. * to insert or update are merged into this hash, so any values in the hash passed
    1074. +
    1075. * to insert or update will override values passed to this method.
    1076. +
    1077. *
    1078. +
    1079. * @example
    1080. +
    1081. * DB.from("items").setDefaults({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();
    1082. +
    1083. * //=> INSERT INTO items (a, c, b) VALUES ('d', 'c', 'b')
    1084. +
    1085. *
    1086. +
    1087. * @param {Object} hash object with key value pairs to use as override values
    1088. +
    1089. *
    1090. +
    1091. * @return {patio.Dataset} a cloned dataset with the defaults added to the current datasets defaults.
    1092. +
    1093. */
    1094. +
    1095. setDefaults: function (hash) {
    1096. +
    1097. 5 return this.mergeOptions({defaults: merge({}, this.__opts.defaults || {}, hash)});
    1098. },
    1099. -
    1100. isAlpha:function isAlpha(opts) {
    1101. -
    1102. 2 return this.__addAction(function (col) {
    1103. -
    1104. 11 return validatorCheck(col).isAlpha();
    1105. -
    1106. }, merge({message:"{col} must be a only letters got {val}"}, opts));
    1107. +
    1108. /**
    1109. +
    1110. * Set values that override hash arguments given to insert and update statements.
    1111. +
    1112. * This hash is merged into the hash provided to insert or update, so values
    1113. +
    1114. * will override any values given in the insert/update hashes.
    1115. +
    1116. *
    1117. +
    1118. * @example
    1119. +
    1120. * DB.from("items").setOverrides({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();
    1121. +
    1122. * //=> INSERT INTO items (a, c, b) VALUES ('a', 'c', 'b')
    1123. +
    1124. *
    1125. +
    1126. * @param {Object} hash object with key value pairs to use as override values
    1127. +
    1128. *
    1129. +
    1130. * @return {patio.Dataset} a cloned dataset with the overrides added to the current datasets overrides.
    1131. +
    1132. */
    1133. +
    1134. setOverrides: function (hash) {
    1135. +
    1136. 5 return this.mergeOptions({overrides: merge({}, this.__opts.overrides || {}, hash)});
    1137. },
    1138. -
    1139. isAlphaNumeric:function isAlphaNumeric(opts) {
    1140. -
    1141. 1 return this.__addAction(function (col) {
    1142. -
    1143. 3 return validatorCheck(col).isAlphanumeric();
    1144. -
    1145. }, merge({message:"{col} must be a alphanumeric got {val}"}, opts));
    1146. +
    1147. /**
    1148. +
    1149. * Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
    1150. +
    1151. * @example
    1152. +
    1153. * DB.from("items").group("a").having({a : 1}).where("b").unfiltered().sql;
    1154. +
    1155. * //=> SELECT * FROM items GROUP BY a
    1156. +
    1157. *
    1158. +
    1159. * @return {patio.Dataset} a cloned dataset with no HAVING or WHERE clause.
    1160. +
    1161. */
    1162. +
    1163. unfiltered: function () {
    1164. +
    1165. 14 return this.mergeOptions({where: null, having: null});
    1166. },
    1167. -
    1168. hasLength:function hasLength(min, max, opts) {
    1169. -
    1170. 2 return this.__addAction(function (col) {
    1171. -
    1172. 6 return validatorCheck(col).len(min, max);
    1173. -
    1174. }, merge({message:"{col} must have a length between " + min + (max ? " and " + max : "") + "."}, opts));
    1175. +
    1176. /**
    1177. +
    1178. * Returns a copy of the dataset with no GROUP or HAVING clause.
    1179. +
    1180. *
    1181. +
    1182. * @example
    1183. +
    1184. * DB.from("t").group("a").having({a : 1}).where("b").ungrouped().sql;
    1185. +
    1186. * //=> SELECT * FROM t WHERE b
    1187. +
    1188. *
    1189. +
    1190. * @return {patio.Dataset} a cloned dataset with no GROUP or HAVING clause.
    1191. +
    1192. */
    1193. +
    1194. ungrouped: function () {
    1195. +
    1196. 1 return this.mergeOptions({group: null, having: null});
    1197. },
    1198. -
    1199. isLowercase:function isLowercase(opts) {
    1200. -
    1201. 1 return this.__addAction(function (col) {
    1202. -
    1203. 3 return validatorCheck(col).isLowercase();
    1204. -
    1205. }, merge({message:"{col} must be lowercase got {val}."}, opts));
    1206. +
    1207. /**
    1208. +
    1209. * Adds a UNION clause using a second dataset object.
    1210. +
    1211. * A UNION compound dataset returns all rows in either the current dataset
    1212. +
    1213. * or the given dataset.
    1214. +
    1215. * Options:
    1216. +
    1217. * :alias :: Use the given value as the from_self alias
    1218. +
    1219. * :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
    1220. +
    1221. * :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
    1222. +
    1223. *
    1224. +
    1225. * @example
    1226. +
    1227. * DB.from("items").union(DB.from("otherItems")).sql;
    1228. +
    1229. * //=> SELECT * FROM items UNION SELECT * FROM other_items
    1230. +
    1231. *
    1232. +
    1233. * DB.from("items").union(DB.from("otherItems"), {all : true, fromSelf : false}).sql;
    1234. +
    1235. * //=> SELECT * FROM items UNION ALL SELECT * FROM other_items
    1236. +
    1237. *
    1238. +
    1239. * DB.from("items").union(DB.from("otherItems"), {alias : "i"})
    1240. +
    1241. * //=> SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS i
    1242. +
    1243. *
    1244. +
    1245. * @param {patio.Dataset} dataset dataset to union with
    1246. +
    1247. * @param {Object} opts addional options
    1248. +
    1249. * @param {String|patio.sql.Identifier} [opts.alias] Alias to use as the fromSelf alias.
    1250. +
    1251. * @param {Boolean} [opt.all=false] Set to true to use UNION ALL instead of UNION so duplicate rows can occur
    1252. +
    1253. * @param {Boolean} [opts.fromSelf=true] Set to false to not wrap the returned dataset in a fromSelf.
    1254. +
    1255. *
    1256. +
    1257. * @return {patio.Dataset} a cloned dataset with the union.
    1258. +
    1259. *
    1260. +
    1261. */
    1262. +
    1263. union: function (dataset, opts) {
    1264. +
    1265. 21 opts = isUndefined(opts) ? {} : opts;
    1266. +
    1267. 21 if (!isHash(opts)) {
    1268. +
    1269. 3 opts = {all: opts};
    1270. +
    1271. }
    1272. +
    1273. 21 return this.compoundClone("union", dataset, opts);
    1274. },
    1275. -
    1276. isUppercase:function isUppercase(opts) {
    1277. -
    1278. 1 return this.__addAction(function (col) {
    1279. -
    1280. 3 return validatorCheck(col).isUppercase();
    1281. -
    1282. }, merge({message:"{col} must be uppercase got {val}."}, opts));
    1283. +
    1284. /**
    1285. +
    1286. * Returns a copy of the dataset with no limit or offset.
    1287. +
    1288. *
    1289. +
    1290. * @example
    1291. +
    1292. * DB.from("t").limit(10, 20).unlimited().sql;
    1293. +
    1294. * //=> SELECT * FROM t
    1295. +
    1296. *
    1297. +
    1298. * @return {patio.Dataset} a cloned dataset with no limit or offset.
    1299. +
    1300. */
    1301. +
    1302. unlimited: function () {
    1303. +
    1304. 1 return this.mergeOptions({limit: null, offset: null});
    1305. },
    1306. -
    1307. isEmpty:function isEmpty(opts) {
    1308. -
    1309. 1 return this.__addAction(function (col) {
    1310. -
    1311. 3 try {
    1312. -
    1313. 3 validatorCheck(col).notEmpty();
    1314. -
    1315. 2 return false;
    1316. -
    1317. } catch (e) {
    1318. -
    1319. 1 return true;
    1320. -
    1321. }
    1322. -
    1323. }, merge({message:"{col} must be empty got {val}."}, opts));
    1324. +
    1325. /**
    1326. +
    1327. * Returns a copy of the dataset with no order.
    1328. +
    1329. *
    1330. +
    1331. * @example
    1332. +
    1333. * DB.from("t").order("a", sql.identifier("b").desc()).unordered().sql;
    1334. +
    1335. * //=> SELECT * FROM t
    1336. +
    1337. *
    1338. +
    1339. * @return {patio.Dataset} a cloned dataset with no order.
    1340. +
    1341. */
    1342. +
    1343. unordered: function () {
    1344. +
    1345. 115 return this.order(null);
    1346. },
    1347. -
    1348. isNotEmpty:function isNotEmpty(opts) {
    1349. -
    1350. 2 return this.__addAction(function (col) {
    1351. -
    1352. 11 return validatorCheck(col).notEmpty();
    1353. -
    1354. }, merge({message:"{col} must not be empty."}, opts));
    1355. +
    1356. /**
    1357. +
    1358. * Add a condition to the WHERE clause. See {@link patio.Dataset#filter} for argument types.
    1359. +
    1360. *
    1361. +
    1362. * @example
    1363. +
    1364. * DB.from("test").where('price < ? AND id in ?', 100, [1, 2, 3]).sql;
    1365. +
    1366. * //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
    1367. +
    1368. * DB.from("test").where('price < {price} AND id in {ids}', {price:100, ids:[1, 2, 3]}).sql;
    1369. +
    1370. * //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))")
    1371. +
    1372. *
    1373. +
    1374. */
    1375. +
    1376. where: function () {
    1377. +
    1378. 252 return this._filter.apply(this, ["where"].concat(argsToArray(arguments)));
    1379. },
    1380. -
    1381. isCreditCard:function isCreditCard(opts) {
    1382. -
    1383. 0 return this.__addAction(function (col) {
    1384. -
    1385. 0 return validatorCheck(col).isCreditCard();
    1386. -
    1387. }, merge({message:"{col} is an invalid credit card"}, opts));
    1388. +
    1389. /**
    1390. +
    1391. * Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
    1392. +
    1393. * A common table expression acts as an inline view for the query.
    1394. +
    1395. *
    1396. +
    1397. * @name with
    1398. +
    1399. * @example
    1400. +
    1401. *
    1402. +
    1403. * DB.from("t")["with"]("t", db.from("x"))["with"]("j", db.from("y")).sql;
    1404. +
    1405. * //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y) SELECT * FROM t'
    1406. +
    1407. *
    1408. +
    1409. * DB.from("t")["with"]("t", db.from("x")).withRecursive("j", db.from("y"), db.from("j")).sql;
    1410. +
    1411. * //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t'
    1412. +
    1413. *
    1414. +
    1415. * DB.from("t")["with"]("t", db.from("x"), {args:["b"]}).sql;
    1416. +
    1417. * //=> 'WITH t(b) AS (SELECT * FROM x) SELECT * FROM t'
    1418. +
    1419. *
    1420. +
    1421. * @param {String} name the name of the to assign to the CTE.
    1422. +
    1423. * @param {patio.Dataset} dataset the dataset to use for the CTE.
    1424. +
    1425. * @param {Object} opts extra options.
    1426. +
    1427. * @param {String[]} [opts.args] colums/args for the CTE.
    1428. +
    1429. * @param {Boolean} [opts.recursive] set to true that the CTE is recursive.
    1430. +
    1431. *
    1432. +
    1433. * @return {patio.Dataset} a cloned dataset with the CTE.
    1434. +
    1435. */
    1436. +
    1437. "with": function (name, dataset, opts) {
    1438. +
    1439. 6 if (!this.supportsCte) {
    1440. +
    1441. 1 throw new QueryError("this dataset does not support common table expressions");
    1442. +
    1443. }
    1444. +
    1445. 5 return this.mergeOptions({
    1446. +
    1447. "with": (this.__opts["with"] || []).concat([merge(opts || {}, {name: this.stringToIdentifier(name), dataset: dataset})])
    1448. +
    1449. });
    1450. },
    1451. -
    1452. check:function (fun, opts) {
    1453. -
    1454. 4 return this.__addAction(fun, opts);
    1455. +
    1456. /**
    1457. +
    1458. * Add a recursive common table expression (CTE) with the given name, a dataset that
    1459. +
    1460. * defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
    1461. +
    1462. * of the CTE.
    1463. +
    1464. *
    1465. +
    1466. * @example
    1467. +
    1468. *
    1469. +
    1470. * //Sing withRecursive call.
    1471. +
    1472. * DB.from("t").withRecursive("t", db.from("x"), db.from("t")).sql;
    1473. +
    1474. * //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
    1475. +
    1476. *
    1477. +
    1478. * //Multiple withRecursive calls.
    1479. +
    1480. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"))
    1481. +
    1482. * .withRecursive("j", db.from("y"), db.from("j")).sql;
    1483. +
    1484. * //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t),
    1485. +
    1486. * j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t';
    1487. +
    1488. *
    1489. +
    1490. * //Adding args
    1491. +
    1492. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {args:["b", "c"]}).sql;
    1493. +
    1494. * //=> 'WITH t(b, c) AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
    1495. +
    1496. *
    1497. +
    1498. * //Setting union all to false
    1499. +
    1500. * DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {unionAll:false}).sql;
    1501. +
    1502. * //=> 'WITH t AS (SELECT * FROM x UNION SELECT * FROM t) SELECT * FROM t');
    1503. +
    1504. *
    1505. +
    1506. * @param {String} name the name to assign to the CTE
    1507. +
    1508. * @param {patio.Dataset} nonRecursive the non-recursive part of the CTE
    1509. +
    1510. * @param {patio.Dataset} recursive the recursive part of the CTE
    1511. +
    1512. * @param {Object} [opts={}] extra options
    1513. +
    1514. * @param {String[]} [opts.args] columns to include with the CTE
    1515. +
    1516. * @param {Boolena} [opts.unionAll] set to false to use UNION instead of UNION ALL when combining non recursive
    1517. +
    1518. * with recursive.
    1519. +
    1520. *
    1521. +
    1522. * @return {patio.Dataset} a cloned dataset with the CTE.
    1523. +
    1524. */
    1525. +
    1526. withRecursive: function (name, nonRecursive, recursive, opts) {
    1527. +
    1528. 7 if (!this.supportsCte) {
    1529. +
    1530. 1 throw new QueryError("This dataset does not support common table expressions");
    1531. +
    1532. }
    1533. +
    1534. 6 opts = opts || {};
    1535. +
    1536. 6 var wit = (this.__opts["with"] || []).concat([merge(opts, {recursive: true, name: this.stringToIdentifier(name), dataset: nonRecursive.union(recursive, {all: opts.unionAll != false, fromSelf: false})})]);
    1537. +
    1538. 6 return this.mergeOptions({"with": wit});
    1539. },
    1540. -
    1541. validate:function validate(value) {
    1542. -
    1543. 218 var errOpts = {col:this.col, val:value};
    1544. -
    1545. 218 return compact(this.__actions.map(function (action) {
    1546. -
    1547. 248 var actionOpts = action.opts;
    1548. -
    1549. 248 if (!actionOpts.onlyDefined || (combIsDefined(value) &&
    1550. -
    1551. (!actionOpts.onlyNotNull || !combIsNull(value)) )) {
    1552. -
    1553. 186 var ret = null;
    1554. -
    1555. 186 try {
    1556. -
    1557. 186 if (!action.action(value)) {
    1558. -
    1559. 69 ret = format(actionOpts.message, errOpts);
    1560. -
    1561. }
    1562. -
    1563. } catch (e) {
    1564. -
    1565. 28 ret = format(actionOpts.message, errOpts);
    1566. -
    1567. }
    1568. -
    1569. 186 return ret;
    1570. -
    1571. }
    1572. -
    1573. }, this));
    1574. -
    1575. }
    1576. -
    1577. -
    1578. }
    1579. -
    1580. });
    1581. -
    1582. -
    1583. 1function shouldValidate(opts, def) {
    1584. -
    1585. 115 opts = opts || {};
    1586. -
    1587. 115 return combIsBoolean(opts.validate) ? opts.validate : def;
    1588. -
    1589. }
    1590. -
    1591. -
    1592. 1function validateHook(prop, next, opts) {
    1593. -
    1594. 115 if (shouldValidate(opts, prop) && !this.isValid()) {
    1595. -
    1596. 45 next(flatten(toArray(this.errors).map(function (entry) {
    1597. -
    1598. 64 return entry[1].map(function (err) {
    1599. -
    1600. 48 return new Error(err);
    1601. -
    1602. });
    1603. -
    1604. })));
    1605. -
    1606. } else {
    1607. -
    1608. 70 next();
    1609. -
    1610. }
    1611. -
    1612. }
    1613. -
    1614. -
    1615. 1define(null, {
    1616. +
    1617. /**
    1618. +
    1619. * Returns a copy of the dataset with the static SQL used. This is useful if you want
    1620. +
    1621. * to keep the same {@link patio.Dataset#rowCb}/{@link patio.Dataset#graph},
    1622. +
    1623. * but change the SQL used to custom SQL.
    1624. +
    1625. *
    1626. +
    1627. * @example
    1628. +
    1629. * DB.from("items").withSql('SELECT * FROM foo')
    1630. +
    1631. * //=> SELECT * FROM foo
    1632. +
    1633. *
    1634. +
    1635. * @param {String} sql sql for the dataset to use.
    1636. +
    1637. *
    1638. +
    1639. * @return {patio.Dataset} a cloned dataset with the static sql set.
    1640. +
    1641. */
    1642. +
    1643. withSql: function (sql) {
    1644. +
    1645. 46 var args = argsToArray(arguments).slice(1);
    1646. +
    1647. 46 if (args.length) {
    1648. +
    1649. 23 sql = new PlaceHolderLiteralString(sql, args)
    1650. +
    1651. }
    1652. +
    1653. 46 return this.mergeOptions({sql: sql});
    1654. +
    1655. },
    1656. -
    1657. instance:{
    1658. /**
    1659. -
    1660. * A validation plugin for patio models. This plugin adds a <code>validate</code> method to each {@link patio.Model}
    1661. -
    1662. * class that adds it as a plugin. This plugin does not include most typecast checks as <code>patio</code> already checks
    1663. -
    1664. * types upon column assignment.
    1665. +
    1666. * Add the dataset to the list of compounds
    1667. *
    1668. -
    1669. * To do single col validation
    1670. -
    1671. * {@code
    1672. +
    1673. * @param {String} type the type of compound (i.e. "union", "intersect")
    1674. +
    1675. * @param {patio.Dataset} dataset the dataset to add to
    1676. +
    1677. * @param [Object] [options={}] compound option to use (i.e {all : true})
    1678. *
    1679. -
    1680. * var Model = patio.addModel("validator", {
    1681. -
    1682. * plugins:[ValidatorPlugin]
    1683. -
    1684. * });
    1685. -
    1686. * //this ensures column assignment
    1687. -
    1688. * Model.validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);
    1689. -
    1690. * //col2 does not have to be assigned but if it is it must match /hello/ig.
    1691. -
    1692. * Model.validate("col2").like(/hello/ig);
    1693. -
    1694. * //Ensures that the emailAddress column is a valid email address.
    1695. -
    1696. * Model.validate("emailAddress").isEmailAddress();
    1697. -
    1698. * }
    1699. +
    1700. * @return {patio.Dataset} ds with the dataset added to the compounds.
    1701. +
    1702. */
    1703. +
    1704. compoundClone: function (type, dataset, options) {
    1705. +
    1706. 51 var ds = this._compoundFromSelf().mergeOptions({compounds: (array.toArray(this.__opts.compounds || [])).concat([
    1707. +
    1708. [type, dataset._compoundFromSelf(), options.all]
    1709. +
    1710. ])});
    1711. +
    1712. 51 return options.fromSelf === false ? ds : ds.fromSelf(options);
    1713. +
    1714. },
    1715. +
    1716. +
    1717. /**
    1718. +
    1719. * Returns the a cloned dataset with out the {@link patio.Dataset#rowCb}
    1720. *
    1721. -
    1722. * Or you can do a mass validation through a callback.
    1723. -
    1724. * {@code
    1725. +
    1726. * @example
    1727. +
    1728. * var ds = DB.from("test");
    1729. +
    1730. * ds.rowCb = function(r){
    1731. +
    1732. * r.a = r.a * 2;
    1733. +
    1734. * }
    1735. *
    1736. -
    1737. * var Model = patio.addModel("validator", {
    1738. -
    1739. * plugins:[ValidatorPlugin]
    1740. +
    1741. * ds.all().then(function(ret){
    1742. +
    1743. * //ret === [{a : 4}, {a : 6}]
    1744. * });
    1745. -
    1746. * Model.validate(function(validate){
    1747. -
    1748. * //this ensures column assignment
    1749. -
    1750. * validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);
    1751. -
    1752. * //col2 does not have to be assigned but if it is it must match /hello/ig.
    1753. -
    1754. * validate("col2").isLike(/hello/ig);
    1755. -
    1756. * //Ensures that the emailAddress column is a valid email address.
    1757. -
    1758. * validate("emailAddress").isEmail();
    1759. +
    1760. * ds.naked().all().then(function(ret){
    1761. +
    1762. * //ret === [{a : 2}, {a : 3}];
    1763. * });
    1764. -
    1765. * }
    1766. *
    1767. -
    1768. * To check if a {@link patio.Model} is valid you can run the <code>isValid</code> method.
    1769. +
    1770. * @return {patio.Dataset} a cloned dataset with out the {@link patio.Dataset#rowCb}
    1771. +
    1772. */
    1773. +
    1774. naked: function () {
    1775. +
    1776. 2266 var ds = this.mergeOptions({});
    1777. +
    1778. 2266 ds.rowCb = null;
    1779. +
    1780. 2266 return ds;
    1781. +
    1782. },
    1783. +
    1784. +
    1785. /**
    1786. +
    1787. * @private
    1788. *
    1789. -
    1790. * {@code
    1791. -
    1792. * var model1 = new Model({col2 : 'grape', emailAddress : "test"}),
    1793. -
    1794. * model2 = new Model({col1 : "grape", col2 : "hello", emailAddress : "test@test.com"});
    1795. +
    1796. * Adds a group of conditions joined by the second arg, wrapped in parens, connected to an existing where/having
    1797. +
    1798. * clause by the first arg.
    1799. +
    1800. * If the where/having clause doesn't yet exist, a where clause is created with the group.
    1801. *
    1802. -
    1803. * model1.isValid() //false
    1804. -
    1805. * model2.isValid() //true
    1806. -
    1807. * }
    1808. +
    1809. * @example
    1810. *
    1811. -
    1812. * To get the errors associated with an invalid model you can access the errors property
    1813. +
    1814. * DB.from("items").filter({id, [1,2,3]})._addGroupedCondition("AND", "OR", [{price: {lt : 0}}, {price: {gt: 10}]).sql;
    1815. +
    1816. * //=> SELECT
    1817. +
    1818. * *
    1819. +
    1820. * FROM
    1821. +
    1822. * items
    1823. +
    1824. * WHERE
    1825. +
    1826. * ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))
    1827. *
    1828. -
    1829. * {@code
    1830. -
    1831. * model1.errors; //{ col1: [ 'col1 must be defined.' ],
    1832. -
    1833. * // col2: [ 'col2 must be like /hello/gi got grape.' ],
    1834. -
    1835. * // emailAddress: [ 'emailAddress must be a valid Email Address got test' ] }
    1836. -
    1837. * }
    1838. +
    1839. * DB.from("items")._addGroupedCondition("AND", "OR", [{price: {lt : 0}}, {price: {gt: 10}]).sql;
    1840. +
    1841. * //=> SELECT
    1842. +
    1843. * *
    1844. +
    1845. * FROM
    1846. +
    1847. * items
    1848. +
    1849. * WHERE
    1850. +
    1851. * ((price < 0) OR (price > 10))
    1852. *
    1853. -
    1854. * Validation is also run pre save and pre update. To prevent this you can specify the <code>validate</code> option
    1855. +
    1856. * @param {String} If there is an existing where/having clause, this arg will join the new condition group to it. "AND" if undef. Should be "AND" or "OR"
    1857. +
    1858. * @param {String} The conditions will be joined this. Same expectations as the first param.
    1859. +
    1860. * @param See {@link patio.Dataset#filter}.
    1861. *
    1862. -
    1863. * {@code
    1864. -
    1865. * model1.save(null, {validate : false});
    1866. -
    1867. * model2.save(null, {validate : false});
    1868. -
    1869. * }
    1870. +
    1871. * @return {patio.Dataset} a cloned dataset with the condition group added to the WHERE/HAVING clause.
    1872. +
    1873. */
    1874. +
    1875. _addGroupedCondition: function (addedByBool, groupedByBool) {
    1876. +
    1877. 6 groupedByBool = isUndefined(groupedByBool) ? "AND" : groupedByBool;
    1878. +
    1879. 6 var tOpts = this.__opts,
    1880. +
    1881. clause = (tOpts.having ? "having" : "where"),
    1882. +
    1883. clauseObj = tOpts[clause];
    1884. +
    1885. 6 var args = argsToArray(arguments, 2);
    1886. +
    1887. 6 args = args.length == 1 ? args[0] : args;
    1888. +
    1889. 6 var opts = {};
    1890. +
    1891. 6 if (clauseObj) {
    1892. +
    1893. 3 addedByBool = isUndefined(addedByBool) ? "AND" : addedByBool;
    1894. +
    1895. 3 opts[clause] = new BooleanExpression(addedByBool, clauseObj, this._filterExpr(args, null, groupedByBool));
    1896. +
    1897. } else {
    1898. +
    1899. 3 opts[clause] = this._filterExpr(args, null, groupedByBool);
    1900. +
    1901. }
    1902. +
    1903. 6 return this.mergeOptions(opts);
    1904. +
    1905. },
    1906. +
    1907. +
    1908. /**
    1909. +
    1910. * @private
    1911. +
    1912. * Protected
    1913. *
    1914. -
    1915. * Or you can specify the class level properties <code>validateOnSave</code> and <code>validateOnUpdate</code>
    1916. -
    1917. * to false respectively
    1918. -
    1919. * {@code
    1920. -
    1921. * Model.validateOnSave = false;
    1922. -
    1923. * Model.validateOnUpdate = false;
    1924. -
    1925. * }
    1926. +
    1927. * Internal filter method so it works on either the having or where clauses.
    1928. +
    1929. */
    1930. +
    1931. _filter: function (clause) {
    1932. +
    1933. 3765 var cond = argsToArray(arguments).slice(1), cb;
    1934. +
    1935. 3765 if (cond.length && isFunction(cond[cond.length - 1])) {
    1936. +
    1937. 59 cb = cond.pop();
    1938. +
    1939. }
    1940. +
    1941. 3765 cond = cond.length == 1 ? cond[0] : cond
    1942. +
    1943. 3765 if ((cond == null || cond == undefined || cond === "") || (isArray(cond) && cond.length == 0 && !cb) || (isObject(cond) && isEmpty(cond) && !cb)) {
    1944. +
    1945. 293 return this.mergeOptions();
    1946. +
    1947. } else {
    1948. +
    1949. 3472 cond = this._filterExpr(cond, cb);
    1950. +
    1951. 3469 var cl = this.__opts[clause];
    1952. +
    1953. 3469 cl && (cond = new BooleanExpression("AND", cl, cond));
    1954. +
    1955. 3469 var opts = {};
    1956. +
    1957. 3469 opts[clause] = cond;
    1958. +
    1959. 3469 return this.mergeOptions(opts);
    1960. +
    1961. }
    1962. +
    1963. },
    1964. +
    1965. +
    1966. /**
    1967. +
    1968. * Splits a possible implicit alias, handling both {@link patio.sql.AliasedExpression}s
    1969. +
    1970. * and strings. Returns an array of two elements, with the first being the
    1971. +
    1972. * main expression, and the second being the alias. Alias may be null if it is a
    1973. +
    1974. * string that does not contain an alias {table}___{alias}.
    1975. +
    1976. */
    1977. +
    1978. _splitAlias: function (c) {
    1979. +
    1980. 619 var ret;
    1981. +
    1982. 619 if (isInstanceOf(c, AliasedExpression)) {
    1983. +
    1984. 5 ret = [c.expression, c.alias];
    1985. +
    1986. 614 } else if (isString(c)) {
    1987. +
    1988. 0 var parts = this._splitString(c), cTable = parts[0], column = parts[1], alias = parts[2];
    1989. +
    1990. 0 if (alias) {
    1991. +
    1992. 0 ret = [cTable ? new QualifiedIdentifier(cTable, column) : column, alias];
    1993. +
    1994. } else {
    1995. +
    1996. 0 ret = [c, null];
    1997. +
    1998. }
    1999. +
    2000. } else {
    2001. +
    2002. 614 ret = [c, null];
    2003. +
    2004. }
    2005. +
    2006. 619 return ret;
    2007. +
    2008. },
    2009. +
    2010. +
    2011. +
    2012. /**
    2013. +
    2014. * @private
    2015. +
    2016. * Inverts the given order by breaking it into a list of column references
    2017. +
    2018. * and inverting them.
    2019. *
    2020. -
    2021. * Avaiable validation methods are.
    2022. +
    2023. * ds.invertOrder([sql.identifier("id").desc()]]) //=> [id]
    2024. +
    2025. * ds.invertOrder("category", sql.identifier("price").desc()]) #=> [category.desc(), price]
    2026. +
    2027. */
    2028. +
    2029. +
    2030. _invertOrder: function (order) {
    2031. +
    2032. 46 var ret = order;
    2033. +
    2034. 46 if (order) {
    2035. +
    2036. 45 ret = order.map(function (o) {
    2037. +
    2038. 57 if (isInstanceOf(o, OrderedExpression)) {
    2039. +
    2040. 17 return o.invert();
    2041. +
    2042. } else {
    2043. +
    2044. 40 return new OrderedExpression(isString(o) ? new Identifier(o) : o);
    2045. +
    2046. }
    2047. +
    2048. }, this);
    2049. +
    2050. }
    2051. +
    2052. 46 return ret;
    2053. +
    2054. },
    2055. +
    2056. +
    2057. /**
    2058. +
    2059. * Creates a boolean expression that each key is compared to its value using the provided operator.
    2060. *
    2061. -
    2062. * <ul>
    2063. -
    2064. * <li><code>isAfter</code> : check that a date is after a specified date</li>
    2065. -
    2066. * <li><code>isBefore</code> : check that a date is after before a specified date </li>
    2067. -
    2068. * <li><code>isDefined</code> : ensure that a column is defined</li>
    2069. -
    2070. * <li><code>isNotDefined</code> : ensure that a column is not defined</li>
    2071. -
    2072. * <li><code>isNotNull</code> : ensure that a column is defined and not null</li>
    2073. -
    2074. * <li><code>isNull</code> : ensure that a column is not defined or null</li>
    2075. -
    2076. * <li><code>isEq</code> : ensure that a column equals a value <b>this uses deep equal</b></li>
    2077. -
    2078. * <li><code>isNeq</code> : ensure that a column does not equal a value <b>this uses deep equal</b></li>
    2079. -
    2080. * <li><code>isLike</code> : ensure that a column is like a value, can be a regexp or string</li>
    2081. -
    2082. * <li><code>isNotLike</code> : ensure that a column is not like a value, can be a regexp or string</li>
    2083. -
    2084. * <li><code>isLt</code> : ensure that a column is less than a value</li>
    2085. -
    2086. * <li><code>isGt</code> : ensure that a column is greater than a value</li>
    2087. -
    2088. * <li><code>isLte</code> : ensure that a column is less than or equal to a value</li>
    2089. -
    2090. * <li><code>isGte</code> : ensure that a column is greater than or equal to a value</li>
    2091. -
    2092. * <li><code>isIn</code> : ensure that a column is contained in an array of values</li>
    2093. -
    2094. * <li><code>isNotIn</code> : ensure that a column is not contained in an array of values</li>
    2095. -
    2096. * <li><code>isMacAddress</code> : ensure that a column is a valid MAC address</li>
    2097. -
    2098. * <li><code>isIPAddress</code> : ensure that a column is a valid IPv4 or IPv6 address</li>
    2099. -
    2100. * <li><code>isIPv4Address</code> : ensure that a column is a valid IPv4 address</li>
    2101. -
    2102. * <li><code>isIPv6Address</code> : ensure that a column is a valid IPv6 address</li>
    2103. -
    2104. * <li><code>isUUID</code> : ensure that a column is a valid UUID</li>
    2105. -
    2106. * <li><code>isEmail</code> : ensure that a column is a valid email address</li>
    2107. -
    2108. * <li><code>isUrl</code> : ensure than a column is a valid URL</li>
    2109. -
    2110. * <li><code>isAlpha</code> : ensure than a column is all letters</li>
    2111. -
    2112. * <li><code>isAlphaNumeric</code> : ensure than a column is all letters or numbers</li>
    2113. -
    2114. * <li><code>hasLength</code> : ensure than a column is fits within the specified length accepts a min and optional max value</li>
    2115. -
    2116. * <li><code>isLowercase</code> : ensure than a column is lowercase</li>
    2117. -
    2118. * <li><code>isUppercase</code> : ensure than a column is uppercase</li>
    2119. -
    2120. * <li><code>isEmpty</code> : ensure than a column empty (i.e. a blank string)</li>
    2121. -
    2122. * <li><code>isNotEmpty</code> : ensure than a column not empty (i.e. not a blank string)</li>
    2123. -
    2124. * <li><code>isCreditCard</code> : ensure than a is a valid credit card number</li>
    2125. -
    2126. * <li><code>check</code> : accepts a function to perform validation</li>
    2127. -
    2128. * </ul>
    2129. +
    2130. * @example
    2131. +
    2132. *
    2133. +
    2134. * ds.__createBoolExpression("gt", {x : 1, y:2, z : 5}) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))
    2135. +
    2136. * ds.__createBoolExpression("gt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))
    2137. +
    2138. * ds.__createBoolExpression("lt", {x : 1, y:2, z : 5}) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))
    2139. +
    2140. * ds.__createBoolExpression("lt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))
    2141. +
    2142. *
    2143. +
    2144. * @param {String} op valid boolean expression operator to capare each K,V pair with
    2145. +
    2146. * @param {Object| Array } obj object or two dimensional array containing key value pairs
    2147. +
    2148. *
    2149. +
    2150. * @return {patio.sql.BooleanExpression} boolean expression joined by a AND of each key value pair compared by the op
    2151. +
    2152. */
    2153. +
    2154. __createBoolExpression: function (op, obj) {
    2155. +
    2156. 32 var pairs = [];
    2157. +
    2158. 32 if (Expression.isConditionSpecifier(obj)) {
    2159. +
    2160. 32 if (isHash(obj)) {
    2161. +
    2162. 18 obj = array.toArray(obj);
    2163. +
    2164. }
    2165. +
    2166. 32 obj.forEach(function (pair) {
    2167. +
    2168. 42 pairs.push(new BooleanExpression(op, new Identifier(pair[0]), pair[1]));
    2169. +
    2170. });
    2171. +
    2172. }
    2173. +
    2174. 32 return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
    2175. +
    2176. },
    2177. +
    2178. +
    2179. /**
    2180. +
    2181. * @private
    2182. *
    2183. -
    2184. * All of the validation methods are chainable, and accept an options argument.
    2185. +
    2186. * Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
    2187. *
    2188. -
    2189. * The options include
    2190. -
    2191. * <ul>
    2192. -
    2193. * <li><code>message</code> : a message to return if a column fails validation. The message can include <code>{val}</code> and <code>{col}</code>
    2194. -
    2195. * replacements which will insert the invalid value and the column name.
    2196. -
    2197. * </li>
    2198. -
    2199. * <li><code>[onlyDefined=true]</code> : set to false to run the method even if the column value is not defined.</li>
    2200. -
    2201. * <li><code>[onlyNotNull=true]</code> : set to false to run the method even if the column value is null.</li>
    2202. -
    2203. * </ul>
    2204. +
    2205. * @example
    2206. *
    2207. +
    2208. * ds.__createBetweenExpression({x : [1,2]}) => //=> WHERE ((x >= 1) AND (x <= 10))
    2209. +
    2210. * ds.__createBetweenExpression({x : [1,2]}, true) => //=> WHERE ((x < 1) OR (x > 10))
    2211. *
    2212. -
    2213. * @constructs
    2214. -
    2215. * @name ValidatorPlugin
    2216. -
    2217. * @memberOf patio.plugins
    2218. -
    2219. * @property {Object} [errors={}] the validation errors for this model.
    2220. +
    2221. * @param {Object} obj object where the keys are columns and the values are two element arrays.
    2222. +
    2223. * @param {Boolean} [invert] if set to true it inverts the between to make it not between the two values
    2224. *
    2225. +
    2226. * @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
    2227. */
    2228. -
    2229. constructor:function () {
    2230. -
    2231. 114 this._super(arguments);
    2232. -
    2233. 114 this.errors = {};
    2234. +
    2235. __createBetweenExpression: function (obj, invert) {
    2236. +
    2237. 4 var pairs = [];
    2238. +
    2239. 4 for (var i in obj) {
    2240. +
    2241. 4 var v = obj[i];
    2242. +
    2243. 4 if (isArray(v) && v.length) {
    2244. +
    2245. 2 var ident = this.stringToIdentifier(i);
    2246. +
    2247. 2 pairs.push(new BooleanExpression("AND", new BooleanExpression("gte", ident, v[0]), new BooleanExpression("lte", ident, v[1])));
    2248. +
    2249. } else {
    2250. +
    2251. 2 throw new QueryError("Between requires an array for the value");
    2252. +
    2253. }
    2254. +
    2255. }
    2256. +
    2257. 2 var ret = pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs))
    2258. +
    2259. 2 return invert ? BooleanExpression.invert(ret) : ret;
    2260. },
    2261. +
    2262. /**
    2263. -
    2264. * Validates a model, returning an array of error messages for each invalid property.
    2265. -
    2266. * @return {String[]} an array of error messages for each invalid property.
    2267. +
    2268. * @private
    2269. +
    2270. * Converts an array to a two dimensional array where the first element
    2271. +
    2272. * is the identifier and the second argument is the value that the value should equal
    2273. +
    2274. * used by is{Null|NotNull|True|NotTrue|False|notFalse} functions to join all the values passed in.
    2275. +
    2276. * @param {String[]|patio.sql.Identifier} arr array of elements to make a condition specifier out of
    2277. +
    2278. * @param defaultOp the value to assign a value if one is not provided.
    2279. +
    2280. *
    2281. +
    2282. * @return { [[]] } an array of two element arrays.
    2283. */
    2284. -
    2285. validate:function () {
    2286. -
    2287. 159 this.errors = {};
    2288. -
    2289. 159 return flatten(this._static.validators.map(function runValidator(validator) {
    2290. -
    2291. 218 var col = validator.col, val = this.__values[validator.col], ret = validator.validate(val);
    2292. -
    2293. 218 this.errors[col] = ret;
    2294. -
    2295. 218 return ret;
    2296. -
    2297. }, this));
    2298. +
    2299. __arrayToConditionSpecifier: function (arr, defaultOp) {
    2300. +
    2301. 22 var ret = [];
    2302. +
    2303. 22 arr.forEach(function (a) {
    2304. +
    2305. 28 if (isString(a)) {
    2306. +
    2307. 18 a = this.stringToIdentifier(a);
    2308. +
    2309. }
    2310. +
    2311. 28 if (isInstanceOf(a, Identifier)) {
    2312. +
    2313. 18 ret.push([a, defaultOp]);
    2314. +
    2315. 10 } else if (isHash(a)) {
    2316. +
    2317. 4 ret = ret.concat(array.toArray(a));
    2318. +
    2319. } else {
    2320. +
    2321. 6 throw new QueryError("Invalid condition specifier " + a);
    2322. +
    2323. }
    2324. +
    2325. }, this);
    2326. +
    2327. 16 return ret;
    2328. },
    2329. /**
    2330. -
    2331. * Returns if this model passes validation.
    2332. +
    2333. * @private
    2334. *
    2335. -
    2336. * @return {Boolean}
    2337. +
    2338. * SQL expression object based on the expr type. See {@link patio.Dataset#filter}
    2339. */
    2340. -
    2341. isValid:function () {
    2342. -
    2343. 159 return this.validate().length === 0;
    2344. +
    2345. _filterExpr: function (expr, cb, joinCond) {
    2346. +
    2347. 4295 expr = (isUndefined(expr) || isNull(expr) || (isArray(expr) && !expr.length)) ? null : expr;
    2348. +
    2349. 4295 if (expr && cb) {
    2350. +
    2351. 1 return new BooleanExpression(joinCond || "AND", this._filterExpr(expr, null, joinCond), this._filterExpr(cb, null, joinCond))
    2352. +
    2353. 4294 } else if (cb) {
    2354. +
    2355. 58 expr = cb
    2356. +
    2357. }
    2358. +
    2359. 4294 if (isInstanceOf(expr, Expression)) {
    2360. +
    2361. 459 if (isInstanceOf(expr, NumericExpression, StringExpression)) {
    2362. +
    2363. 2 throw new QueryError("Invalid SQL Expression type : " + expr);
    2364. +
    2365. }
    2366. +
    2367. 457 return expr;
    2368. +
    2369. 3835 } else if (isArray(expr)) {
    2370. +
    2371. 744 if (expr.length) {
    2372. +
    2373. 744 var first = expr[0];
    2374. +
    2375. 744 if (isString(first)) {
    2376. +
    2377. 25 return new PlaceHolderLiteralString(first, expr.slice(1), true);
    2378. +
    2379. 719 } else if (Expression.isConditionSpecifier(expr)) {
    2380. +
    2381. 708 return BooleanExpression.fromValuePairs(expr, joinCond)
    2382. +
    2383. } else {
    2384. +
    2385. 11 return BooleanExpression.fromArgs([joinCond || "AND"].concat(expr.map(function (e) {
    2386. +
    2387. 23 return this._filterExpr(e, null, joinCond);
    2388. +
    2389. }, this)));
    2390. +
    2391. }
    2392. +
    2393. }
    2394. +
    2395. 3091 } else if (isFunction(expr)) {
    2396. +
    2397. 59 return this._filterExpr(expr.call(sql, sql), null, joinCond);
    2398. +
    2399. 3032 } else if (isBoolean(expr)) {
    2400. +
    2401. 7 return new BooleanExpression("NOOP", expr);
    2402. +
    2403. 3025 } else if (isString(expr)) {
    2404. +
    2405. 1 return this.stringToIdentifier(expr);
    2406. +
    2407. 3024 } else if (isInstanceOf(expr, LiteralString)) {
    2408. +
    2409. 16 return new LiteralString("(" + expr + ")");
    2410. +
    2411. 3008 } else if (isHash(expr)) {
    2412. +
    2413. 3007 return BooleanExpression.fromValuePairs(expr, joinCond);
    2414. +
    2415. } else {
    2416. +
    2417. 1 throw new QueryError("Invalid filter argument");
    2418. +
    2419. }
    2420. +
    2421. },
    2422. +
    2423. +
    2424. +
    2425. /**@ignore*/
    2426. +
    2427. getters: {
    2428. +
    2429. +
    2430. /**
    2431. +
    2432. * @ignore
    2433. +
    2434. * Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
    2435. +
    2436. */
    2437. +
    2438. isSimpleSelectAll: function () {
    2439. +
    2440. 33 var o = {}, opts = this.__opts, count = 0;
    2441. +
    2442. 33 for (var i in opts) {
    2443. +
    2444. 70 if (opts[i] != null && this._static.NON_SQL_OPTIONS.indexOf(i) == -1) {
    2445. +
    2446. 37 o[i] = opts[i];
    2447. +
    2448. 37 count++;
    2449. +
    2450. }
    2451. +
    2452. }
    2453. +
    2454. 33 var f = o.from
    2455. +
    2456. 33 return count == 1 && f.length == 1 && (isString(f[0]) || isInstanceOf(f[0], AliasedExpression, Identifier));
    2457. +
    2458. }
    2459. }
    2460. },
    2461. -
    2462. "static":{
    2463. -
    2464. /**@lends patio.plugins.ValidatorPlugin*/
    2465. +
    2466. static: {
    2467. +
    2468. /**@lends patio.Dataset*/
    2469. /**
    2470. -
    2471. * Set to false to prevent model validation when saving.
    2472. -
    2473. * @default true
    2474. -
    2475. */
    2476. -
    2477. validateOnSave:true,
    2478. +
    2479. * These strings have {name}Join methods created (e.g. {@link patio.Dataset#innerJoin}) that
    2480. +
    2481. * call {@link patio.Dataset#joinTable} with the string, passing along the arguments and
    2482. +
    2483. * block from the method call.
    2484. +
    2485. **/
    2486. +
    2487. CONDITIONED_JOIN_TYPES: ["inner", "fullOuter", "rightOuter", "leftOuter", "full", "right", "left"],
    2488. /**
    2489. -
    2490. * Set to false to prevent model validation when updating.
    2491. -
    2492. * @default true
    2493. +
    2494. *
    2495. +
    2496. * These strings have {name}Join methods created (e.g. naturalJoin) that
    2497. +
    2498. * call {@link patio.Dataset#joinTable}. They only accept a single table
    2499. +
    2500. * argument which is passed to {@link patio.Dataset#joinTable}, and they throw an error
    2501. +
    2502. * if called with a block.
    2503. +
    2504. **/
    2505. +
    2506. UNCONDITIONED_JOIN_TYPES: ["natural", "naturalLeft", "naturalRight", "naturalFull", "cross"],
    2507. +
    2508. +
    2509. /**
    2510. +
    2511. * The dataset options that require the removal of cached columns
    2512. +
    2513. * if changed.
    2514. */
    2515. -
    2516. validateOnUpdate:true,
    2517. +
    2518. COLUMN_CHANGE_OPTS: ["select", "sql", "from", "join"],
    2519. -
    2520. init:function () {
    2521. -
    2522. 35 this._super(arguments);
    2523. -
    2524. },
    2525. +
    2526. /**
    2527. +
    2528. * Which options don't affect the SQL generation. Used by {@link patio.Dataset#simpleSelectAll}
    2529. +
    2530. * to determine if this is a simple SELECT * FROM table.
    2531. +
    2532. */
    2533. +
    2534. NON_SQL_OPTIONS: ["server", "defaults", "overrides", "graph", "eagerGraph", "graphAliases"],
    2535. -
    2536. __initValidation:function () {
    2537. -
    2538. 43 if (!this.__isValidationInited) {
    2539. -
    2540. 34 this.validators = [];
    2541. -
    2542. 34 this.pre("save", function preSaveValidate(next, opts) {
    2543. -
    2544. 114 validateHook.call(this, this._static.validateOnSave, next, opts);
    2545. -
    2546. });
    2547. -
    2548. 34 this.pre("update", function preUpdateValidate(next, opts) {
    2549. -
    2550. 1 validateHook.call(this, this._static.validateOnSave, next, opts);
    2551. -
    2552. });
    2553. -
    2554. 34 this.__isValidationInited = true;
    2555. -
    2556. }
    2557. -
    2558. },
    2559. -
    2560. __getValidator:function validator(name) {
    2561. -
    2562. 44 var ret = new Validator(name);
    2563. -
    2564. 44 this.validators.push(ret);
    2565. -
    2566. 44 return ret;
    2567. -
    2568. },
    2569. +
    2570. /**
    2571. +
    2572. * All methods that return modified datasets with a joined table added.
    2573. +
    2574. */
    2575. +
    2576. JOIN_METHODS: ["join", "joinTable"],
    2577. /**
    2578. -
    2579. * Sets up validation for a model.
    2580. -
    2581. *
    2582. -
    2583. * To do single col validation
    2584. -
    2585. * {@code
    2586. -
    2587. *
    2588. -
    2589. * var Model = patio.addModel("validator", {
    2590. -
    2591. * plugins:[ValidatorPlugin]
    2592. -
    2593. * });
    2594. -
    2595. * //this ensures column assignment
    2596. -
    2597. * Model.validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);
    2598. -
    2599. * //col2 does not have to be assigned but if it is it must match /hello/ig.
    2600. -
    2601. * Model.validate("col2").like(/hello/ig);
    2602. -
    2603. * //Ensures that the emailAddress column is a valid email address.
    2604. -
    2605. * Model.validate("emailAddress").isEmailAddress();
    2606. -
    2607. * }
    2608. -
    2609. *
    2610. -
    2611. * Or you can do a mass validation through a callback.
    2612. -
    2613. * {@code
    2614. -
    2615. *
    2616. -
    2617. * var Model = patio.addModel("validator", {
    2618. -
    2619. * plugins:[ValidatorPlugin]
    2620. -
    2621. * });
    2622. -
    2623. * Model.validate(function(validate){
    2624. -
    2625. * //this ensures column assignment
    2626. -
    2627. * validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);
    2628. -
    2629. * //col2 does not have to be assigned but if it is it must match /hello/ig.
    2630. -
    2631. * validate("col2").isLike(/hello/ig);
    2632. -
    2633. * //Ensures that the emailAddress column is a valid email address.
    2634. -
    2635. * validate("emailAddress").isEmail();
    2636. -
    2637. * });
    2638. -
    2639. * }
    2640. -
    2641. *
    2642. -
    2643. *
    2644. -
    2645. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    2646. -
    2647. *
    2648. -
    2649. * @throws {patio.ModelError} if name is not a function or string.
    2650. -
    2651. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    2652. +
    2653. * Methods that return modified datasets
    2654. */
    2655. -
    2656. validate:function (name) {
    2657. -
    2658. 43 this.__initValidation();
    2659. -
    2660. 43 var ret;
    2661. -
    2662. 43 if (isFunction(name)) {
    2663. -
    2664. 1 name.apply(this, [this.__getValidator.bind(this)]);
    2665. -
    2666. 1 ret = this;
    2667. -
    2668. 42 } else if (isString(name)) {
    2669. -
    2670. 42 ret = this.__getValidator(name);
    2671. -
    2672. } else {
    2673. -
    2674. 0 throw new ModelError("name is must be a string or function when validating");
    2675. -
    2676. }
    2677. -
    2678. 43 return ret;
    2679. +
    2680. QUERY_METHODS: ['addGraphAliases', "and", "distinct", "except", "exclude", "filter", "find", "is", "isNot",
    2681. +
    2682. "eq", "neq", "lt", "lte", "gt", "gte", "forUpdate", "from", "fromSelf", "graph", "grep", "group",
    2683. +
    2684. "groupAndCount", "groupBy", "having", "intersect", "invert", "limit", "lockStyle", "naked", "or", "order",
    2685. +
    2686. "orderAppend", "orderBy", "orderMore", "orderPrepend", "qualify", "reverse",
    2687. +
    2688. "reverseOrder", "select", "selectAll", "selectAppend", "selectMore", "setDefaults",
    2689. +
    2690. "setGraphAliases", "setOverrides", "unfiltered", "ungraphed", "ungrouped", "union", "unlimited",
    2691. +
    2692. "unordered", "where", "with", "withRecursive", "withSql"],
    2693. +
    2694. +
    2695. init: function () {
    2696. +
    2697. 35 this._super(arguments);
    2698. +
    2699. //initialize our join methods array
    2700. +
    2701. 35 var joinMethods = this.JOIN_METHODS;
    2702. +
    2703. 35 var queryMethods = this.QUERY_METHODS;
    2704. +
    2705. 35 this.UNCONDITIONED_JOIN_TYPES.forEach(function (joinType) {
    2706. +
    2707. 175 var m = joinType + "Join";
    2708. +
    2709. 175 joinMethods.push(m);
    2710. +
    2711. 175 queryMethods.push(m);
    2712. +
    2713. });
    2714. +
    2715. 35 this.CONDITIONED_JOIN_TYPES.forEach(function (joinType) {
    2716. +
    2717. 245 var m = joinType + "Join";
    2718. +
    2719. 245 joinMethods.push(m);
    2720. +
    2721. 245 queryMethods.push(m);
    2722. +
    2723. });
    2724. +
    2725. 35 this.QUERY_METHODS = queryMethods.concat(joinMethods);
    2726. }
    2727. }
    2728. -
    2729. -
    2730. }).as(module);
    2731. -
    +
  • }).
  • +
  • as(module);
  • @@ -21056,8 +21113,8 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 25725 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 25725 this._super(arguments);
  • +
  • 25745 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 25745 this._super(arguments);
  • },
  • /**
  • @@ -21736,7 +21793,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(){
  • -
  • 42250 return this.__quoteIdentifiers;
  • +
  • 42266 return this.__quoteIdentifiers;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21744,80 +21801,80 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(){
  • -
  • 13414 return this.__providesAccurateRowsMatched;
  • +
  • 13418 return this.__providesAccurateRowsMatched;
  • },
  • //Whether the dataset requires SQL standard datetimes (false by default,
  • // as most allow strings with ISO 8601 format).
  • /**@ignore*/
  • requiresSqlStandardDateTimes:function(){
  • -
  • 13421 return this.__requiresSqlStandardDateTimes;
  • +
  • 13425 return this.__requiresSqlStandardDateTimes;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(){
  • -
  • 13427 return this.__supportsCte;
  • +
  • 13431 return this.__supportsCte;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • /**@ignore*/
  • supportsDistinctOn:function(){
  • -
  • 2291 return this.__supportsDistinctOn;
  • +
  • 2295 return this.__supportsDistinctOn;
  • },
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(){
  • -
  • 13450 return this.__supportsIntersectExcept;
  • +
  • 13454 return this.__supportsIntersectExcept;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(){
  • -
  • 13426 return this.__supportsIntersectExceptAll;
  • +
  • 13430 return this.__supportsIntersectExceptAll;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(){
  • -
  • 13623 return this.__supportsIsTrue;
  • +
  • 13627 return this.__supportsIsTrue;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(){
  • -
  • 13425 return this.__supportsJoinUsing;
  • +
  • 13429 return this.__supportsJoinUsing;
  • },
  • //Whether modifying joined datasets is supported.
  • /**@ignore*/
  • supportsModifyingJoins:function(){
  • -
  • 2531 return this.__supportsModifyingJoins;
  • +
  • 2535 return this.__supportsModifyingJoins;
  • },
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(){
  • -
  • 13418 return this.__supportsMultipleColumnIn;
  • +
  • 13422 return this.__supportsMultipleColumnIn;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • /**@ignore*/
  • supportsTimestampTimezones:function(){
  • -
  • 2288 return this.__supportsTimestampTimezones;
  • +
  • 2292 return this.__supportsTimestampTimezones;
  • },
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(){
  • -
  • 13414 return this.__supportsTimestampUsecs;
  • +
  • 13418 return this.__supportsTimestampUsecs;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(){
  • -
  • 13414 return this.__supportsWindowFunctions;
  • +
  • 13418 return this.__supportsWindowFunctions;
  • }
  • },
  • @@ -21828,7 +21885,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(val){
  • -
  • 13426 this.__quoteIdentifiers = val;
  • +
  • 13430 this.__quoteIdentifiers = val;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21836,80 +21893,80 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(val){
  • -
  • 13414 this.__providesAccurateRowsMatched = val;
  • +
  • 13418 this.__providesAccurateRowsMatched = val;
  • },
  • //Whether the dataset requires SQL standard datetimes (false by default,
  • // as most allow strings with ISO 8601 format).
  • /**@ignore*/
  • requiresSqlStandardDateTimes:function(val){
  • -
  • 13414 this.__requiresSqlStandardDateTimes = val;
  • +
  • 13418 this.__requiresSqlStandardDateTimes = val;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(val){
  • -
  • 13415 this.__supportsCte = val;
  • +
  • 13419 this.__supportsCte = val;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • /**@ignore*/
  • supportsDistinctOn:function(val){
  • -
  • 2289 this.__supportsDistinctOn = val;
  • +
  • 2293 this.__supportsDistinctOn = val;
  • },
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(val){
  • -
  • 13416 this.__supportsIntersectExcept = val;
  • +
  • 13420 this.__supportsIntersectExcept = val;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(val){
  • -
  • 13416 this.__supportsIntersectExceptAll = val;
  • +
  • 13420 this.__supportsIntersectExceptAll = val;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(val){
  • -
  • 13414 this.__supportsIsTrue = val;
  • +
  • 13418 this.__supportsIsTrue = val;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(val){
  • -
  • 13416 this.__supportsJoinUsing = val;
  • +
  • 13420 this.__supportsJoinUsing = val;
  • },
  • //Whether modifying joined datasets is supported.
  • /**@ignore*/
  • supportsModifyingJoins:function(val){
  • -
  • 2289 this.__supportsModifyingJoins = val;
  • +
  • 2293 this.__supportsModifyingJoins = val;
  • },
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(val){
  • -
  • 13414 this.__supportsMultipleColumnIn = val;
  • +
  • 13418 this.__supportsMultipleColumnIn = val;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • /**@ignore*/
  • supportsTimestampTimezones:function(val){
  • -
  • 2288 this.__supportsTimestampTimezones = val;
  • +
  • 2292 this.__supportsTimestampTimezones = val;
  • },
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(val){
  • -
  • 13414 this.__supportsTimestampUsecs = val;
  • +
  • 13418 this.__supportsTimestampUsecs = val;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(val){
  • -
  • 13414 this.__supportsWindowFunctions = val;
  • +
  • 13418 this.__supportsWindowFunctions = val;
  • }
  • }
  • diff --git a/package.json b/package.json index cf5d4c9a..65be47f7 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "patio", "description": "Patio query engine and ORM", - "version": "0.2.10", + "version": "0.2.11", "keywords": [ "ORM", "object relation mapper",