diff --git a/History.md b/History.md index ba6c1571..65b9bcec 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,11 @@ +#0.1.1 / 2012-08-29 +* new patio.Dataset features + * #sourceList - get all sources as identifiers + * #joinSourceList - get all join sources + * #hasSelectSource - returns true if there are not any select sources (i.e select *) + * #seleIfNoSource - add the selects if there is not currently a select +* Fixed issue with patio.Model#_setFromDb where values not in the models table columns would be in accesible, (i.e a join with a model would not show the join columns) +* updated docs #0.1.0 / 2012-08-25 * Added custom getters (mbenedettini) * Added Validator plugin for models diff --git a/docs-md/coverage.html b/docs-md/coverage.html index c6344a66..21fb1ef8 100644 --- a/docs-md/coverage.html +++ b/docs-md/coverage.html @@ -256,10 +256,10 @@
* @param opts
*/
configureLogging:function (opts) {
comb.logger.configure();
LOGGER.level = "info";
comb.logger.configure(opts);
if (!opts) {
LOGGER.level = "info";
}
},
/**
* @return the typecasted value.
* */
typecastValue:function (columnType, value) {
if (isNull(value) || isUndefined(value)) {
if (isNull(value) || isUndefined(value)) {
return null;
}
var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
try {
if (isFunction(this[meth])) {
return this[meth](value);
var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
try {
if (isFunction(this[meth])) {
return this[meth](value);
} else {
return value;
}
// Typecast the value to a String
__typecastValueString:function (value) {
return "" + value;
return "" + value;
}
},
- associations/_Association.js
+ dataset/index.js
|
- Coverage87.18
- SLOC515
- LOC156
- Missed20
+ Coverage86.11
+ SLOC457
+ LOC72
+ Missed10
|
- 1
-var comb = require("comb-proxy"),
- +
define = comb.define,
+
- 1
+var comb = require("comb"),
- +
hitch = comb.hitch,
- +
logging = comb.logging,
- +
Logger = logging.Logger,
- +
errors = require("../errors"),
- +
QueryError = errors.QueryError,
- +
DatasetError = errors.DatasetError,
- +
Promise = comb.Promise,
PromiseList = comb.PromiseList,
isUndefined = comb.isUndefined,
- -
isUndefinedOrNull = comb.isUndefinedOrNull,
isBoolean = comb.isBoolean,
- -
isString = comb.isString,
- -
isHash = comb.isHash,
isFunction = comb.isFunction,
- -
isInstanceOf = comb.isInstanceOf,
- -
Promise = comb.Promise,
- -
PromiseList = comb.PromiseList,
- -
hitch = comb.hitch,
- -
array = comb.array,
- -
isArray = comb.isArray,
- -
Middleware = comb.plugins.Middleware,
- -
PatioError = require("../errors").PatioError;
- -
- 1
-var fetch = {
- -
LAZY:"lazy",
- -
EAGER:"eager"
- +
};
- +
isString = comb.isString,
- +
isFunction = comb.isFunction,
- +
isNull = comb.isNull,
- +
merge = comb.merge,
- +
define = comb.define,
- +
graph = require("./graph"),
- +
actions = require("./actions"),
- +
features = require("./features"),
- +
query = require("./query"),
- +
sql = require("./sql"),
- +
SQL = require("../sql").sql,
- +
AliasedExpression = SQL.AliasedExpression,
- +
Identifier = SQL.Identifier,
QualifiedIdentifier = SQL.QualifiedIdentifier;
- -
- -
/**
- -
* @class
- -
* Base class for all associations.
- -
*
- -
* </br>
- -
* <b>NOT to be instantiated directly</b>
- -
* Its just documented for reference.
- -
*
- -
* @constructs
- -
* @param {Object} options
- -
* @param {String} options.model a string to look up the model that we are associated with
- -
* @param {Function} options.filter a callback to find association if a filter is defined then
- -
* the association is read only
- -
* @param {Object} options.key object with left key and right key
- -
* @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
- -
* @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
- -
* the associations are automatically filled, if fetch.Lazy is supplied
- -
* then a promise is returned and is called back with the loaded models
- -
* @property {Model} model the model associatied with this association.
- -
* @name Association
- -
* @memberOf patio.associations
- -
* */
- 1
+define(Middleware, {
- 1
+var LOGGER = comb.logger("patio.Dataset");
- 1
define([actions, graph, features, query, sql], {
- -
instance:{
- -
/**@lends patio.associations.Association.prototype*/
- -
- -
type:"",
- -
- -
//Our associated model
- -
_model:null,
- -
- -
/**
- -
* Fetch type
- -
*/
- -
fetchType:fetch.LAZY,
- -
- -
/**how to order our association*/
- -
orderBy:null,
- -
- -
/**Our filter method*/
- -
filter:null,
- -
- -
__hooks:null,
- -
- -
isOwner:true,
- -
- -
createSetter:true,
- -
- -
isCascading:false,
- -
- -
supportsStringKey:true,
- -
- -
supportsHashKey:true,
- -
supportsCompositeKey:true,
- -
- +
supportsLeftAndRightKey:true,
/**@lends patio.Dataset.prototype*/
- +
/**
* Class that is used for querying/retirving datasets from a database.
- -
*
- -
*Method to call to look up association,
- -
*called after the model has been filtered
- -
**/
- -
_fetchMethod:"all",
- -
- -
- -
constructor:function (options, patio, filter) {
- 55
-options = options || {};
- 55
-if (!options.model) {
- 0
-throw new Error("Model is required for " + this.type + " association");
- -
}
- 55
-this._model = options.model;
- 55
-this.patio = patio;
- 55
-this.__opts = options;
- 55
-!isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
- 55
-this.filter = filter;
- 55
-this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
- 55
-this.__hooks =
- -
{before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
- 55
-var hooks = ["Add", "Remove", "Set", "Load"];
- 55
-["before", "after"].forEach(function (h) {
- 110
-hooks.forEach(function (a) {
- 440
-var hookName = h + a, hook;
- 440
-if (isFunction((hook = options[hookName]))) {
- 0
-this.__hooks[h][a.toLowerCase()] = hook;
- -
}
- -
}, this);
- -
}, this);
- 55
-this.fetchType = options.fetchType || fetch.LAZY;
- -
},
- -
- -
_callHook:function (hook, action, args) {
- 0
-var func = this.__hooks[hook][action], ret;
- 0
-if (isFunction(func)) {
- 0
-ret = func.apply(this, args);
- -
}
- 0
-return ret;
- -
},
- -
- -
_clearAssociations:function (model) {
- 125
-if (!this.readOnly) {
- 125
-delete model.__associations[this.name];
- -
}
- -
},
- -
- -
_forceReloadAssociations:function (model) {
- 243
-if (!this.readOnly) {
- 243
-delete model.__associations[this.name];
- 243
-return model[this.name];
- -
}
- -
},
- -
- -
/**
- -
* @return {Boolean} true if the association is eager.
- -
*/
- -
isEager:function () {
- 2114
-return this.fetchType == fetch.EAGER;
- -
},
- -
- -
_checkAssociationKey:function (parent) {
- 785
-var q = {};
- 785
-this._setAssociationKeys(parent, q);
- 785
-return Object.keys(q).every(function (k) {
- 785
-return q[k] != null
- -
});
- -
},
- -
- -
_getAssociationKey:function () {
- 5205
-var options = this.__opts, key, ret = [], lk, rk;
- 5205
-if (!isUndefinedOrNull((key = options.key))) {
- 722
-if (this.supportsStringKey && isString(key)) {
- -
//normalize the key first!
- 356
-ret = [
- -
[this.isOwner ? this.defaultLeftKey : key],
- -
[this.isOwner ? key : this.defaultRightKey]
- -
];
- 366
-} else if (this.supportsHashKey && isHash(key)) {
- 366
-var leftKey = Object.keys(key)[0];
- 366
-var rightKey = key[leftKey];
- 366
-ret = [
- -
[leftKey],
- -
[rightKey]
- -
];
- 0
-} else if (this.supportsCompositeKey && isArray(key)) {
- 0
-ret = [
- -
[key],
- -
null
- -
];
- -
}
- 4483
-} else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
- -
&& !isUndefinedOrNull((rk = options.rightKey)))) {
- 140
-ret = [
- -
array.toArray(lk), array.toArray(rk)
- -
];
- -
} else {
- -
//todo handle composite primary keys
- 4343
-ret = [
- -
[this.defaultLeftKey],
- -
[this.defaultRightKey]
- -
];
- -
}
- 5205
-return ret;
- -
},
- -
- -
- -
_setAssociationKeys:function (parent, model, val) {
- 1573
-var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
- 1573
-if (leftKey && rightKey) {
- 1573
-for (var i = 0; i < leftKey.length; i++) {
- 1573
-model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
- -
}
- -
} else {
- 0
-leftKey.forEach(function (k) {
- 0
-model[k] = !isUndefined(val) ? val : parent[k];
- -
});
- -
}
- -
},
- -
- -
_setDatasetOptions:function (ds) {
- 974
-var options = this.__opts || {};
- 974
-var order, limit, distinct, select, query;
- -
//allow for multi key ordering
- 974
-if (!isUndefined((select = this.select))) {
- 0
-ds = ds.select.apply(ds, array.toArray(select));
- -
}
- 974
-if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
- 0
-ds = ds.filter(query);
- -
}
- 974
-if (isFunction(this.filter)) {
- 334
-var ret = this.filter.apply(this, [ds]);
- 334
-if (isInstanceOf(ret, ds._static)) {
- 334
-ds = ret;
- -
}
- -
}
- 974
-if (!isUndefined((distinct = options.distinct))) {
- 0
-ds = ds.limit.apply(ds, array.toArray(distinct));
- -
}
- 974
-if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
- 0
-ds = ds.order.apply(ds, array.toArray(order));
- -
}
- 974
-if (!isUndefined((limit = options.limit))) {
- 0
-ds = ds.limit.apply(ds, array.toArray(limit));
- -
}
- 974
-return ds;
- -
- -
},
- -
- +
/**
- +
* <p> Dynamically genertated methods include
- +
* <ul>
- +
* <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
- +
* {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
- +
* to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
- +
* {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
- +
* <ul>
- +
* <li>Conditioned join types that accept conditions.
- +
* <ul>
- +
* <li>inner - INNER JOIN</li>
- +
* <li>fullOuter - FULL OUTER</li>
- +
* <li>rightOuter - RIGHT OUTER JOIN</li>
- +
* <li>leftOuter - LEFT OUTER JOIN</li>
- +
* <li>full - FULL JOIN</li>
- +
* <li>right - RIGHT JOIN</li>
- +
* <li>left - LEFT JOIN</li>
- +
* </ul>
- +
* </li>
- +
* <li>Unconditioned join types that do not accept join conditions
- +
* <ul>
- +
* <li>natural - NATURAL JOIN</li>
- +
* <li>naturalLeft - NATURAL LEFT JOIN</li>
- +
* <li>naturalRight - NATURAL RIGHT JOIN</li>
- +
* <li>naturalFull - NATURA FULLL JOIN</li>
- +
* <li>cross - CROSS JOIN</li>
- +
* </ul>
- +
* </li>
- +
* </ul>
- +
* </li>
- +
* </li>
- +
* </ul>
- +
*
- +
* <p>
- +
* <h4>Features:</h4>
- +
* <p>
- +
* Features that a particular {@link patio.Dataset} supports are shown in the example below.
- +
* If you wish to implement an adapter please override these values depending on the database that
- +
* you are developing the adapter for.
- +
* </p>
- +
* <pre class="code">
- +
* var ds = DB.from("test");
- +
*
- +
* //The default values returned
- +
*
- +
* //Whether this dataset quotes identifiers.
- +
* //Whether this dataset quotes identifiers.
- +
* ds.quoteIdentifiers //=>true
- +
*
- +
* //Whether this dataset will provide accurate number of rows matched for
- +
* //delete and update statements. Accurate in this case is the number of
- +
* //rows matched by the dataset's filter.
- +
* ds.providesAccurateRowsMatched; //=>true
- +
*
- +
* //Times Whether the dataset requires SQL standard datetimes (false by default,
- +
* // as most allow strings with ISO 8601 format).
- +
* ds.requiresSqlStandardDate; //=>false
- +
*
- +
* //Whether the dataset supports common table expressions (the WITH clause).
- +
* ds.supportsCte; //=>true
- +
*
- +
* //Whether the dataset supports the DISTINCT ON clause, false by default.
- +
* ds.supportsDistinctOn; //=>false
- +
*
- +
* //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
- +
* ds.supportsIntersectExcept; //=>true
- +
*
- +
* //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
- +
* ds.supportsIntersectExceptAll; //=>true
- +
*
- +
* //Whether the dataset supports the IS TRUE syntax.
- +
* ds.supportsIsTrue; //=>true
- +
*
- +
* //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
- +
* ds.supportsJoinUsing; //=>true
- +
*
- +
* //Whether modifying joined datasets is supported.
- +
* ds.supportsModifyingJoin; //=>false
- +
*
- +
* //Whether the IN/NOT IN operators support multiple columns when an
- +
* ds.supportsMultipleColumnIn; //=>true
- +
*
- +
* //Whether the dataset supports timezones in literal timestamps
- +
* ds.supportsTimestampTimezone; //=>false
- +
*
- +
* //Whether the dataset supports fractional seconds in literal timestamps
- +
* ds.supportsTimestampUsecs; //=>true
- +
*
- +
* //Whether the dataset supports window functions.
- +
* ds.supportsWindowFunctions; //=>false
- +
* </pre>
- +
* <p>
- +
* <p>
- +
* <h4>Actions</h4>
- +
* <p>
- +
* Each dataset does not actually send any query to the database until an action method has
- +
* been called upon it(with the exception of {@link patio.Dataset#graph} because columns
- +
* from the other table might need retrived in order to set up the graph). Each action
- +
* returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
- +
* that you account for errors otherwise it can be difficult to track down issues.
- +
* The list of action methods is:
- +
* <ul>
- +
* <li>{@link patio.Dataset#all}</li>
- +
* <li>{@link patio.Dataset#one}</li>
- +
* <li>{@link patio.Dataset#avg}</li>
- +
* <li>{@link patio.Dataset#count}</li>
- +
* <li>{@link patio.Dataset#columns}</li>
- +
* <li>{@link patio.Dataset#remove}</li>
- +
* <li>{@link patio.Dataset#forEach}</li>
- +
* <li>{@link patio.Dataset#empty}</li>
- +
* <li>{@link patio.Dataset#first}</li>
- +
* <li>{@link patio.Dataset#get}</li>
- +
* <li>{@link patio.Dataset#import}</li>
- +
* <li>{@link patio.Dataset#insert}</li>
- +
* <li>{@link patio.Dataset#save}</li>
- +
* <li>{@link patio.Dataset#insertMultiple}</li>
- +
* <li>{@link patio.Dataset#saveMultiple}</li>
- +
* <li>{@link patio.Dataset#interval}</li>
- +
* <li>{@link patio.Dataset#last}</li>
- +
* <li>{@link patio.Dataset#map}</li>
- +
* <li>{@link patio.Dataset#max}</li>
- +
* <li>{@link patio.Dataset#min}</li>
- +
* <li>{@link patio.Dataset#multiInsert}</li>
- +
* <li>{@link patio.Dataset#range}</li>
- +
* <li>{@link patio.Dataset#selectHash}</li>
- +
* <li>{@link patio.Dataset#selectMap}</li>
- +
* <li>{@link patio.Dataset#selectOrderMap}</li>
- +
* <li>{@link patio.Dataset#set}</li>
- +
* <li>{@link patio.Dataset#singleRecord}</li>
- +
* <li>{@link patio.Dataset#singleValue}</li>
- +
* <li>{@link patio.Dataset#sum}</li>
- +
* <li>{@link patio.Dataset#toCsv}</li>
- +
* <li>{@link patio.Dataset#toHash}</li>
- +
* <li>{@link patio.Dataset#truncate}</li>
- +
* <li>{@link patio.Dataset#update}</li>
- +
* </ul>
- +
*
- +
* </p>
- +
* </p>
- +
*
- +
* @constructs
- +
*
- +
*
- +
* @param {patio.Database} db the database this dataset should use when querying for data.
- +
* @param {Object} opts options to set on this dataset instance
- +
*
- +
* @property {Function} rowCb callback to be invoked for each row returned from the database.
- +
* the return value will be used as the result of query. The rowCb can also return a promise,
- +
* The resolved value of the promise will be used as result.
- +
*
- +
* @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
- +
* This value will be defaulted to whatever the identifierInputMethod
- +
* is on the database used in initialization.
- +
*
- +
* @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
- +
* This value will be defaulted to whatever the identifierOutputMethod
- +
* is on the database used in initialization.
- +
* @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
- +
* throws a {patio.DatasetError} tf the dataset doesn't have a table.
- +
* <pre class="code">
- +
* DB.from("table").firstSourceAlias;
- +
* //=> "table"
- +
*
- +
* DB.from("table___t").firstSourceAlias;
- +
* //=> "t"
- +
* </pre>
- +
*
- +
* @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
- +
* have a table, raises a {@link patio.erros.DatasetError}.
- +
*<pre class="code">
- +
*
- +
* DB.from("table").firstSourceTable;
- +
* //=> "table"
- +
*
- +
* DB.from("table___t").firstSourceTable;
- +
* //=> "t"
- +
* </pre>
- +
* @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
- +
* <pre class="code">
- +
* DB.from("items").isSimpleSelectAll; //=> true
- +
* DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
- +
* </pre>
- +
* @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
- +
* @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
- +
* delete and update statements. Accurate in this case is the number of
- +
* rows matched by the dataset's filter.
- +
* @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
- +
* as most allow strings with ISO 8601 format).
- +
* @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
- +
* @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
- +
* @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
- +
* @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
- +
* @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
- +
* @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
- +
* @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
- +
* @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
- +
* @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
- +
* @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
- +
* @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
- +
* @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset.
- +
* @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources
- +
* @property {Boolean} hasSelectSource true if this dataset already has a select sources.
- +
*/
- +
constructor:function (db, opts) {
- 29216
+this._super(arguments);
- 29216
+this.db = db;
- 29216
+this.__opts = {};
- 29216
+this.__rowCb = null;
- 29216
+if (db) {
- 15039
+this.__quoteIdentifiers = db.quoteIdentifiers;
- 15039
+this.__identifierInputMethod = db.identifierInputMethod;
- 15039
+this.__identifierOutputMethod = db.identifierOutputMethod;
- +
}
- +
},
- +
- +
- +
/**
- +
* Returns a new clone of the dataset with with the given options merged into the current datasets options.
- +
* If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
- +
* columns are deleted. This method should generally not be called
- +
* directly by user code.
- +
*
- +
* @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
- +
* @return [patio.Dataset] a cloned dataset with the merged options
- +
**/
- +
mergeOptions:function (opts) {
- 14948
+opts = isUndefined(opts) ? {} : opts;
- 14948
+var ds = new this._static(this.db, {});
- 14948
+ds.rowCb = this.rowCb;
- 14948
+this._static.FEATURES.forEach(function (f) {
- 209272
+ds[f] = this[f];
- +
}, this);
- 14948
+ds.__opts = merge({}, this.__opts, opts);
- 14948
+ds.identifierInputMethod = this.identifierInputMethod;
- 14948
+ds.identifierOutputMethod = this.identifierOutputMethod;
- 14948
+var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
- 14948
+if (Object.keys(opts).some(function (o) {
- 13536
+return columnChangeOpts.indexOf(o) != -1;
- +
})) {
- 2456
+ds.__opts.columns = null;
- +
}
- 14948
+return ds;
- +
},
- +
- +
- +
/**
- +
* Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
- +
* or {@link patio.sql.AliasedExpression}, depending on the format:
- +
*
- +
* <ul>
- +
* <li>For columns : table__column___alias.</li>
- +
* <li>For tables : schema__table___alias.</li>
- +
* </ul>
- +
* each portion of the identifier is optional. See example below
- +
*
- +
* @example
- +
*
- +
* ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
- +
* ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
- +
* ds.stringToIdentifier("table__column___alias");
- +
* //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
- +
*
- +
* @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
- +
* or {@link patio.sql.AliasedExpression}.
- +
*
- +
* @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
- +
*/
- +
stringToIdentifier:function (name) {
- 15093
+if (isString(name)) {
- 10138
+var parts = this._splitString(name);
- 10138
+var schema = parts[0], table = parts[1], alias = parts[2];
- 10138
+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 {
- 4955
+return name;
- +
}
- +
},
- +
- +
/**
- +
* Can either be a string or null.
- +
*
- +
*
- +
* @example
- +
* //columns
- +
* table__column___alias //=> table.column as alias
- +
* table__column //=> table.column
- +
* //tables
- +
* schema__table___alias //=> schema.table as alias
- +
* schema__table //=> schema.table
- +
*
- +
* //name and alias
- +
* columnOrTable___alias //=> columnOrTable as alias
- +
*
- +
*
- +
*
- +
* @return {String[]} an array with the elements being:
- +
* <ul>
- +
* <li>For columns :[table, column, alias].</li>
- +
* <li>For tables : [schema, table, alias].</li>
- +
* </ul>
- +
*/
- +
_splitString:function (s) {
- 19662
+var ret, m;
- 19662
+if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
- 189
+ret = m.slice(1);
- +
}
- 19473
+else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
- 28
+ret = [null, m[1], m[2]];
- +
}
- 19445
+else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
- 2079
+ret = [m[1], m[2], null];
- +
}
- +
else {
- 17366
+ret = [null, s, null];
- +
}
- 19662
+return ret;
- +
},
- +
- +
/**
- +
* @ignore
- +
**/
- +
getters:{
- +
- +
rowCb:function () {
- 23052
+return this.__rowCb;
- +
},
- +
- +
identifierInputMethod:function () {
- 14948
+return this.__identifierInputMethod;
- +
},
- +
- +
identifierOutputMethod:function () {
- 14948
+return this.__identifierOutputMethod;
- +
},
- +
- +
firstSourceAlias:function () {
- 579
+var source = this.__opts.from;
- 579
+if (isUndefinedOrNull(source) || !source.length) {
- 2
+throw new DatasetError("No source specified for the query");
- +
}
- 577
+source = source[0];
- 577
+if (isInstanceOf(source, AliasedExpression)) {
- 20
+return source.alias;
- 557
+} else if (isString(source)) {
- 0
+var parts = this._splitString(source);
- 0
+var alias = parts[2];
- 0
+return alias ? alias : source;
- +
} else {
- 557
+return source;
- +
}
- +
},
- +
- +
firstSourceTable:function () {
- 15
+var source = this.__opts.from;
- 15
+if (isUndefinedOrNull(source) || !source.length) {
- 1
+throw new QueryError("No source specified for the query");
- +
}
- 14
+var source = source[0];
- 14
+if (isInstanceOf(source, AliasedExpression)) {
- 3
+return source.expression;
- 11
+} else if (isString(source)) {
- 0
+var parts = this._splitString(source);
- 0
+return source;
- +
} else {
- 11
+return source;
- +
}
- +
},
- +
- +
sourceList:function () {
- 0
+return (this.__opts.from || []).map(this.stringToIdentifier, this);
- +
},
- +
- +
joinSourceList:function () {
- 0
+return (this.__opts.join || []).map(function (join) {
- 0
+return this.stringToIdentifier(join.tableAlias || join.table);
- +
}, this);
- +
},
- +
- +
hasSelectSource:function () {
- 0
+var select = this.__opts.select;
- 0
+return !(isUndefinedOrNull(select) || select.length === 0);
- +
}
- +
},
- +
- +
/**
- +
* @ignore
- +
**/
- +
setters:{
- +
/**@lends patio.Dataset.prototype*/
- +
- +
identifierInputMethod:function (meth) {
- 15038
+this.__identifierInputMethod = meth;
- +
},
- +
- +
identifierOutputMethod:function (meth) {
- 15038
+this.__identifierOutputMethod = meth;
- +
},
- +
- +
rowCb:function (cb) {
- 18564
+if (isFunction(cb) || isNull(cb)) {
- 18559
+this.__rowCb = cb;
- +
} else {
- 5
+throw new DatasetError("rowCb mus be a function");
- +
}
- +
}
- +
}
- +
},
- +
- +
static:{
- +
COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
- +
COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
- +
COLUMN_REF_RE3:/^(\w+)__(\w+)$/
- +
}
- +
}).as(module);
- +
+ associations/_Association.js
+ |
+
+
+ Coverage87.18
+ SLOC515
+ LOC156
+ Missed20
+
+ |
+
- 1
+var comb = require("comb-proxy"),
- +
define = comb.define,
- +
isUndefined = comb.isUndefined,
- +
isUndefinedOrNull = comb.isUndefinedOrNull,
- +
isBoolean = comb.isBoolean,
- +
isString = comb.isString,
- +
isHash = comb.isHash,
- +
isFunction = comb.isFunction,
- +
isInstanceOf = comb.isInstanceOf,
- +
Promise = comb.Promise,
- +
PromiseList = comb.PromiseList,
- +
hitch = comb.hitch,
- +
array = comb.array,
- +
isArray = comb.isArray,
- +
Middleware = comb.plugins.Middleware,
- +
PatioError = require("../errors").PatioError;
- +
- 1
+var fetch = {
- +
LAZY:"lazy",
- +
EAGER:"eager"
- +
};
- +
- +
- +
/**
- +
* @class
- +
* Base class for all associations.
- +
*
- +
* </br>
- +
* <b>NOT to be instantiated directly</b>
- +
* Its just documented for reference.
- +
*
- +
* @constructs
- +
* @param {Object} options
- +
* @param {String} options.model a string to look up the model that we are associated with
- +
* @param {Function} options.filter a callback to find association if a filter is defined then
- +
* the association is read only
- +
* @param {Object} options.key object with left key and right key
- +
* @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
- +
* @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
- +
* the associations are automatically filled, if fetch.Lazy is supplied
- +
* then a promise is returned and is called back with the loaded models
- +
* @property {Model} model the model associatied with this association.
- +
* @name Association
- +
* @memberOf patio.associations
- +
* */
- 1
+define(Middleware, {
- +
instance:{
- +
/**@lends patio.associations.Association.prototype*/
- +
- +
type:"",
- +
- +
//Our associated model
- +
_model:null,
- +
- +
/**
- +
* Fetch type
- +
*/
- +
fetchType:fetch.LAZY,
- +
- +
/**how to order our association*/
- +
orderBy:null,
- +
- +
/**Our filter method*/
- +
filter:null,
- +
- +
__hooks:null,
- +
- +
isOwner:true,
- +
- +
createSetter:true,
- +
- +
isCascading:false,
- +
- +
supportsStringKey:true,
- +
- +
supportsHashKey:true,
- +
- +
supportsCompositeKey:true,
- +
- +
supportsLeftAndRightKey:true,
- +
- +
/**
- +
*
- +
*Method to call to look up association,
- +
*called after the model has been filtered
- +
**/
- +
_fetchMethod:"all",
- +
- +
- +
constructor:function (options, patio, filter) {
- 55
+options = options || {};
- 55
+if (!options.model) {
- 0
+throw new Error("Model is required for " + this.type + " association");
- +
}
- 55
+this._model = options.model;
- 55
+this.patio = patio;
- 55
+this.__opts = options;
- 55
+!isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
- 55
+this.filter = filter;
- 55
+this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
- 55
+this.__hooks =
- +
{before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
- 55
+var hooks = ["Add", "Remove", "Set", "Load"];
- 55
+["before", "after"].forEach(function (h) {
- 110
+hooks.forEach(function (a) {
- 440
+var hookName = h + a, hook;
- 440
+if (isFunction((hook = options[hookName]))) {
- 0
+this.__hooks[h][a.toLowerCase()] = hook;
- +
}
- +
}, this);
- +
}, this);
- 55
+this.fetchType = options.fetchType || fetch.LAZY;
- +
},
- +
- +
_callHook:function (hook, action, args) {
- 0
+var func = this.__hooks[hook][action], ret;
- 0
+if (isFunction(func)) {
- 0
+ret = func.apply(this, args);
- +
}
- 0
+return ret;
- +
},
- +
- +
_clearAssociations:function (model) {
- 125
+if (!this.readOnly) {
- 125
+delete model.__associations[this.name];
- +
}
- +
},
- +
- +
_forceReloadAssociations:function (model) {
- 243
+if (!this.readOnly) {
- 243
+delete model.__associations[this.name];
- 243
+return model[this.name];
- +
}
- +
},
- +
- +
/**
- +
* @return {Boolean} true if the association is eager.
- +
*/
- +
isEager:function () {
- 2114
+return this.fetchType == fetch.EAGER;
- +
},
- +
- +
_checkAssociationKey:function (parent) {
- 785
+var q = {};
- 785
+this._setAssociationKeys(parent, q);
- 785
+return Object.keys(q).every(function (k) {
- 785
+return q[k] != null
- +
});
- +
},
- +
- +
_getAssociationKey:function () {
- 5205
+var options = this.__opts, key, ret = [], lk, rk;
- 5205
+if (!isUndefinedOrNull((key = options.key))) {
- 722
+if (this.supportsStringKey && isString(key)) {
- +
//normalize the key first!
- 356
+ret = [
- +
[this.isOwner ? this.defaultLeftKey : key],
- +
[this.isOwner ? key : this.defaultRightKey]
- +
];
- 366
+} else if (this.supportsHashKey && isHash(key)) {
- 366
+var leftKey = Object.keys(key)[0];
- 366
+var rightKey = key[leftKey];
- 366
+ret = [
- +
[leftKey],
- +
[rightKey]
- +
];
- 0
+} else if (this.supportsCompositeKey && isArray(key)) {
- 0
+ret = [
- +
[key],
- +
null
- +
];
- +
}
- 4483
+} else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
- +
&& !isUndefinedOrNull((rk = options.rightKey)))) {
- 140
+ret = [
- +
array.toArray(lk), array.toArray(rk)
- +
];
- +
} else {
- +
//todo handle composite primary keys
- 4343
+ret = [
- +
[this.defaultLeftKey],
- +
[this.defaultRightKey]
- +
];
- +
}
- 5205
+return ret;
- +
},
- +
- +
- +
_setAssociationKeys:function (parent, model, val) {
- 1573
+var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
- 1573
+if (leftKey && rightKey) {
- 1573
+for (var i = 0; i < leftKey.length; i++) {
- 1573
+model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
- +
}
- +
} else {
- 0
+leftKey.forEach(function (k) {
- 0
+model[k] = !isUndefined(val) ? val : parent[k];
- +
});
- +
}
- +
},
- +
- +
_setDatasetOptions:function (ds) {
- 974
+var options = this.__opts || {};
- 974
+var order, limit, distinct, select, query;
- +
//allow for multi key ordering
- 974
+if (!isUndefined((select = this.select))) {
- 0
+ds = ds.select.apply(ds, array.toArray(select));
- +
}
- 974
+if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
- 0
+ds = ds.filter(query);
- +
}
- 974
+if (isFunction(this.filter)) {
- 334
+var ret = this.filter.apply(this, [ds]);
- 334
+if (isInstanceOf(ret, ds._static)) {
- 334
+ds = ret;
- +
}
- +
}
- 974
+if (!isUndefined((distinct = options.distinct))) {
- 0
+ds = ds.limit.apply(ds, array.toArray(distinct));
- +
}
- 974
+if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
- 0
+ds = ds.order.apply(ds, array.toArray(order));
- +
}
- 974
+if (!isUndefined((limit = options.limit))) {
- 0
+ds = ds.limit.apply(ds, array.toArray(limit));
- +
}
- 974
+return ds;
- +
- +
},
- +
/**
*Filters our associated dataset to load our association.
*
- @@ -6274,9 +6751,9 @@
*@return {Dataset} the dataset with all filters applied.
@@ -6474,24 +6951,24 @@ - Coverage88.36 - SLOC1061 - LOC318 + Coverage88.47 + SLOC1066 + LOC321 Missed37* @borrows patio.Dataset#leftJoin as leftJoin
* */
- -
constructor:function (options, fromDb) {
- 3218
-if (this.synced) {
- 3218
-this.__emitter = new EventEmitter();
- 3218
-this._super(arguments);
- 3218
-this.patio = patio || require("./index");
- 3218
-fromDb = isBoolean(fromDb) ? fromDb : false;
- 3218
-this.__changed = {};
- 3218
-this.__values = {};
- 3218
-if (fromDb) {
- 1753
-this._hook("pre", "load");
- 1753
-this.__isNew = false;
- 1753
-this.__setFromDb(options, true);
- 1753
-if (this._static.emitOnLoad) {
- 1753
-this.emit("load", this);
- 1753
+this._static.emit("load", this);
- 3107
+if (this.synced) {
- 3107
+this.__emitter = new EventEmitter();
- 3107
+this._super(arguments);
- 3107
+this.patio = patio || require("./index");
- 3107
+fromDb = isBoolean(fromDb) ? fromDb : false;
- 3107
+this.__changed = {};
- 3107
+this.__values = {};
- 3107
+if (fromDb) {
- 1754
+this._hook("pre", "load");
- 1754
+this.__isNew = false;
- 1754
+this.__setFromDb(options, true);
- 1754
+if (this._static.emitOnLoad) {
- 1754
+this.emit("load", this);
- 1754
this._static.emit("load", this);
}
- -
} else {
- 1465
-this.__isNew = true;
- 1465
+this.__set(options);
- 1353
+this.__isNew = true;
- 1353
this.__set(options);
}
} else {
- 0
@@ -6499,26 +6976,31 @@throw new ModelError("Model " + this.tableName + " has not been synced");
},
- -
__set:function (values, ignore) {
- 1587
-values = values || {};
- 1587
-this.__ignore = ignore === true;
- 1587
+Object.keys(values).forEach(function (attribute) {
- 1475
+values = values || {};
- 1475
+this.__ignore = ignore === true;
- 1475
Object.keys(values).forEach(function (attribute) {
- 6632
var value = values[attribute];
//check if the column is a constrained value and is allowed to be set
- 6632
!ignore && this._checkIfColumnIsConstrained(attribute);
- 6632
this[attribute] = value;
- -
}, this);
- 1587
+this.__ignore = false;
- 1475
this.__ignore = false;
},
- -
__setFromDb:function (values, ignore) {
- 3056
-values = values || {};
- 3056
-this.__ignore = ignore === true;
- 3056
-Object.keys(values).forEach(function (column) {
- 25828
+var value = values[column];
- 3057
+values = values || {};
- 3057
+this.__ignore = ignore === true;
- 3057
+var schema = this.schema;
- 3057
+Object.keys(values).forEach(function (column) {
- 25830
var value = values[column];
- -
// Typecast value retrieved from db
- 25828
+this.__values[column] = this._typeCastValue(column, value, ignore);
- 25830
+if (schema.hasOwnProperty(column)) {
- 25149
+this.__values[column] = this._typeCastValue(column, value, ignore);
- +
} else {
- 681
+this[column] = value;
}
- -
}, this);
- 3056
+this.__ignore = false;
- 3057
this.__ignore = false;
},
- @@ -6576,11 +7058,11 @@
},
- -
_getColumnValue:function (name) {
- 5426
-var val = this.__values[name];
- 5426
-var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
- 5426
+var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
- 5427
+var val = this.__values[name];
- 5427
+var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
- 5427
var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
- -
- 5426
+return columnValue;
- 5427
return columnValue;
},
- @@ -6600,25 +7082,25 @@
_setColumnValue:function (name, val) {
//typecast_value method, so database adapters can override/augment the handling
//for database specific column types.
- -
_typeCastValue:function (column, value, fromDatabase) {
- 33384
-var colSchema, clazz = this._static;
- 33384
-if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
- 32704
-var type = colSchema.type;
- 32704
+if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
- 32705
+var colSchema, clazz = this._static;
- 32705
+if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
- 32705
+var type = colSchema.type;
- 32705
if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
- 3
value = null;
- -
}
- 32704
-var raiseOnError = clazz.raiseOnTypecastError;
- 32704
+if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
- 32705
+var raiseOnError = clazz.raiseOnTypecastError;
- 32705
if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
- 0
throw new ModelError("null is not allowed for the " + column + " column.");
- -
}
- 32704
-try {
- 32704
+value = clazz.db.typecastValue(type, value);
- 32705
+try {
- 32705
value = clazz.db.typecastValue(type, value);
} catch (e) {
- 0
if (raiseOnError === true) {
- 0
throw e;
}
}
- -
}
- 33384
+return value;
- 32705
return value;
},
- @@ -6690,8 +7172,8 @@
/**
- 0
return emitter.listeners.apply(emitter, arguments);
},
- -
emit:function () {
- 11119
-var emitter = this.__emitter;
- 11119
+return emitter.emit.apply(emitter, arguments);
- 11120
+var emitter = this.__emitter;
- 11120
return emitter.emit.apply(emitter, arguments);
},
- @@ -6732,7 +7214,7 @@
},
- -
schema:function () {
- 73400
+return this._static.schema;
- 75099
return this._static.schema;
},
- @@ -6740,7 +7222,7 @@
columns:function () {
},
- -
synced:function () {
- 12891
+return this._static.synced;
- 12780
return this._static.synced;
}
- @@ -7049,7 +7531,7 @@
}
_defineColumnGetter:function (name) {
- 779
-this.prototype.__defineGetter__(name, function () {
- 5426
+return this._getColumnValue(name);
- 5427
return this._getColumnValue(name);
});
},
- @@ -7105,14 +7587,14 @@
* @type patio.Database
*/
- -
db:function () {
- 46373
-var db = this.__db;
- 46373
+if (!db) {
- 46374
+var db = this.__db;
- 46374
if (!db) {
- 86
db = this.__db = patio.defaultDatabase;
- -
}
- 46373
+if (!db) {
- 46374
if (!db) {
- 0
throw new ModelError("patio has not been connected to a database");
- -
}
- 46373
+return db;
- 46374
return db;
},
- @@ -7159,8 +7641,8 @@
/**
* @type Object
*/
- -
schema:function () {
- 73404
-if (this.synced) {
- 73404
+return this.__schema;
- 75103
+if (this.synced) {
- 75103
return this.__schema;
} else {
- 0
throw new ModelError("Model has not been synced yet");
- @@ -9060,21 +9542,21 @@
}
- 1
-var virtualRow = function (name) {
- 1069
-var WILDCARD = new LiteralString('*');
- 1069
-var QUESTION_MARK = new LiteralString('?');
- 1069
-var COMMA_SEPARATOR = new LiteralString(', ');
- 1069
-var DOUBLE_UNDERSCORE = '__';
- -
- 1069
-var parts = name.split(DOUBLE_UNDERSCORE);
- 1069
-var table = parts[0], column = parts[1];
- 1069
-var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
- 1069
+var prox = methodMissing(ident, function (m) {
- 875
+var WILDCARD = new LiteralString('*');
- 875
+var QUESTION_MARK = new LiteralString('?');
- 875
+var COMMA_SEPARATOR = new LiteralString(', ');
- 875
+var DOUBLE_UNDERSCORE = '__';
- +
- 875
+var parts = name.split(DOUBLE_UNDERSCORE);
- 875
+var table = parts[0], column = parts[1];
- 875
+var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
- 875
var prox = methodMissing(ident, function (m) {
- 4
return function () {
- 3
var args = argsToArray(arguments);
- 3
return SQLFunction.fromArgs([m, name].concat(args));
}
- -
}, column ? QualifiedIdentifier : Identifier);
- 1069
+var ret = createFunctionWrapper(prox, function (m) {
- 875
var ret = createFunctionWrapper(prox, function (m) {
- 548
var args = argsToArray(arguments);
- 548
if (args.length) {
- 542
@@ -9084,8 +9566,8 @@return SQLFunction.fromArgs([name].concat(args));
}, function () {
- 0
return SQLFunction.fromArgs(arguments);
- -
});
- 1069
-ret.__proto__ = ident;
- 1069
+return ret;
- 875
+ret.__proto__ = ident;
- 875
return ret;
};
- 1
@@ -9472,3395 +9954,2938 @@var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
- 442
return args.length > 1 ? PlaceHolderLiteralString.fromArgs(args) : new LiteralString(s);
},
- -
- -
/**
- -
* Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.
- -
*
- -
* @example
- -
*
- -
* sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)
- -
*
- -
*/
- -
"case":function (hash, /*args**/opts) {
- 2
-var args = argsToArray(arguments, 1);
- 2
-return CaseExpression.fromArgs([hashToArray(hash)].concat(args));
- -
},
- -
- -
/**
- -
* Creates a {@link patio.sql.StringExpression}
- -
*
- -
* Return a {@link patio.sql.StringExpression} representing an SQL string made up of the
- -
* concatenation of this array's elements. If an joiner is passed
- -
* it is used in between each element of the array in the SQL
- -
* concatenation.
- -
*
- -
* @example
- -
* patio.sql.sqlStringJoin(["a"]); //=> a
- -
* //you can use sql.* as a shortcut to get an identifier
- -
* patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b
- -
* patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'
- -
* patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b
- -
*/
- -
sqlStringJoin:function (arr, joiner) {
- 6
-joiner = joiner || null;
- 6
-var args;
- 6
-arr = arr.map(function (a) {
- 12
-return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)
- -
});
- 6
-if (joiner) {
- 4
-var newJoiner = [];
- 4
-for (var i = 0; i < arr.length; i++) {
- 9
-newJoiner.push(joiner);
- -
}
- 4
-args = array.flatten(array.zip(arr, newJoiner));
- 4
-args.pop();
- -
} else {
- 2
-args = arr;
- -
}
- 6
-args = args.map(function (a) {
- 17
-return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;
- -
});
- 6
-return StringExpression.fromArgs(["||"].concat(args));
- -
},
- -
- -
Year:Year,
- -
TimeStamp:TimeStamp,
- -
Time:Time,
- -
DateTime:DateTime,
- -
Float:Float,
- -
Decimal:Decimal
- -
- -
};
- -
- 1
-sql.__defineGetter__("patio", function () {
- 0
-!patio && (patio = require("./index"));
- 0
-return patio;
- -
});
- -
- 1
-exports.sql = methodMissing(sql, function (name) {
- 1069
-return virtualRow(name);
- -
});
- -
- 1
-var OPERTATOR_INVERSIONS = {
- -
AND:"OR",
- -
OR:"AND",
- -
GT:"lte",
- -
GTE:"lt",
- -
LT:"gte",
- -
LTE:"gt",
- -
EQ:"neq",
- -
NEQ:"eq",
- -
LIKE:'NOT LIKE',
- -
"NOT LIKE":"LIKE",
- -
'!~*':'~*',
- -
'~*':'!~*',
- -
"~":'!~',
- -
"IN":'NOTIN',
- -
"NOTIN":"IN",
- -
"IS":'IS NOT',
- -
"ISNOT":"IS",
- -
NOT:"NOOP",
- -
NOOP:"NOT",
- -
ILIKE:'NOT ILIKE',
- -
NOTILIKE:"ILIKE"
- -
};
- -
- -
// Standard mathematical operators used in +NumericMethods+
- 1
-var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};
- -
- -
// Bitwise mathematical operators used in +NumericMethods+
- 1
-var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};
- -
- -
- 1
-var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};
- -
- -
//Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+
- 1
-var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};
- -
- -
//Operators that use IN/NOT IN for inclusion/exclusion
- 1
-var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};
- -
- -
//Operators that use IS, used for special casing to override literal true/false values
- 1
-var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};
- -
- -
//Operator symbols that take exactly two arguments
- 1
-var TWO_ARITY_OPERATORS = merge({
- -
EQ:'=',
- -
NEQ:'!=', LIKE:"LIKE",
- -
"NOT LIKE":'NOT LIKE',
- -
ILIKE:"ILIKE",
- -
"NOT ILIKE":'NOT ILIKE',
- -
"~":"~",
- -
'!~':"!~",
- -
'~*':"~*",
- -
'!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);
- -
- -
//Operator symbols that take one or more arguments
- 1
-var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);
- -
- -
//Operator symbols that take only a single argument
- 1
-var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};
- -
- -
/**
- -
* @class Mixin to provide alias methods to an expression.
- -
*
- -
* @name AliasMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var AliasMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.AliasMethods.prototype*/
- -
- -
/**
- -
* Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression
- -
* to the given alias.
- -
*
- -
* @example
- -
*
- -
* sql.identifier("column").as("alias");
- -
* //=> "column" AS "alias"
- -
*
- -
* @param {String} alias the alias to assign to the expression.
- -
*
- -
* @return {patio.sql.AliasedExpression} the aliased expression.
- -
*/
- -
as:function (alias) {
- 552
-return new AliasedExpression(this, alias);
- -
}
- -
- -
}
- -
}).as(sql, "AliasMethods");
- -
- 1
-var bitWiseMethod = function (op) {
- 5
-return function (expression) {
- 0
-if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
- -
}
- -
else {
- 0
-return new BooleanExpression(op, this, expression);
- -
}
- -
}
- -
};
- -
- -
/**
- -
* @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These
- -
* methods are only on {@link patio.sql.NumericExpression}
- -
*
- -
* @example
- -
* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
- -
* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
- -
* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
- -
* sql.a.sqlNumber.leftShift("b") // "a" << "b"
- -
* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
- -
*
- -
* @name BitWiseMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var BitWiseMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.BitWiseMethods.prototype*/
- -
- -
/**
- -
* Bitwise and
- -
*
- -
* @example
- -
* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
- -
*/
- -
bitWiseAnd:bitWiseMethod("bitWiseAnd"),
- -
- -
/**
- -
* Bitwise or
- -
*
- -
* @example
- -
* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
- -
*/
- -
bitWiseOr:bitWiseMethod("bitWiseOr"),
- -
- -
/**
- -
* Exclusive Or
- -
*
- -
* @example
- -
*
- -
* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
- -
*/
- -
exclusiveOr:bitWiseMethod("exclusiveOr"),
- -
- -
/**
- -
* Bitwise shift left
- -
*
- -
* @example
- -
*
- -
* sql.a.sqlNumber.leftShift("b") // "a" << "b"
- -
*/
- -
leftShift:bitWiseMethod("leftShift"),
- -
- -
/**
- -
* Bitwise shift right
- -
*
- -
* @example
- -
*
- -
* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
- -
*/
- -
rightShift:bitWiseMethod("rightShift")
- -
}
- +
}).as(sql, "BitWiseMethods");
- +
/**
- +
* Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.
- +
*
- +
* @example
- +
*
- +
* sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)
- +
*
- +
*/
- +
"case":function (hash, /*args**/opts) {
- 2
+var args = argsToArray(arguments, 1);
- 2
+return CaseExpression.fromArgs([hashToArray(hash)].concat(args));
},
- -
- 1
-var booleanMethod = function (op) {
- 2
-return function (expression) {
- 7
-if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");
- -
}
- -
else {
- 7
+return new BooleanExpression(op, this, expression);
- +
/**
- +
* Creates a {@link patio.sql.StringExpression}
- +
*
- +
* Return a {@link patio.sql.StringExpression} representing an SQL string made up of the
- +
* concatenation of this array's elements. If an joiner is passed
- +
* it is used in between each element of the array in the SQL
- +
* concatenation.
- +
*
- +
* @example
- +
* patio.sql.sqlStringJoin(["a"]); //=> a
- +
* //you can use sql.* as a shortcut to get an identifier
- +
* patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b
- +
* patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'
- +
* patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b
- +
*/
- +
sqlStringJoin:function (arr, joiner) {
- 6
+joiner = joiner || null;
- 6
+var args;
- 6
+arr = arr.map(function (a) {
- 12
+return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)
- +
});
- 6
+if (joiner) {
- 4
+var newJoiner = [];
- 4
+for (var i = 0; i < arr.length; i++) {
- 9
+newJoiner.push(joiner);
- +
}
- 4
+args = array.flatten(array.zip(arr, newJoiner));
- 4
+args.pop();
- +
} else {
- 2
args = arr;
- -
}
- -
}
- -
};
- -
- -
/**
- -
* @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators
- -
* that are defined on objects that can be used in a boolean context in SQL
- -
* ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).
- -
*
- -
* @example
- -
* sql.a.and(sql.b) //=> "a" AND "b"
- -
* sql.a.or(sql.b) //=> "a" OR "b"
- -
* sql.a.not() //=> NOT "a"
- -
*
- -
* @name BooleanMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var BooleanMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.BooleanMethods.prototype*/
- -
- -
/**
- -
*
- -
* @function
- -
* Logical AND
- -
*
- -
* @example
- -
*
- -
* sql.a.and(sql.b) //=> "a" AND "b"
- -
*
- -
* @return {patio.sql.BooleanExpression} a ANDed boolean expression.
- -
*/
- +
and:booleanMethod("and"),
- 6
+args = args.map(function (a) {
- 17
+return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;
- +
});
- 6
+return StringExpression.fromArgs(["||"].concat(args));
},
- -
- -
/**
- -
* @function
- -
* Logical OR
- -
*
- -
* @example
- -
*
- -
* sql.a.or(sql.b) //=> "a" OR "b"
- -
*
- -
* @return {patio.sql.BooleanExpression} a ORed boolean expression
- -
*/
- +
or:booleanMethod("or"),
- +
Year:Year,
- +
TimeStamp:TimeStamp,
- +
Time:Time,
- +
DateTime:DateTime,
- +
Float:Float,
Decimal:Decimal
- -
- -
/**
- -
* Logical NOT
- -
*
- -
* @example
- -
*
- -
* sql.a.not() //=> NOT "a"
- -
*
- -
* @return {patio.sql.BooleanExpression} a inverted boolean expression.
- -
*/
- -
not:function () {
- 5
-return BooleanExpression.invert(this);
- +
}
};
- -
- -
}
- +
}).as(sql, "BooleanMethods");
- 1
+sql.__defineGetter__("patio", function () {
- 0
+!patio && (patio = require("./index"));
- 0
+return patio;
});
- -
- -
/**
- -
* @class Defines case methods
- -
*
- -
* @name CastMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var CastMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.CastMethods.prototype*/
- -
/**
- -
* Cast the reciever to the given SQL type.
- -
*
- -
* @example
- -
*
- -
* sql.a.cast("integer") //=> CAST(a AS integer)
- -
* sql.a.cast(String) //=> CAST(a AS varchar(255))
- -
*
- -
* @return {patio.sql.Cast} the casted expression
- -
*/
- -
cast:function (type) {
- 2
-return new Cast(this, type);
- +
},
- 1
+exports.sql = methodMissing(sql, function (name) {
- 875
+return virtualRow(name);
});
- -
- -
/**
- -
* Cast the reciever to the given SQL type (or the database's default Number type if none given.
- -
*
- -
* @example
- -
*
- -
* sql.a.castNumeric() //=> CAST(a AS integer)
- -
* sql.a.castNumeric("double") //=> CAST(a AS double precision)
- -
*
- -
* @param type the numeric type to cast to
- -
*
- -
* @return {patio.sql.NumericExpression} a casted numberic expression
- -
*/
- -
castNumeric:function (type) {
- 0
-return this.cast(type || "integer").sqlNumber;
- +
},
- 1
+var OPERTATOR_INVERSIONS = {
- +
AND:"OR",
- +
OR:"AND",
- +
GT:"lte",
- +
GTE:"lt",
- +
LT:"gte",
- +
LTE:"gt",
- +
EQ:"neq",
- +
NEQ:"eq",
- +
LIKE:'NOT LIKE',
- +
"NOT LIKE":"LIKE",
- +
'!~*':'~*',
- +
'~*':'!~*',
- +
"~":'!~',
- +
"IN":'NOTIN',
- +
"NOTIN":"IN",
- +
"IS":'IS NOT',
- +
"ISNOT":"IS",
- +
NOT:"NOOP",
- +
NOOP:"NOT",
- +
ILIKE:'NOT ILIKE',
- +
NOTILIKE:"ILIKE"
};
- -
- -
/**
- -
* Cast the reciever to the given SQL type (or the database's default String type if none given),
- -
* and return the result as a {@link patio.sql.StringExpression}.
- -
*
- -
* @example
- -
*
- -
* sql.a.castString() //=> CAST(a AS varchar(255))
- -
* sql.a.castString("text") //=> CAST(a AS text)
- -
* @param type the string type to cast to
- -
*
- -
* @return {patio.sql.StringExpression} the casted string expression
- -
*/
- -
castString:function (type) {
- 0
-return this.cast(type || String).sqlString;
- -
}
- -
}
- +
}).as(sql, "CastMethods");
- +
// Standard mathematical operators used in +NumericMethods+
- 1
var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};
- +
- +
// Bitwise mathematical operators used in +NumericMethods+
- 1
var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};
- -
- -
/**
- -
* @class Provides methods to assist in assigning a SQL type to
- -
* particular types, i.e. Boolean, Function, Number or String.
- -
*
- -
* @name ComplexExpressionMethods
- -
* @memberOf patio.sql
- -
* @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.
- -
* @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.
- -
* @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.
- -
* <pre class="code">
- -
* sql.a.not("a") //=> NOT "a"
- -
* sql.a.sqlNumber.not() //=> ~"a"
- -
* </pre>
- -
* @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.
- -
* <pre class="code">
- -
* sql.a.plus(sql.b); //=> "a" + "b"
- -
* sql.a.sqlString.concat(sql.b) //=> "a" || "b"
- -
* </pre>
- -
*/
- 1
-var ComplexExpressionMethods = define(null, {
- -
instance:{
- -
/**@ignore*/
getters:{
- -
- -
/**
- -
* @ignore
- -
*/
- -
sqlBoolean:function () {
- 0
-return new BooleanExpression("noop", this);
- +
},
- 1
var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};
- +
- +
//Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+
- 1
var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};
- -
- -
/**
- -
* @ignore
- -
*/
- -
sqlFunction:function () {
- 13
-return new SQLFunction(this);
- +
},
- +
//Operators that use IN/NOT IN for inclusion/exclusion
- 1
var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};
- +
- +
//Operators that use IS, used for special casing to override literal true/false values
- 1
var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};
- -
- -
/**
- -
* @ignore
- -
*/
- -
sqlNumber:function () {
- 50
-return new NumericExpression("noop", this);
- +
},
- +
//Operator symbols that take exactly two arguments
- 1
+var TWO_ARITY_OPERATORS = merge({
- +
EQ:'=',
- +
NEQ:'!=', LIKE:"LIKE",
- +
"NOT LIKE":'NOT LIKE',
- +
ILIKE:"ILIKE",
- +
"NOT ILIKE":'NOT ILIKE',
- +
"~":"~",
- +
'!~':"!~",
- +
'~*':"~*",
'!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);
- -
- -
/**
- -
* @ignore
- -
*/
- -
sqlString:function () {
- 0
-return new StringExpression("noop", this);
- -
}
- -
}
- -
}
- +
}).as(sql, "ComplexExpressionMethods");
- +
//Operator symbols that take one or more arguments
- 1
var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);
- -
- 1
-var inequalityMethod = function (op) {
- 6
-return function (expression) {
- 88
-if (isInstanceOf(expression, BooleanExpression)
- -
|| isBoolean(expression)
- -
|| isNull(expression)
- -
|| (isHash(expression))
- -
|| isArray(expression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
- -
} else {
- 88
-return new BooleanExpression(op, this, expression);
- -
}
- -
}
- +
};
- +
//Operator symbols that take only a single argument
- 1
var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};
- -
/**
- -
* @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
- -
* used in a numeric or string context in SQL.
- -
*
- -
* @example
- -
* sql.a.gt("b") //=> a > "b"
- -
* sql.a.lt("b") //=> a > "b"
- -
* sql.a.gte("b") //=> a >= "b"
- -
* sql.a.lte("b") //=> a <= "b"
- +
* sql.a.eq("b") //=> a = "b"
* @class Mixin to provide alias methods to an expression.
- -
*
- +
* @name InequalityMethods
* @name AliasMethods
* @memberOf patio.sql
- -
*/
- 1
+var InequalityMethods = define(null, {
- 1
var AliasMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.InequalityMethods.prototype*/
- -
- -
/**
- -
* @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.
- -
* @example
- -
*
- -
* sql.a.gt("b") //=> a > "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
gt:inequalityMethod("gt"),
- -
/**
- -
* @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- -
* @example
- -
*
- -
* sql.a.gte("b") //=> a >= "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
gte:inequalityMethod("gte"),
- -
/**
- -
* @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- -
* @example
- -
*
- -
* sql.a.lt("b") //=> a < "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
lt:inequalityMethod("lt"),
- -
/**
- -
* @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- -
* @example
- -
*
- -
* sql.a.lte("b") //=> a <= "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
lte:inequalityMethod("lte"),
- -
/**
- -
* @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- -
* @example
- -
*
- -
* sql.a.eq("b") //=> a = "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
eq:inequalityMethod("eq"),
- -
- +
neq:inequalityMethod("neq"),
/**@lends patio.sql.AliasMethods.prototype*/
- -
/**
- -
* @private
- -
*
- +
* Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
- +
* Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression
* to the given alias.
*
* @example
- -
*
- -
* sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))
- +
* sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))
- +
* sql.identifier("column").as("alias");
* //=> "column" AS "alias"
- -
*
- +
* @param {Object} items a two element array where the first element it the item to be gte and the second item lte.
* @param {String} alias the alias to assign to the expression.
- -
*
- +
* @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
* @return {patio.sql.AliasedExpression} the aliased expression.
- -
*/
- -
between:function (items) {
- 6
+return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))
- +
as:function (alias) {
- 552
return new AliasedExpression(this, alias);
- -
}
- -
}
}).as(sql, "InequalityMethods");
- -
- -
/**
- -
* @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},
- -
* so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}
- -
* or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>
- -
*
- -
* @name NoBooleanInputMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var NoBooleanInputMethods = define(null, {
- -
instance:{
- -
constructor:function (op) {
- 22
-var args = argsToArray(arguments, 1);
- 22
-args.forEach(function (expression) {
- 26
-if ((isInstanceOf(expression, BooleanExpression))
- -
|| isBoolean(expression)
- -
|| isNull(expression)
- -
|| (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))
- -
|| isArray(expression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
- -
}
- -
});
- 22
-this._super(arguments);
}
- -
}
- +
}).as(sql, "NoBooleanInputMethods");
}).as(sql, "AliasMethods");
- -
- 1
-var numericMethod = function (op) {
- 4
-return function (expression) {
- 12
+if (isInstanceOf(expression, BooleanExpression, StringExpression)) {
- 1
+var bitWiseMethod = function (op) {
- 5
+return function (expression) {
- 0
if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
- -
} else {
- 12
+return new NumericExpression(op, this, expression);
- +
}
- +
else {
- 0
return new BooleanExpression(op, this, expression);
}
}
};
- -
- -
/**
- -
* @class This mixin includes the standard mathematical methods (+, -, *, and /)
- +
* that are defined on objects that can be used in a numeric context in SQL.
- +
* @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These
* methods are only on {@link patio.sql.NumericExpression}
*
- -
* @example
- -
* sql.a.plus(sql.b) //=> "a" + "b"
- -
* sql.a.minus(sql.b) //=> "a" - "b"
- -
* sql.a.multiply(sql.b) //=> "a" * "b"
- +
* sql.a.divide(sql.b) //=> "a" / "b"
- +
* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
- +
* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
- +
* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
- +
* sql.a.sqlNumber.leftShift("b") // "a" << "b"
* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
- -
*
- +
* @name NumericMethods
* @name BitWiseMethods
* @memberOf patio.sql
- -
*/
- 1
+var NumericMethods = define(null, {
- 1
var BitWiseMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.NumericMethods.prototype*/
- -
- -
- -
/**
- -
* @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @example
- -
*
- -
* sql.a.plus(sql.b) //=> "a" + "b"
- -
*
- -
* @return {patio.sql.NumericExpression}
- -
*/
- +
plus:numericMethod("plus"),
/**@lends patio.sql.BitWiseMethods.prototype*/
- -
/**
- +
* @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.
* Bitwise and
*
- -
* @example
- -
*
- -
* sql.a.minus(sql.b) //=> "a" - "b"
- -
*
- +
* @return {patio.sql.NumericExpression}
* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
- -
*/
- +
minus:numericMethod("minus"),
bitWiseAnd:bitWiseMethod("bitWiseAnd"),
- -
/**
- +
* @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
* Bitwise or
*
- -
* @example
- -
*
- -
* sql.a.divide(sql.b) //=> "a" / "b"
- -
*
- +
* @return {patio.sql.NumericExpression}
* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
- -
*/
- +
divide:numericMethod("divide"),
bitWiseOr:bitWiseMethod("bitWiseOr"),
- -
/**
- +
* @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
* Exclusive Or
*
* @example
- -
*
- -
* sql.a.multiply(sql.b) //=> "a" * "b"
- -
*
- +
* @return {patio.sql.NumericExpression}
* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
- -
*/
- -
multiply:numericMethod("multiply")
- -
}
- -
}).as(sql, "NumericMethods");
- -
- -
- -
/**
- -
* @class This mixin provides ordering methods ("asc", "desc") to expression.
- -
*
- -
* @example
- -
*
- -
* sql.name.asc(); //=> name ASC
- -
* sql.price.desc(); //=> price DESC
- -
* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
- -
* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
- -
*
- -
* @name OrderedMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var OrderedMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.OrderedMethods.prototype*/
exclusiveOr:bitWiseMethod("exclusiveOr"),
- -
/**
- +
* Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
* Bitwise shift left
- -
*
- -
* @example
- -
* sql.name.asc(); //=> name ASC
- +
* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
* @example
- -
*
- -
* @param {Object} [options] options to use when sorting
- -
* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
- -
* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
- +
* @return {patio.sql.OrderedExpression}
* sql.a.sqlNumber.leftShift("b") // "a" << "b"
- -
*/
- -
asc:function (options) {
- 7
-return new OrderedExpression(this, false, options);
- +
},
leftShift:bitWiseMethod("leftShift"),
- -
/**
- -
* Mark the receiving SQL column as sorting in a descending fashion.
- +
* @example
* Bitwise shift right
- -
*
- -
* sql.price.desc(); //=> price DESC
- +
* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
* @example
- -
*
- -
* @param {Object} [options] options to use when sorting
- -
* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
- -
* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
- +
* @return {patio.sql.OrderedExpression}
* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
- -
*/
- -
desc:function (options) {
- 26
-return new OrderedExpression(this, true, options);
- +
}
rightShift:bitWiseMethod("rightShift")
- -
}
- +
}).as(sql, "OrderedMethods");
}).as(sql, "BitWiseMethods");
- +
- 1
+var booleanMethod = function (op) {
- 2
+return function (expression) {
- 7
+if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
- 0
+throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");
- +
}
- +
else {
- 7
+return new BooleanExpression(op, this, expression);
- +
}
- +
}
};
- -
/**
- +
* @class This mixin provides methods related to qualifying expression.
- +
* @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators
- +
* that are defined on objects that can be used in a boolean context in SQL
* ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).
*
- +
* @example
- +
* sql.a.and(sql.b) //=> "a" AND "b"
- +
* sql.a.or(sql.b) //=> "a" OR "b"
* sql.a.not() //=> NOT "a"
- -
*
- -
* sql.column.qualify("table") //=> "table"."column"
- -
* sql.table.qualify("schema") //=> "schema"."table"
- -
* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
- -
*
- +
* @name QualifyingMethods
* @name BooleanMethods
* @memberOf patio.sql
- -
*/
- 1
+var QualifyingMethods = define(null, {
- 1
var BooleanMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.QualifyingMethods.prototype*/
/**@lends patio.sql.BooleanMethods.prototype*/
- -
/**
- +
* Qualify the receiver with the given qualifier (table for column/schema for table).
- +
*
- +
* @function
* Logical AND
*
- -
* @example
- -
* sql.column.qualify("table") //=> "table"."column"
- -
* sql.table.qualify("schema") //=> "schema"."table"
* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
- -
*
- +
* @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.
* sql.a.and(sql.b) //=> "a" AND "b"
- -
*
- +
* @return {patio.sql.QualifiedIdentifier}
* @return {patio.sql.BooleanExpression} a ANDed boolean expression.
- -
*/
- -
qualify:function (qualifier) {
- 576
-return new QualifiedIdentifier(qualifier, this);
- +
},
and:booleanMethod("and"),
- -
/**
- +
* Use to create a .* expression.
- +
* @function
* Logical OR
*
- -
* @example
- -
* sql.table.all() //=> "table".*
* sql.table.qualify("schema").all() //=> "schema"."table".*
- +
*
* sql.a.or(sql.b) //=> "a" OR "b"
- -
*
- +
* @return {patio.sql.ColumnAll}
* @return {patio.sql.BooleanExpression} a ORed boolean expression
- -
*/
- -
all:function () {
- 3
-return new ColumnAll(this);
- +
}
or:booleanMethod("or"),
- +
- +
/**
- +
* Logical NOT
- +
*
- +
* @example
- +
*
- +
* sql.a.not() //=> NOT "a"
- +
*
- +
* @return {patio.sql.BooleanExpression} a inverted boolean expression.
- +
*/
- +
not:function () {
- 5
+return BooleanExpression.invert(this);
}
- -
}
- -
}).as(sql, "QualifyingMethods");
- +
}).as(sql, "BooleanMethods");
- -
/**
- -
* @class This mixin provides SQL string methods such as (like and iLike).
- -
*
- -
* @example
- -
*
- -
* sql.a.like("A%"); //=> "a" LIKE 'A%'
- -
* sql.a.iLike("A%"); //=> "a" LIKE 'A%'
- +
* sql.a.like(/^a/); //=> "a" ~* '^a'
* @class Defines case methods
- -
*
- +
* @name StringMethods
* @name CastMethods
* @memberOf patio.sql
- -
*/
- 1
+var StringMethods = define(null, {
- 1
var CastMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.StringMethods.prototype*/
- +
/**@lends patio.sql.CastMethods.prototype*/
- -
/**
- -
* Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver
- +
* with the given patterns. See {@link patio.sql.StringExpression#like}.
* Cast the reciever to the given SQL type.
*
- -
* @example
* sql.a.iLike("A%"); //=> "a" LIKE 'A%'
- -
*
- +
* @return {patio.sql.BooleanExpression}
- +
* sql.a.cast("integer") //=> CAST(a AS integer)
- +
* sql.a.cast(String) //=> CAST(a AS varchar(255))
- +
*
* @return {patio.sql.Cast} the casted expression
- -
*/
- -
ilike:function (expression) {
- 310
-expression = argsToArray(arguments);
- 310
-return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([
- -
{caseInsensitive:true}
- +
]));
- +
cast:function (type) {
- 2
return new Cast(this, type);
},
- -
/**
- -
* Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with
- +
* the given patterns. See {@link patio.sql.StringExpression#like}.
* Cast the reciever to the given SQL type (or the database's default Number type if none given.
*
- -
* @example
- -
* sql.a.like(/^a/); //=> "a" ~* '^a'
* sql.a.like("A%"); //=> "a" LIKE 'A%'
- -
*
- +
* @param expression
- +
* sql.a.castNumeric() //=> CAST(a AS integer)
- +
* sql.a.castNumeric("double") //=> CAST(a AS double precision)
- +
*
- +
* @param type the numeric type to cast to
- +
*
* @return {patio.sql.NumericExpression} a casted numberic expression
- -
*/
- -
like:function (expression) {
- 11
-expression = argsToArray(arguments);
- 11
-return StringExpression.like.apply(StringExpression, [this].concat(expression));
- -
}
- -
}
- -
}).as(sql, "StringMethods");
- -
- -
/**
- -
* @class This mixin provides string concatenation methods ("concat");
- -
*
- -
* @example
- -
*
- -
* sql.x.sqlString.concat("y"); //=> "x" || "y"
- -
*
- -
* @name StringConcatenationMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var StringConcatenationMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.StringConcatenationMethods.prototype*/
- +
castNumeric:function (type) {
- 0
+return this.cast(type || "integer").sqlNumber;
},
- -
/**
- -
* Return a {@link patio.sql.StringExpression} representing the concatenation of this expression
- +
* with the given argument.
- +
* Cast the reciever to the given SQL type (or the database's default String type if none given),
* and return the result as a {@link patio.sql.StringExpression}.
*
* @example
- -
*
- +
* sql.x.sqlString.concat("y"); //=> "x" || "y"
- +
* sql.a.castString() //=> CAST(a AS varchar(255))
- +
* sql.a.castString("text") //=> CAST(a AS text)
* @param type the string type to cast to
- -
*
- +
* @param expression expression to concatenate this expression with.
* @return {patio.sql.StringExpression} the casted string expression
- -
*/
- -
concat:function (expression) {
- 0
+return new StringExpression("||", this, expression);
- +
castString:function (type) {
- 0
return this.cast(type || String).sqlString;
}
- -
}
- +
}).as(sql, "StringConcatenationMethods");
- +
}).as(sql, "CastMethods");
- -
/**
- -
* @class This mixin provides the ability to access elements within a SQL array.
- -
*
- -
* @example
- -
* sql.array.sqlSubscript(1) //=> array[1]
- -
* sql.array.sqlSubscript(1, 2) //=> array[1, 2]
- +
* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
- +
* @class Provides methods to assist in assigning a SQL type to
* particular types, i.e. Boolean, Function, Number or String.
- -
*
- +
* @name SubscriptMethods
* @name ComplexExpressionMethods
- +
* @memberOf patio.sql
- +
* @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.
- +
* @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.
- +
* @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.
- +
* <pre class="code">
- +
* sql.a.not("a") //=> NOT "a"
- +
* sql.a.sqlNumber.not() //=> ~"a"
- +
* </pre>
- +
* @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.
- +
* <pre class="code">
- +
* sql.a.plus(sql.b); //=> "a" + "b"
- +
* sql.a.sqlString.concat(sql.b) //=> "a" || "b"
* </pre>
- -
*/
- 1
+var SubscriptMethods = define(null, {
- 1
var ComplexExpressionMethods = define(null, {
- +
instance:{
- +
/**@ignore*/
getters:{
- -
- -
/**
- -
* Return a {@link patio.sql.Subscript} with the given arguments, representing an
- -
* SQL array access.
- -
*
- -
* @example
- -
* sql.array.sqlSubscript(1) //=> array[1]
- -
* sql.array.sqlSubscript(1, 2) //=> array[1, 2]
- -
* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
- -
*
- -
* @param subscript
- -
*/
- -
sqlSubscript:function (subscript) {
- 108
-var args = argsToArray(arguments);
- 108
+return new SubScript(this, flatten(args));
- +
/**
- +
* @ignore
- +
*/
- +
sqlBoolean:function () {
- 0
+return new BooleanExpression("noop", this);
- +
},
- +
- +
- +
/**
- +
* @ignore
- +
*/
- +
sqlFunction:function () {
- 13
+return new SQLFunction(this);
- +
},
- +
- +
- +
/**
- +
* @ignore
- +
*/
- +
sqlNumber:function () {
- 50
+return new NumericExpression("noop", this);
- +
},
- +
- +
/**
- +
* @ignore
- +
*/
- +
sqlString:function () {
- 0
+return new StringExpression("noop", this);
}
}
- -
}
- +
}).as(sql, "SubScriptMethods");
}).as(sql, "ComplexExpressionMethods");
- +
- 1
+var inequalityMethod = function (op) {
- 6
+return function (expression) {
- 88
+if (isInstanceOf(expression, BooleanExpression)
- +
|| isBoolean(expression)
- +
|| isNull(expression)
- +
|| (isHash(expression))
- +
|| isArray(expression)) {
- 0
+throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
- +
} else {
- 88
+return new BooleanExpression(op, this, expression);
- +
}
- +
}
};
- -
/**
- +
* @class This is the parent of all expressions.
- +
* @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
* used in a numeric or string context in SQL.
- -
*
- +
* @name Expression
- +
* @example
- +
* sql.a.gt("b") //=> a > "b"
- +
* sql.a.lt("b") //=> a > "b"
- +
* sql.a.gte("b") //=> a >= "b"
- +
* sql.a.lte("b") //=> a <= "b"
- +
* sql.a.eq("b") //=> a = "b"
- +
*
* @name InequalityMethods
* @memberOf patio.sql
- -
*/
- 1
-var Expression = define(null, {
- +
- 1
var InequalityMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.Expression.prototype*/
/**@lends patio.sql.InequalityMethods.prototype*/
- -
/**
- +
* Returns the string representation of this expression
- +
* @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.
* @example
- -
*
- -
* @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.
- +
* @return {String} a string literal version of this expression.
- +
* sql.a.gt("b") //=> a > "b"
- +
*
* @return {patio.sql.BooleanExpression}
- -
*/
- -
sqlLiteral:function (ds) {
- 0
-return this.toString(ds);
- -
}
- -
- -
},
- -
- -
static:{
- -
/**@lends patio.sql.Expression*/
- +
gt:inequalityMethod("gt"),
- -
/**
- +
* This is a helper method that will take in an array of arguments and return an expression.
* @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.
*
* @example
- -
*
- +
* QualifiedIdentifier.fromArgs(["table", "column"]);
* sql.a.gte("b") //=> a >= "b"
- -
*
- +
* @param {*[]} args array of arguments to pass into the constructor of the function.
- +
* @return {patio.sql.BooleanExpression}
- +
*/
- +
gte:inequalityMethod("gte"),
- +
/**
* @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- +
* @return {patio.sql.Expression} an expression.
- +
* @example
- +
*
- +
* sql.a.lt("b") //=> a < "b"
- +
*
* @return {patio.sql.BooleanExpression}
- -
*/
- -
fromArgs:function (args) {
- 2410
-var ret;
- 2410
-try {
- 2410
-ret = new this();
- -
} catch (ignore) {
- -
}
- 2410
-this.apply(ret, args);
- 2410
-return ret;
- +
},
- +
lt:inequalityMethod("lt"),
- +
/**
- +
* @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.
- +
*
- +
* @example
- +
*
- +
* sql.a.lte("b") //=> a <= "b"
- +
*
- +
* @return {patio.sql.BooleanExpression}
- +
*/
- +
lte:inequalityMethod("lte"),
- +
/**
- +
* @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.
- +
*
- +
* @example
- +
*
- +
* sql.a.eq("b") //=> a = "b"
- +
*
- +
* @return {patio.sql.BooleanExpression}
- +
*/
- +
eq:inequalityMethod("eq"),
- +
neq:inequalityMethod("neq"),
- -
/**
- -
* Helper to determine if something is a condition specifier. Returns true if the object
- +
* is a Hash or is an array of two element arrays.
- +
* @private
- +
*
* Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
*
- -
* @example
- -
* Expression.isConditionSpecifier({a : "b"}); //=> true
- -
* Expression.isConditionSpecifier("a"); //=> false
- -
* Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true
* Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false
- -
*
- -
* @param {*} obj object to test if it is a condition specifier
- +
* @return {Boolean} true if the object is a Hash or is an array of two element arrays.
- +
* sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))
- +
* sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))
- +
*
- +
* @param {Object} items a two element array where the first element it the item to be gte and the second item lte.
- +
*
* @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
- -
*/
- -
isConditionSpecifier:function (obj) {
- 22715
-return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
- 9896
-return isArray(i) && i.length === 2;
- +
}));
- +
between:function (items) {
- 6
return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))
}
- -
}
- -
- +
}).as(sql, "Expression");
}).as(sql, "InequalityMethods");
- -
/**
- -
* @class Base class for all GenericExpressions
- -
*
- -
* @augments patio.sql.Expression
- -
* @augments patio.sql.AliasMethods
- -
* @augments patio.sql.BooleanMethods
- -
* @augments patio.sql.CastMethods
- -
* @augments patio.sql.ComplexExpressionMethods
- -
* @augments patio.sql.InequalityMethods
- -
* @augments patio.sql.NumericMethods
- -
* @augments patio.sql.OrderedMethods
- -
* @augments patio.sql.StringMethods
- +
* @augments patio.sql.SubscriptMethods
- +
* @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},
- +
* so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}
* or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>
- -
*
- +
* @name GenericExpression
* @name NoBooleanInputMethods
* @memberOf patio.sql
- -
*/
- 1
-var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");
- -
- -
- 1
-var AliasedExpression = define(Expression, {
- -
instance:{
- -
/**@lends patio.sql.AliasedExpression.prototype*/
- -
- -
/**
- -
* This class reperesents an Aliased Expression
- -
*
- -
* @constructs
- -
* @augments patio.sql.Expression
- -
*
- -
* @param expression the expression to alias.
- -
* @param alias the alias to alias the expression to.
- -
*
- -
* @property expression the expression being aliased
- -
* @property alias the alias of the expression
- -
*
- -
*/
- -
constructor:function (expression, alias) {
- 945
-this.expression = expression;
- 945
-this.alias = alias;
- +
},
- 1
+var NoBooleanInputMethods = define(null, {
- +
instance:{
- +
constructor:function (op) {
- 22
+var args = argsToArray(arguments, 1);
- 22
+args.forEach(function (expression) {
- 26
+if ((isInstanceOf(expression, BooleanExpression))
- +
|| isBoolean(expression)
- +
|| isNull(expression)
- +
|| (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))
- +
|| isArray(expression)) {
- 0
+throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
- +
}
- +
});
- 22
+this._super(arguments);
- +
}
- +
}
}).as(sql, "NoBooleanInputMethods");
- -
- -
/**
- -
* Converts the aliased expression to a string
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- -
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- -
* @return String the SQL alias fragment.
- -
*/
- -
toString:function (ds) {
- 877
-!Dataset && (Dataset = require("./dataset"));
- 877
-ds = ds || new Dataset();
- 877
-return ds.aliasedExpressionSql(this);
- +
}
- 1
+var numericMethod = function (op) {
- 4
+return function (expression) {
- 12
+if (isInstanceOf(expression, BooleanExpression, StringExpression)) {
- 0
+throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
- +
} else {
- 12
return new NumericExpression(op, this, expression);
}
- -
}
- +
).as(sql, "AliasedExpression");
};
- -
- 1
+var CaseExpression = define(GenericExpression, {
- +
/**
- +
* @class This mixin includes the standard mathematical methods (+, -, *, and /)
- +
* that are defined on objects that can be used in a numeric context in SQL.
- +
*
- +
* @example
- +
* sql.a.plus(sql.b) //=> "a" + "b"
- +
* sql.a.minus(sql.b) //=> "a" - "b"
- +
* sql.a.multiply(sql.b) //=> "a" * "b"
- +
* sql.a.divide(sql.b) //=> "a" / "b"
- +
*
- +
* @name NumericMethods
- +
* @memberOf patio.sql
- +
*/
- 1
var NumericMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.CaseExpression.prototype*/
- +
/**@lends patio.sql.NumericMethods.prototype*/
- -
/**
- -
* Create an object with the given conditions and
- -
* default value. An expression can be provided to
- -
* test each condition against, instead of having
- +
* all conditions represent their own boolean expression.
* @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @constructs
- -
* @augments patio.sql.GenericExpression
- -
* @param {Array|Object} conditions conditions to create the case expression from
- -
* @param def default value
- +
* @param expression expression to create the CASE expression from
* @example
- -
*
- -
* @property {Boolean} hasExpression returns true if this case expression has a expression
- -
* @property conditions the conditions of the {@link patio.sql.CaseExpression}.
- -
* @property def the default value of the {@link patio.sql.CaseExpression}.
- -
* @property expression the expression of the {@link patio.sql.CaseExpression}.
- +
* @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.
- +
* sql.a.plus(sql.b) //=> "a" + "b"
- +
*
* @return {patio.sql.NumericExpression}
- -
*/
- -
constructor:function (conditions, def, expression) {
- 8
-if (Expression.isConditionSpecifier(conditions)) {
- 4
-this.conditions = toArray(conditions);
- 4
-this.def = def;
- 4
-this.expression = expression;
- 4
-this.noExpression = isUndefined(expression);
- -
}
- +
},
plus:numericMethod("plus"),
- -
/**
- -
* Converts the case expression to a string
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
* @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @return String the SQL case expression fragment.
- -
*/
- -
toString:function (ds) {
- 2
-!Dataset && (Dataset = require("./dataset"));
- 2
-ds = ds || new Dataset();
- 2
-return ds.caseExpressionSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
/**@ignore*/
- -
hasExpression:function () {
- 2
-return !this.noExpression;
- -
}
- -
}
- -
}
- -
}).as(sql, "CaseExpression");
- -
- -
- 1
-var Cast = define(GenericExpression, {
- -
instance:{
- +
/**@lends patio.sql.Cast*/
- +
* @example
- +
*
- +
* sql.a.minus(sql.b) //=> "a" - "b"
- +
*
- +
* @return {patio.sql.NumericExpression}
- +
*/
minus:numericMethod("minus"),
- -
/**
- -
* Represents a cast of an SQL expression to a specific type.
- -
* @constructs
- +
* @augments patio.sql.GenericExpression
* @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @param expr the expression to CAST.
- +
* @param type the type to CAST the expression to.
* @example
- -
*
- -
* @property expr the expression to CAST.
- +
* @property type the type to CAST the expression to.
- +
* sql.a.divide(sql.b) //=> "a" / "b"
- +
*
* @return {patio.sql.NumericExpression}
- -
*/
- -
constructor:function (expr, type) {
- 3
-this.expr = expr;
- 3
-this.type = type;
- +
},
divide:numericMethod("divide"),
- -
/**
- +
* Converts the cast expression to a string
* @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
* @example
- -
*
- +
* @return String the SQL cast expression fragment.
- +
* sql.a.multiply(sql.b) //=> "a" * "b"
- +
*
* @return {patio.sql.NumericExpression}
- -
*/
- -
toString:function (ds) {
- 2
-!Dataset && (Dataset = require("./dataset"));
- 2
-ds = ds || new Dataset();
- 2
-return ds.castSql(this.expr, this.type);
- +
}
multiply:numericMethod("multiply")
- -
}
- +
}).as(sql, "Cast");
}).as(sql, "NumericMethods");
- -
- 1
+var ColumnAll = define(Expression, {
- +
/**
- +
* @class This mixin provides ordering methods ("asc", "desc") to expression.
- +
*
- +
* @example
- +
*
- +
* sql.name.asc(); //=> name ASC
- +
* sql.price.desc(); //=> price DESC
- +
* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
- +
* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
- +
*
- +
* @name OrderedMethods
- +
* @memberOf patio.sql
- +
*/
- 1
var OrderedMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.ColumnAll.prototype*/
/**@lends patio.sql.OrderedMethods.prototype*/
- -
/**
- -
* Represents all columns in a given table, table.* in SQL
- -
* @constructs
- -
*
- +
* @augments patio.sql.Expression
* Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
- -
*
- +
* @param table the table this expression is for.
- +
* @example
- +
* sql.name.asc(); //=> name ASC
* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
- -
*
- +
* @property table the table this all column expression represents.
- +
* @param {Object} [options] options to use when sorting
- +
* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
- +
* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
* @return {patio.sql.OrderedExpression}
- -
*/
- -
constructor:function (table) {
- 20
+this.table = table;
- +
asc:function (options) {
- 7
return new OrderedExpression(this, false, options);
},
- -
/**
- +
* Converts the ColumnAll expression to a string
- +
* Mark the receiving SQL column as sorting in a descending fashion.
* @example
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* sql.price.desc(); //=> price DESC
* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
- -
*
- +
* @return String the SQL columnAll expression fragment.
- +
* @param {Object} [options] options to use when sorting
- +
* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
- +
* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
* @return {patio.sql.OrderedExpression}
- -
*/
- -
toString:function (ds) {
- 19
-!Dataset && (Dataset = require("./dataset"));
- 19
-ds = ds || new Dataset();
- 19
+return ds.columnAllSql(this);
- +
desc:function (options) {
- 26
return new OrderedExpression(this, true, options);
}
- -
}
- +
}).as(sql, "ColumnAll");
}).as(sql, "OrderedMethods");
- -
- 1
+var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {
- +
- +
/**
- +
* @class This mixin provides methods related to qualifying expression.
- +
*
- +
* @example
- +
*
- +
* sql.column.qualify("table") //=> "table"."column"
- +
* sql.table.qualify("schema") //=> "schema"."table"
- +
* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
- +
*
- +
* @name QualifyingMethods
- +
* @memberOf patio.sql
- +
*/
- 1
var QualifyingMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.ComplexExpression.prototype*/
/**@lends patio.sql.QualifyingMethods.prototype*/
- -
/**
- -
* Represents a complex SQL expression, with a given operator and one
- -
* or more attributes (which may also be ComplexExpressions, forming
- -
* a tree).
- -
*
- -
* This is an abstract class that is not that useful by itself. The
- -
* subclasses @link patio.sql.BooleanExpression},
- -
* {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should
- +
* be used instead of this class directly.
* Qualify the receiver with the given qualifier (table for column/schema for table).
- -
*
- -
* @constructs
- -
* @augments patio.sql.Expression
- -
* @augments patio.sql.AliasMethods
- -
* @augments patio.sql.CastMethods
- -
* @augments patio.sql.OrderedMethods
- +
* @augments patio.sql.SubscriptMethods
- +
* @example
- +
* sql.column.qualify("table") //=> "table"."column"
- +
* sql.table.qualify("schema") //=> "schema"."table"
* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
- -
*
- -
* @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.
- +
* @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.
* @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.
- -
*
- -
* @param {...} op The operator and arguments for this object to the ones given.
- -
* <p>
- -
* Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,
- -
* other than the second arg for an IN/NOT IN operator.</li>
- +
* </p>
* @return {patio.sql.QualifiedIdentifier}
- -
*/
- -
constructor:function (op) {
- 7952
-if (op) {
- 7164
-var args = argsToArray(arguments,1 );
- -
//make a copy of the args
- 7164
-var origArgs = args.slice(0);
- 7164
-args.forEach(function (a, i) {
- 14676
-if (Expression.isConditionSpecifier(a)) {
- 6
-args[i] = BooleanExpression.fromValuePairs(a);
- -
}
- -
});
- 7164
-op = op.toUpperCase();
- -
- 7164
-if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
- 1164
-if (args.length < 1) {
- 0
-throw new ExpressionError("The " + op + " operator requires at least 1 argument")
- -
}
- 1164
-var oldArgs = args.slice(0);
- 1164
-args = [];
- 1164
-oldArgs.forEach(function (a) {
- 2739
-a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
- -
});
- -
- 6000
-} else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
- 5937
-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.
- 5937
-if (IN_OPERATORS[op]) {
- 23
-args[1] = origArgs[1]
- -
}
- 63
-} else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
- 63
-if (args.length != 1) {
- 0
-throw new ExpressionError("The " + op + " operator requires only one argument");
- -
}
- -
} else {
- 0
-throw new ExpressionError("Invalid operator " + op);
- -
}
- 7164
-this.op = op;
- 7164
-this.args = args;
- +
}
- +
qualify:function (qualifier) {
- 576
return new QualifiedIdentifier(qualifier, this);
},
- -
/**
- +
* Converts the ComplexExpression to a string.
* Use to create a .* expression.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* @example
- +
* sql.table.all() //=> "table".*
* sql.table.qualify("schema").all() //=> "schema"."table".*
- -
*
- +
* @return String the SQL version of the {@link patio.sql.ComplexExpression}.
- +
*
* @return {patio.sql.ColumnAll}
- -
*/
- -
toString:function (ds) {
- 6761
-!Dataset && (Dataset = require("./dataset"));
- 6761
-ds = ds || new Dataset();
- 6761
+return ds.complexExpressionSql(this.op, this.args);
- +
all:function () {
- 3
return new ColumnAll(this);
- -
}
},
- -
- -
static:{
/**@lends patio.sql.ComplexExpression*/
- -
- -
/**
- -
* Hash of operator inversions
- -
* @type Object
- -
* @default {
- -
* AND:"OR",
- -
* OR:"AND",
- -
* GT:"lte",
- -
* GTE:"lt",
- -
* LT:"gte",
- -
* LTE:"gt",
- -
* EQ:"neq",
- -
* NEQ:"eq",
- -
* LIKE:'NOT LIKE',
- -
* "NOT LIKE":"LIKE",
- -
* '!~*':'~*',
- -
* '~*':'!~*',
- -
* "~":'!~',
- -
* "IN":'NOTIN',
- -
* "NOTIN":"IN",
- -
* "IS":'IS NOT',
- -
* "ISNOT":"IS",
- -
* NOT:"NOOP",
- -
* NOOP:"NOT",
- -
* ILIKE:'NOT ILIKE',
- -
* NOTILIKE:"ILIKE"
- -
* }
- -
*/
- +
OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,
- +
}
- +
}).as(sql, "QualifyingMethods");
- +
- +
- +
/**
- +
* @class This mixin provides SQL string methods such as (like and iLike).
- +
*
- +
* @example
- +
*
- +
* sql.a.like("A%"); //=> "a" LIKE 'A%'
- +
* sql.a.iLike("A%"); //=> "a" LIKE 'A%'
- +
* sql.a.like(/^a/); //=> "a" ~* '^a'
- +
*
- +
* @name StringMethods
- +
* @memberOf patio.sql
- +
*/
- 1
+var StringMethods = define(null, {
- +
instance:{
/**@lends patio.sql.StringMethods.prototype*/
- -
/**
- +
* Default mathematical operators.
- +
* Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver
- +
* with the given patterns. See {@link patio.sql.StringExpression#like}.
- +
*
- +
* @example
* sql.a.iLike("A%"); //=> "a" LIKE 'A%'
- -
*
- -
* @type Object
- +
* @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}
* @return {patio.sql.BooleanExpression}
- -
*/
- +
MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,
- +
ilike:function (expression) {
- 310
+expression = argsToArray(arguments);
- 310
+return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([
- +
{caseInsensitive:true}
- +
]));
},
- -
/**
- +
* Default bitwise operators.
- +
* Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with
* the given patterns. See {@link patio.sql.StringExpression#like}.
- -
*
- -
* @type Object
- -
* @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}
- -
*/
- -
BITWISE_OPERATORS:BITWISE_OPERATORS,
- -
/**
- +
* Default inequality operators.
- +
* @example
- +
* sql.a.like(/^a/); //=> "a" ~* '^a'
* sql.a.like("A%"); //=> "a" LIKE 'A%'
- -
*
- -
* @type Object
- +
* @default {GT:">",GTE:">=",LT:"<",LTE:"<="}
* @param expression
- -
*/
- +
INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,
- +
like:function (expression) {
- 11
+expression = argsToArray(arguments);
- 11
+return StringExpression.like.apply(StringExpression, [this].concat(expression));
- +
}
- +
}
- +
}).as(sql, "StringMethods");
- +
- +
/**
- +
* @class This mixin provides string concatenation methods ("concat");
- +
*
- +
* @example
- +
*
- +
* sql.x.sqlString.concat("y"); //=> "x" || "y"
- +
*
- +
* @name StringConcatenationMethods
- +
* @memberOf patio.sql
- +
*/
- 1
+var StringConcatenationMethods = define(null, {
- +
instance:{
/**@lends patio.sql.StringConcatenationMethods.prototype*/
- -
/**
- +
* Default boolean operators.
- +
* Return a {@link patio.sql.StringExpression} representing the concatenation of this expression
* with the given argument.
- -
*
- -
* @type Object
- +
* @default {AND:"AND",OR:"OR"}
- +
* @example
- +
*
- +
* sql.x.sqlString.concat("y"); //=> "x" || "y"
- +
*
* @param expression expression to concatenate this expression with.
- -
*/
- +
BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,
- +
concat:function (expression) {
- 0
+return new StringExpression("||", this, expression);
- +
}
- +
}
- +
}).as(sql, "StringConcatenationMethods");
- +
- +
/**
- +
* @class This mixin provides the ability to access elements within a SQL array.
- +
*
- +
* @example
- +
* sql.array.sqlSubscript(1) //=> array[1]
- +
* sql.array.sqlSubscript(1, 2) //=> array[1, 2]
- +
* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
- +
*
- +
* @name SubscriptMethods
- +
* @memberOf patio.sql
- +
*/
- 1
+var SubscriptMethods = define(null, {
instance:{
- -
/**
- +
* Default IN operators.
- +
* Return a {@link patio.sql.Subscript} with the given arguments, representing an
* SQL array access.
- -
*
- -
* @type Object
- -
* @default {IN:"IN",NOTIN:'NOT IN'}
- -
*/
- -
IN_OPERATORS:IN_OPERATORS,
- -
/**
- +
* Default IS operators.
- +
* @example
- +
* sql.array.sqlSubscript(1) //=> array[1]
- +
* sql.array.sqlSubscript(1, 2) //=> array[1, 2]
* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
- -
*
- -
* @type Object
- +
* @default {IS:"IS", ISNOT:'IS NOT'}
* @param subscript
- -
*/
- +
IS_OPERATORS:IS_OPERATORS,
- +
sqlSubscript:function (subscript) {
- 108
+var args = argsToArray(arguments);
- 108
+return new SubScript(this, flatten(args));
- +
}
- +
}
- +
}).as(sql, "SubScriptMethods");
- +
- +
- +
/**
- +
* @class This is the parent of all expressions.
- +
*
- +
* @name Expression
- +
* @memberOf patio.sql
- +
*/
- 1
+var Expression = define(null, {
- +
- +
instance:{
- +
/**@lends patio.sql.Expression.prototype*/
- -
/**
- +
* Default two arity operators.
* Returns the string representation of this expression
- -
*
- -
* @type Object
- -
* @default {
- -
* EQ:'=',
- -
* NEQ:'!=', LIKE:"LIKE",
- -
* "NOT LIKE":'NOT LIKE',
- -
* ILIKE:"ILIKE",
- -
* "NOT ILIKE":'NOT ILIKE',
- -
* "~":"~",
- -
* '!~':"!~",
- -
* '~*':"~*",
- -
* '!~*':"!~*",
- -
* GT:">",
- -
* GTE:">=",
- -
* LT:"<",
- -
* LTE:"<=",
- -
* bitWiseAnd:"&",
- -
* bitWiseOr:"|",
- -
* exclusiveOr:"^",
- -
* leftShift:"<<",
- -
* rightShift:">>",
- -
* IS:"IS",
- -
* ISNOT:'IS NOT',
- -
* IN:"IN",
- -
* NOTIN:'NOT IN'
- +
* }
- +
* @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.
* @return {String} a string literal version of this expression.
- -
*/
- +
TWO_ARITY_OPERATORS:TWO_ARITY_OPERATORS,
- +
sqlLiteral:function (ds) {
- 0
+return this.toString(ds);
- +
}
- +
- +
},
- +
- +
static:{
/**@lends patio.sql.Expression*/
- -
/**
- +
* Default N(multi) arity operators.
* This is a helper method that will take in an array of arguments and return an expression.
- -
*
- -
* @type Object
- -
* @default {
- -
* "||":"||",
- -
* AND:"AND",
- -
* OR:"OR",
- -
* PLUS:"+",
- -
* MINUS:"-",
- -
* DIVIDE:"/", MULTIPLY:"*"
- +
* }
- +
* @example
- +
*
- +
* QualifiedIdentifier.fromArgs(["table", "column"]);
- +
*
- +
* @param {*[]} args array of arguments to pass into the constructor of the function.
- +
*
* @return {patio.sql.Expression} an expression.
- -
*/
- +
N_ARITY_OPERATORS:N_ARITY_OPERATORS,
- +
fromArgs:function (args) {
- 2216
+var ret;
- 2216
+try {
- 2216
+ret = new this();
- +
} catch (ignore) {
- +
}
- 2216
+this.apply(ret, args);
- 2216
+return ret;
},
- -
/**
- +
* Default ONE operators.
- +
* Helper to determine if something is a condition specifier. Returns true if the object
* is a Hash or is an array of two element arrays.
- -
*
- -
* @type Object
- -
* @default {
- -
* "NOT":"NOT",
- -
* "NOOP":"NOOP"
- +
* }
- +
* @example
- +
* Expression.isConditionSpecifier({a : "b"}); //=> true
- +
* Expression.isConditionSpecifier("a"); //=> false
- +
* Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true
- +
* Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false
- +
*
- +
* @param {*} obj object to test if it is a condition specifier
* @return {Boolean} true if the object is a Hash or is an array of two element arrays.
- -
*/
- +
ONE_ARITY_OPERATORS:ONE_ARITY_OPERATORS
- +
isConditionSpecifier:function (obj) {
- 22715
+return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
- 9896
+return isArray(i) && i.length === 2;
- +
}));
}
- -
}
- +
}).as(sql, "ComplexExpression");
- +
- +
}).as(sql, "Expression");
- +
- +
/**
- +
* @class Base class for all GenericExpressions
- +
*
- +
* @augments patio.sql.Expression
- +
* @augments patio.sql.AliasMethods
- +
* @augments patio.sql.BooleanMethods
- +
* @augments patio.sql.CastMethods
- +
* @augments patio.sql.ComplexExpressionMethods
- +
* @augments patio.sql.InequalityMethods
- +
* @augments patio.sql.NumericMethods
- +
* @augments patio.sql.OrderedMethods
- +
* @augments patio.sql.StringMethods
- +
* @augments patio.sql.SubscriptMethods
- +
*
- +
* @name GenericExpression
- +
* @memberOf patio.sql
- +
*/
- 1
+var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");
- +
- +
- 1
+var AliasedExpression = define(Expression, {
- +
instance:{
- +
/**@lends patio.sql.AliasedExpression.prototype*/
- +
- +
/**
- +
* This class reperesents an Aliased Expression
- +
*
- +
* @constructs
- +
* @augments patio.sql.Expression
- +
*
- +
* @param expression the expression to alias.
- +
* @param alias the alias to alias the expression to.
- +
*
- +
* @property expression the expression being aliased
- +
* @property alias the alias of the expression
- +
*
- +
*/
- +
constructor:function (expression, alias) {
- 945
+this.expression = expression;
- 945
+this.alias = alias;
- +
},
- +
- +
/**
- +
* Converts the aliased expression to a string
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL alias fragment.
- +
*/
- +
toString:function (ds) {
- 877
+!Dataset && (Dataset = require("./dataset"));
- 877
+ds = ds || new Dataset();
- 877
+return ds.aliasedExpressionSql(this);
- +
}
- +
}
- +
}
).as(sql, "AliasedExpression");
- -
- -
/**
- -
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- -
* in a boolean value in SQL.
- -
*
- -
* @augments patio.sql.ComplexExpression
- -
* @augments patio.sql.BooleanMethods
- -
* @name BooleanExpression
- -
* @memberOf patio.sql
- -
*/
- 1
-var BooleanExpression = define([ComplexExpression, BooleanMethods], {
- -
static:{
- +
/**@lends patio.sql.BooleanExpression*/
- 1
+var CaseExpression = define(GenericExpression, {
- +
instance:{
/**@lends patio.sql.CaseExpression.prototype*/
- -
/**
- -
* Invert the expression, if possible. If the expression cannot
- -
* be inverted, it throws an {@link patio.error.ExpressionError}. An inverted expression should match
- -
* everything that the uninverted expression did not match, and vice-versa, except for possible issues with
- -
* SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
- -
*
- -
* @example
- +
* BooleanExpression.invert(sql.a) //=> NOT "a"
- +
* Create an object with the given conditions and
- +
* default value. An expression can be provided to
- +
* test each condition against, instead of having
* all conditions represent their own boolean expression.
- -
*
- -
* @param {patio.sql.BooleanExpression} expression
- +
* the expression to invert.
- +
* @constructs
- +
* @augments patio.sql.GenericExpression
- +
* @param {Array|Object} conditions conditions to create the case expression from
- +
* @param def default value
* @param expression expression to create the CASE expression from
- -
*
- +
* @return {patio.sql.BooleanExpression} the inverted expression.
- +
* @property {Boolean} hasExpression returns true if this case expression has a expression
- +
* @property conditions the conditions of the {@link patio.sql.CaseExpression}.
- +
* @property def the default value of the {@link patio.sql.CaseExpression}.
- +
* @property expression the expression of the {@link patio.sql.CaseExpression}.
* @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.
- -
*/
- -
invert:function (expression) {
- 95
-if (isInstanceOf(expression, BooleanExpression)) {
- 90
-var op = expression.op, newArgs;
- 90
-if (op == "AND" || op == "OR") {
- 3
-newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args.map(function (arg) {
- 6
-return BooleanExpression.invert(arg);
- -
}));
- 3
-return BooleanExpression.fromArgs(newArgs);
- -
} else {
- 87
-newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);
- 87
-return BooleanExpression.fromArgs(newArgs);
- -
}
- 5
-} else if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
- 0
-throw new ExpressionError(format("Cannot invert %4j", [expression]));
- -
} else {
- 5
+return new BooleanExpression("NOT", expression);
- +
constructor:function (conditions, def, expression) {
- 8
+if (Expression.isConditionSpecifier(conditions)) {
- 4
+this.conditions = toArray(conditions);
- 4
+this.def = def;
- 4
+this.expression = expression;
- 4
this.noExpression = isUndefined(expression);
}
- +
},
- -
/**
- -
* Take pairs of values (e.g. a hash or array of two element arrays)
- -
* and converts it to a {@link patio.sql.BooleanExpression}. The operator and args
- +
* used depends on the case of the right (2nd) argument:
* Converts the case expression to a string
- -
*
- -
* <pre class='code'>
- -
* BooleanExpression.fromValuePairs({a : [1,2,3]}) //=> a IN (1,2,3)
- -
* BooleanExpression.fromValuePairs({a : true}); // a IS TRUE;
- -
* BooleanExpression.fromValuePairs({a : /^A/i}); // a *~ '^A'
- +
* </pre>
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- -
* If multiple arguments are given, they are joined with the op given (AND
- -
* by default, OR possible). If negate is set to true,
- -
* all subexpressions are inverted before used.
- -
* <pre class='code'>
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}) //=> a IN (1,2,3) AND b IS TRUE
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
- -
* </pre>
- -
* @param {Object} a object to convert to a {@link patio.sql.BooleanExpression}
- -
* @param {String} [op="AND"] Boolean operator to join each subexpression with.
- -
* <pre class="code">
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
- -
* </pre>
- -
* @param {Boolean} [negate=false] set to try to invert the {@link patio.sql.BooleanExpression}.
- -
* <pre class="code">
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
- -
* </pre>
- +
* @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
* @return String the SQL case expression fragment.
- -
*/
- -
fromValuePairs:function (a, op, negate) {
- 7610
-!Dataset && (Dataset = require("./dataset"));
- 7610
-op = op || "AND", negate = negate || false;
- 7610
-var pairArr = [];
- 7610
-var isArr = isArray(a) && Expression.isConditionSpecifier(a);
- 7610
-if (isHash(a)) {
- 3385
-pairArr.push(this.__filterObject(a));
- -
} else {
- 4225
-for (var k in a) {
- 5203
-var v = isArr ? a[k][1] : a[k], ret;
- 5203
-k = isArr ? a[k][0] : k;
- 5203
-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);
- 5186
-} else if (isInstanceOf(v, NegativeBooleanConstant)) {
- 0
-ret = new BooleanExpression("ISNOT", k, v.constant);
- 5186
-} else if (isInstanceOf(v, BooleanConstant)) {
- 0
-ret = new BooleanExpression("IS", k, v.constant);
- 5186
-} else if (isNull(v) || isBoolean(v)) {
- 240
-ret = new BooleanExpression("IS", k, v);
- 4946
-} else if (isHash(v)) {
- 0
-ret = BooleanExpression.__filterObject(v, k);
- 4946
-} else if (isRegExp(v)) {
- 69
-ret = StringExpression.like(sql.stringToIdentifier(k), v);
- -
} else {
- 4877
-ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
- -
}
- 5203
-negate && (ret = BooleanExpression.invert(ret));
- 5203
-pairArr.push(ret);
- -
}
- -
}
- -
//if We just have one then return the first otherwise create a new Boolean expression
- 7610
+return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
- +
toString:function (ds) {
- 2
+!Dataset && (Dataset = require("./dataset"));
- 2
+ds = ds || new Dataset();
- 2
return ds.caseExpressionSql(this);
},
- +
- +
/**@ignore*/
- +
getters:{
- +
/**@ignore*/
- +
hasExpression:function () {
- 2
+return !this.noExpression;
- +
}
- +
}
- +
}
- +
}).as(sql, "CaseExpression");
- +
- +
- 1
+var Cast = define(GenericExpression, {
- +
instance:{
- +
/**@lends patio.sql.Cast*/
- -
/**
- -
* @private
- -
*
- +
* This builds an expression from a hash
- +
* Represents a cast of an SQL expression to a specific type.
- +
* @constructs
* @augments patio.sql.GenericExpression
- -
*
- +
* @example
- +
* @param expr the expression to CAST.
* @param type the type to CAST the expression to.
- -
*
- -
* Dataset._filterObject({a : 1}) //=> WHERE (a = 1)
- -
* Dataset._filterObject({x : {gt : 1}}) //=> WHERE (x > 1)
- -
* Dataset._filterObject({x : {gt : 1}, a : 1}) //=> WHERE ((x > 1) AND (a = 1))
- -
* Dataset._filterObject({x : {like : "name"}}) //=> WHERE (x LIKE 'name')
- -
* Dataset._filterObject({x : {iLike : "name"}}) //=> WHERE (x LIKE 'name')
- -
* Dataset._filterObject({x : {between : [1,10]}}) //=> WHERE ((x >= 1) AND (x <= 10))
- -
* Dataset._filterObject({x : {notBetween : [1,10]}}) //=> WHERE ((x < 1) OR (x > 10))
- +
* Dataset._filterObject({x : {neq : 1}}) //=> WHERE (x != 1)
- +
* @property expr the expression to CAST.
- +
* @property type the type to CAST the expression to.
- +
*/
- +
constructor:function (expr, type) {
- 3
+this.expr = expr;
- 3
+this.type = type;
- +
},
- +
- +
/**
* Converts the cast expression to a string
- -
*
- -
* @param {Object} expr the expression we need to create an expression out of
- +
* @param {String} [key=null] the key that the hash corresponds to
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return {patio.sql.Expression} an expression to use in the filter
* @return String the SQL cast expression fragment.
- -
*/
- -
__filterObject:function (expr, key) {
- 3502
-var pairs = [], opts, newKey;
- 3502
-var twoArityOperators = this.TWO_ARITY_OPERATORS;
- 3502
-for (var k in expr) {
- 3541
-var v = expr[k];
- 3541
-if (isHash(v)) { //its a hash too filter it too!
- 115
-pairs.push(this.__filterObject(v, k));
- 3426
-} else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
- -
//its a two arrity operator (e.g. '=', '>')
- 118
-newKey = isString(key) ? key.split(",") : [key];
- 118
-if (newKey.length > 1) {
- -
//this represents a hash where the key represents two columns
- -
//(e.g. {"col1,col2" : 1}) => WHERE (col1 = 1 AND col2 = 1)
- 1
-pairs = pairs.concat(newKey.map(function (k) {
- -
//filter each column with the expression
- 2
-return this.__filterObject(expr, k);
- -
}, this));
- -
} else {
- 117
-newKey = [sql.stringToIdentifier(newKey[0])];
- 117
-if (k.match(/^like$/)) {
- -
//its a like clause {col : {like : "hello"}}
- -
- 3
-pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));
- 114
-} else if (k.match(/^iLike$/)) {
- -
//its a like clause {col : {iLike : "hello"}}
- 2
-pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]).concat({caseInsensitive:true}))));
- 112
-} else if (k.match(/between/i)) {
- -
//its a like clause {col : {between : [1,10]}}
- 6
-var between = sql.stringToIdentifier(newKey[0]).between(v);
- 6
-k == "notBetween" && (between = between.not());
- 6
-pairs.push(between);
- -
} else {
- -
//otherwise is just a boolean expressio
- -
//it its not a valid operator then we
- -
//BooleanExpression with throw an error
- 106
-pairs.push(new BooleanExpression(k, newKey[0], v));
- -
}
- -
}
- -
} else {
- -
//we're not a twoarity operator
- -
//so we create a boolean expression out of it
- 3308
-newKey = k.split(",");
- 3308
-if (newKey.length == 1) {
- 3302
-newKey = sql.stringToIdentifier(newKey[0]);
- -
}
- 3308
-opts = [
- -
[newKey, v]
- -
];
- 3308
-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
- 3502
+return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
- +
toString:function (ds) {
- 2
+!Dataset && (Dataset = require("./dataset"));
- 2
+ds = ds || new Dataset();
- 2
return ds.castSql(this.expr, this.type);
}
- -
}
- +
}).as(sql, "BooleanExpression");
}).as(sql, "Cast");
- -
- 1
+var Constant = define(GenericExpression, {
- 1
var ColumnAll = define(Expression, {
- -
instance:{
- +
/**@lends patio.sql.Constant.prototype*/
- +
/**@lends patio.sql.ColumnAll.prototype*/
- -
/**
- -
* Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.
- +
*
* Represents all columns in a given table, table.* in SQL
- -
* @constructs
- -
* @augments patio.sql.GenericExpression
- +
* @property {String} constant <b>READ ONLY</b> the contant.
- +
*
- +
* @augments patio.sql.Expression
- +
*
- +
* @param table the table this expression is for.
- +
*
* @property table the table this all column expression represents.
- -
*/
- -
constructor:function (constant) {
- 18
+this.__constant = constant;
- +
constructor:function (table) {
- 20
this.table = table;
},
- -
/**
- +
* Converts the {@link patio.sql.Constant} to a string.
* Converts the ColumnAll expression to a string
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.Constant}.
* @return String the SQL columnAll expression fragment.
*/
- -
toString:function (ds) {
- 6
-!Dataset && (Dataset = require("./dataset"));
- 6
-ds = ds || new Dataset();
- 6
-return ds.constantSql(this.__constant);
- -
},
- -
- -
getters:{
- -
constant:function () {
- 0
-return this.__constant;
- +
}
- 19
+!Dataset && (Dataset = require("./dataset"));
- 19
+ds = ds || new Dataset();
- 19
return ds.columnAllSql(this);
}
- -
}
- +
}).as(sql, "Constant");
}).as(sql, "ColumnAll");
- -
- -
/**
- -
* @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
- -
* @auments patio.sql.Constant
- -
* @name BooleanConstant
- -
* @memberOf patio.sql
- -
*/
- 1
+var BooleanConstant = define(Constant, {
- 1
var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {
- -
instance:{
- +
/**@lends patio.sql.BooleanConstant.prototype*/
/**@lends patio.sql.ComplexExpression.prototype*/
- -
/**
- +
* Converts the {@link patio.sql.BooleanConstant} to a string.
- +
* Represents a complex SQL expression, with a given operator and one
- +
* or more attributes (which may also be ComplexExpressions, forming
* a tree).
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* This is an abstract class that is not that useful by itself. The
- +
* subclasses @link patio.sql.BooleanExpression},
- +
* {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should
* be used instead of this class directly.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.BooleanConstant}.
- +
* @constructs
- +
* @augments patio.sql.Expression
- +
* @augments patio.sql.AliasMethods
- +
* @augments patio.sql.CastMethods
- +
* @augments patio.sql.OrderedMethods
- +
* @augments patio.sql.SubscriptMethods
- +
*
- +
* @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.
- +
* @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.
- +
*
- +
* @param {...} op The operator and arguments for this object to the ones given.
- +
* <p>
- +
* Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,
- +
* other than the second arg for an IN/NOT IN operator.</li>
* </p>
- -
*/
- -
toString:function (ds) {
- 10
-!Dataset && (Dataset = require("./dataset"));
- 10
-ds = ds || new Dataset();
- 10
-return ds.booleanConstantSql(this.__constant);
- -
}
- -
}
- +
}).as(sql, "BooleanConstant");
- +
constructor:function (op) {
- 7952
+if (op) {
- 7164
+var args = argsToArray(arguments,1 );
- +
//make a copy of the args
- 7164
+var origArgs = args.slice(0);
- 7164
+args.forEach(function (a, i) {
- 14676
+if (Expression.isConditionSpecifier(a)) {
- 6
+args[i] = BooleanExpression.fromValuePairs(a);
- +
}
- +
});
- 7164
op = op.toUpperCase();
- -
- -
/**
- -
* Represents inverse boolean constants (currently only NOTNULL). A
- -
* special class to allow for special behavior.
- -
*
- -
* @augments patio.sql.BooleanConstant
- -
* @name NegativeBooleanConstant
- -
* @memberOf patio.sql
- -
*/
- 1
-var NegativeBooleanConstant = define(BooleanConstant, {
- -
instance:{
- +
/**@lends patio.sql.NegativeBooleanConstant.prototype*/
- 7164
+if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
- 1164
+if (args.length < 1) {
- 0
+throw new ExpressionError("The " + op + " operator requires at least 1 argument")
- +
}
- 1164
+var oldArgs = args.slice(0);
- 1164
+args = [];
- 1164
+oldArgs.forEach(function (a) {
- 2739
+a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
- +
});
- +
- 6000
+} else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
- 5937
+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.
- 5937
+if (IN_OPERATORS[op]) {
- 23
+args[1] = origArgs[1]
- +
}
- 63
+} else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
- 63
+if (args.length != 1) {
- 0
+throw new ExpressionError("The " + op + " operator requires only one argument");
- +
}
- +
} else {
- 0
+throw new ExpressionError("Invalid operator " + op);
- +
}
- 7164
+this.op = op;
- 7164
+this.args = args;
- +
}
},
- -
/**
- +
* Converts the {@link patio.sql.NegativeBooleanConstant} to a string.
* Converts the ComplexExpression to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.NegativeBooleanConstant}.
* @return String the SQL version of the {@link patio.sql.ComplexExpression}.
*/
- -
toString:function (ds) {
- 2
-!Dataset && (Dataset = require("./dataset"));
- 2
-ds = ds || new Dataset();
- 2
+return ds.negativeBooleanConstantSql(this.__constant);
- 6761
+!Dataset && (Dataset = require("./dataset"));
- 6761
+ds = ds || new Dataset();
- 6761
return ds.complexExpressionSql(this.op, this.args);
- -
}
- -
}
- -
}).as(sql, "NegativeBooleanConstant");
- -
- -
/**
- -
* @namespace Holds default generic constants that can be referenced. These
- -
* are included in {@link patio}
- -
* @name Constants
- -
* @memberOf patio.sql
- -
*/
- 1
-sql.Constants = {
- -
/**@lends patio.sql.Constants*/
- -
- -
/**
- -
* Constant for CURRENT DATE
- -
* @type patio.sql.Constant
- -
*/
- -
CURRENT_DATE:new Constant("CURRENT_DATE"),
- -
- -
/**
- -
* Constant for CURRENT TIME
- -
* @type patio.sql.Constant
- -
*/
- -
CURRENT_TIME:new Constant("CURRENT_TIME"),
- -
- -
/**
- -
* Constant for CURRENT TIMESTAMP
- -
* @type patio.sql.Constant
- -
*/
- -
CURRENT_TIMESTAMP:new Constant("CURRENT_TIMESTAMP"),
- -
- -
/**
- -
* Constant for TRUE
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
SQLTRUE:new BooleanConstant(1),
- -
- -
/**
- -
* Constant for TRUE
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
TRUE:new BooleanConstant(1),
- -
- -
/**
- -
* Constant for FALSE.
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
SQLFALSE:new BooleanConstant(0),
- -
- -
/**
- -
* Constant for FALSE
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
FALSE:new BooleanConstant(0),
- -
/**
- -
* Constant for NULL
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
NULL:new BooleanConstant(null),
- -
- -
/**
- -
* Constant for NOT NULL
- -
* @type patio.sql.NegativeBooleanConstant
- -
*/
- -
NOTNULL:new NegativeBooleanConstant(null)
- -
- +
};
},
- -
- 1
+var Constants = sql.Constants
- +
static:{
/**@lends patio.sql.ComplexExpression*/
- -
- 1
-var Identifier = define([GenericExpression, QualifyingMethods], {
- -
instance:{
- +
/**@lends patio.sql.Identifier.prototype*/
- +
/**
- +
* Hash of operator inversions
- +
* @type Object
- +
* @default {
- +
* AND:"OR",
- +
* OR:"AND",
- +
* GT:"lte",
- +
* GTE:"lt",
- +
* LT:"gte",
- +
* LTE:"gt",
- +
* EQ:"neq",
- +
* NEQ:"eq",
- +
* LIKE:'NOT LIKE',
- +
* "NOT LIKE":"LIKE",
- +
* '!~*':'~*',
- +
* '~*':'!~*',
- +
* "~":'!~',
- +
* "IN":'NOTIN',
- +
* "NOTIN":"IN",
- +
* "IS":'IS NOT',
- +
* "ISNOT":"IS",
- +
* NOT:"NOOP",
- +
* NOOP:"NOT",
- +
* ILIKE:'NOT ILIKE',
- +
* NOTILIKE:"ILIKE"
- +
* }
- +
*/
OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,
- -
/**
- -
* Represents an identifier (column or table). Can be used
- -
* to specify a String with multiple underscores that should not be
- +
* split, or for creating an implicit identifier without using a String.
* Default mathematical operators.
- -
*
- -
* @constructs
- -
* @augments patio.sql.GenericExpression
- +
* @augments patio.sql.QualifyingMethods
- +
* @type Object
- +
* @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}
- +
*/
- +
MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,
- +
- +
/**
* Default bitwise operators.
- -
*
- +
* @param {String}value the identifier.
- +
* @type Object
- +
* @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}
- +
*/
- +
BITWISE_OPERATORS:BITWISE_OPERATORS,
- +
/**
* Default inequality operators.
- -
*
- +
* @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
- +
* @type Object
* @default {GT:">",GTE:">=",LT:"<",LTE:"<="}
- -
*/
- -
constructor:function (value) {
- 16802
-this.__value = value;
- +
},
INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,
- -
/**
- -
* Converts the {@link patio.sql.Identifier} to a string.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
* Default boolean operators.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.Identifier}.
- +
* @type Object
* @default {AND:"AND",OR:"OR"}
- -
*/
- -
toString:function (ds) {
- 21906
-!Dataset && (Dataset = require("./dataset"));
- 21906
-ds = ds || new Dataset();
- 21906
-return ds.quoteIdentifier(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
value:function () {
- 25154
-return this.__value;
- -
}
- -
}
- -
}
- -
}).as(sql, "Identifier");
- -
- 1
-var JoinClause = define(Expression, {
- -
instance:{
- +
/**@lends patio.sql.JoinClause.prototype*/
BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,
- -
/**
- -
* Represents an SQL JOIN clause, used for joining tables.
- -
* Created by {@link patio.Dataset} join methods.
- -
* @constructs
- -
* @augments patio.sql.Expression
- -
*
- -
* @param {String} joinType the type of join this JoinClause should use
- -
* @param table the table to join with
- +
* @param tableAlias the alias to use for this join clause
* Default IN operators.
- -
*
- -
* @property {String} joinType <b>READ ONLY</b> the type of join this JoinClause should use
- -
* @property table <b>READ ONLY</b> the table to join with
- -
* @property joinType <b>READ ONLY</b> the alias to use for this join clause
- -
* */
- -
constructor:function (joinType, table, tableAlias) {
- 791
-this.__joinType = joinType;
- 791
-this.__table = table;
- 791
-this.__tableAlias = tableAlias || null;
- -
},
- +
- +
* @type Object
- +
* @default {IN:"IN",NOTIN:'NOT IN'}
- +
*/
IN_OPERATORS:IN_OPERATORS,
- -
/**
- +
* Converts the {@link patio.sql.JoinClause} to a string.
* Default IS operators.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* @type Object
- +
* @default {IS:"IS", ISNOT:'IS NOT'}
- +
*/
- +
IS_OPERATORS:IS_OPERATORS,
- +
/**
* Default two arity operators.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.JoinClause}.
- +
* @type Object
- +
* @default {
- +
* EQ:'=',
- +
* NEQ:'!=', LIKE:"LIKE",
- +
* "NOT LIKE":'NOT LIKE',
- +
* ILIKE:"ILIKE",
- +
* "NOT ILIKE":'NOT ILIKE',
- +
* "~":"~",
- +
* '!~':"!~",
- +
* '~*':"~*",
- +
* '!~*':"!~*",
- +
* GT:">",
- +
* GTE:">=",
- +
* LT:"<",
- +
* LTE:"<=",
- +
* bitWiseAnd:"&",
- +
* bitWiseOr:"|",
- +
* exclusiveOr:"^",
- +
* leftShift:"<<",
- +
* rightShift:">>",
- +
* IS:"IS",
- +
* ISNOT:'IS NOT',
- +
* IN:"IN",
- +
* NOTIN:'NOT IN'
* }
- -
*/
- -
toString:function (ds) {
- 17
-!Dataset && (Dataset = require("./dataset"));
- 17
-ds = ds || new Dataset();
- 17
-return ds.joinClauseSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
joinType:function () {
- 927
-return this.__joinType;
- -
},
- -
- -
table:function () {
- 928
-return this.__table;
- -
},
- -
- -
tableAlias:function () {
- 926
-return this.__tableAlias;
- -
}
- -
}
- -
}
- -
}).as(sql, "JoinClause");
- +
TWO_ARITY_OPERATORS:TWO_ARITY_OPERATORS,
- -
- 1
-var JoinOnClause = define(JoinClause, {
- -
instance:{
/**@lends patio.sql.JoinOnClause.prototype*/
- -
/**
- -
* Represents an SQL JOIN clause with ON conditions. Created by {@link patio.Dataset} join methods.
- -
* See {@link patio.sql.JoinClause} for other argument parameters.
- -
* @constructs
- +
* @augments patio.sql.JoinClause
* Default N(multi) arity operators.
- -
*
- -
* @param on the expression to filter with. See {@link patio.Dataset#filter}
- +
* @property on <b>READ ONLY</b> the filter to use with joining the datasets.
- +
* @type Object
- +
* @default {
- +
* "||":"||",
- +
* AND:"AND",
- +
* OR:"OR",
- +
* PLUS:"+",
- +
* MINUS:"-",
- +
* DIVIDE:"/", MULTIPLY:"*"
* }
- -
*/
- -
constructor:function (on, joinType, table, tableAlias) {
- 761
-this.__on = on;
- 761
-this._super(arguments, [joinType, table, tableAlias]);
- +
},
N_ARITY_OPERATORS:N_ARITY_OPERATORS,
- -
/**
- -
* Converts the {@link patio.sql.JoinOnClause} to a string.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
* Default ONE operators.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.JoinOnClause}.
- +
* @type Object
- +
* @default {
- +
* "NOT":"NOT",
- +
* "NOOP":"NOOP"
* }
- -
*/
- -
toString:function (ds) {
- 813
-!Dataset && (Dataset = require("./dataset"));
- 813
-ds = ds || new Dataset();
- 813
-return ds.joinOnClauseSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
on:function () {
- 813
-return this.__on;
- -
}
- +
}
ONE_ARITY_OPERATORS:ONE_ARITY_OPERATORS
- -
}
- +
}).as(sql, "JoinOnClause");
}).as(sql, "ComplexExpression");
- -
- 1
-var JoinUsingClause = define(JoinClause, {
- -
instance:{
- +
/**@lends patio.sql.JoinUsingClause.prototype*/
- +
/**
- +
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- +
* in a boolean value in SQL.
- +
*
- +
* @augments patio.sql.ComplexExpression
- +
* @augments patio.sql.BooleanMethods
- +
* @name BooleanExpression
- +
* @memberOf patio.sql
- +
*/
- 1
+var BooleanExpression = define([ComplexExpression, BooleanMethods], {
- +
static:{
/**@lends patio.sql.BooleanExpression*/
- -
/**
- -
* Represents an SQL JOIN clause with USING conditions.
- -
* Created by {@link patio.Dataset} join methods.
- +
* See {@link patio.sql.JoinClause} for other argument parameters.
- +
* Invert the expression, if possible. If the expression cannot
- +
* be inverted, it throws an {@link patio.error.ExpressionError}. An inverted expression should match
- +
* everything that the uninverted expression did not match, and vice-versa, except for possible issues with
* SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
- -
*
- -
* @constructs
- +
* @augments patio.sql.JoinClause
- +
* @example
* BooleanExpression.invert(sql.a) //=> NOT "a"
- -
*
- -
* @param using the column/s to use when joining.
- +
* @property using <b>READ ONLY</b> the column/s to use when joining.
- +
* @param {patio.sql.BooleanExpression} expression
- +
* the expression to invert.
- +
*
* @return {patio.sql.BooleanExpression} the inverted expression.
- -
*/
- -
constructor:function (using, joinType, table, tableAlias) {
- 8
-this.__using = using.map(function (u) {
- 9
-return isString(u) ? new Identifier(u) : u;
- -
});
- 8
+this._super(arguments, [joinType, table, tableAlias]);
- +
invert:function (expression) {
- 95
+if (isInstanceOf(expression, BooleanExpression)) {
- 90
+var op = expression.op, newArgs;
- 90
+if (op == "AND" || op == "OR") {
- 3
+newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args.map(function (arg) {
- 6
+return BooleanExpression.invert(arg);
- +
}));
- 3
+return BooleanExpression.fromArgs(newArgs);
- +
} else {
- 87
+newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);
- 87
+return BooleanExpression.fromArgs(newArgs);
- +
}
- 5
+} else if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
- 0
+throw new ExpressionError(format("Cannot invert %4j", [expression]));
- +
} else {
- 5
+return new BooleanExpression("NOT", expression);
}
- -
},
- -
/**
- +
* Converts the {@link patio.sql.JoinUsingClause} to a string.
- +
* Take pairs of values (e.g. a hash or array of two element arrays)
- +
* and converts it to a {@link patio.sql.BooleanExpression}. The operator and args
* used depends on the case of the right (2nd) argument:
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* <pre class='code'>
- +
* BooleanExpression.fromValuePairs({a : [1,2,3]}) //=> a IN (1,2,3)
- +
* BooleanExpression.fromValuePairs({a : true}); // a IS TRUE;
- +
* BooleanExpression.fromValuePairs({a : /^A/i}); // a *~ '^A'
* </pre>
- -
*
- -
* @return String the SQL version of the {@link patio.sql.JoinUsingClause}.
- -
*/
- -
toString:function (ds) {
- 95
-!Dataset && (Dataset = require("./dataset"));
- 95
-ds = ds || new Dataset();
- 95
-return ds.joinUsingClauseSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
using:function () {
- 95
+return this.__using;
- +
* If multiple arguments are given, they are joined with the op given (AND
- +
* by default, OR possible). If negate is set to true,
- +
* all subexpressions are inverted before used.
- +
* <pre class='code'>
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}) //=> a IN (1,2,3) AND b IS TRUE
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
- +
* </pre>
- +
* @param {Object} a object to convert to a {@link patio.sql.BooleanExpression}
- +
* @param {String} [op="AND"] Boolean operator to join each subexpression with.
- +
* <pre class="code">
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
- +
* </pre>
- +
* @param {Boolean} [negate=false] set to try to invert the {@link patio.sql.BooleanExpression}.
- +
* <pre class="code">
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
- +
* </pre>
- +
* @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
- +
*/
- +
fromValuePairs:function (a, op, negate) {
- 7610
+!Dataset && (Dataset = require("./dataset"));
- 7610
+op = op || "AND", negate = negate || false;
- 7610
+var pairArr = [];
- 7610
+var isArr = isArray(a) && Expression.isConditionSpecifier(a);
- 7610
+if (isHash(a)) {
- 3385
+pairArr.push(this.__filterObject(a));
- +
} else {
- 4225
+for (var k in a) {
- 5203
+var v = isArr ? a[k][1] : a[k], ret;
- 5203
+k = isArr ? a[k][0] : k;
- 5203
+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);
- 5186
+} else if (isInstanceOf(v, NegativeBooleanConstant)) {
- 0
+ret = new BooleanExpression("ISNOT", k, v.constant);
- 5186
+} else if (isInstanceOf(v, BooleanConstant)) {
- 0
+ret = new BooleanExpression("IS", k, v.constant);
- 5186
+} else if (isNull(v) || isBoolean(v)) {
- 240
+ret = new BooleanExpression("IS", k, v);
- 4946
+} else if (isHash(v)) {
- 0
+ret = BooleanExpression.__filterObject(v, k);
- 4946
+} else if (isRegExp(v)) {
- 69
+ret = StringExpression.like(sql.stringToIdentifier(k), v);
- +
} else {
- 4877
+ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
- +
}
- 5203
+negate && (ret = BooleanExpression.invert(ret));
- 5203
+pairArr.push(ret);
}
- -
}
- -
}
- -
}
- -
}).as(sql, "JoinUsingClause");
- -
- -
- 1
-var PlaceHolderLiteralString = define(GenericExpression, {
- -
instance:{
- +
/**@lends patio.sql.PlaceHolderLiteralString.prototype*/
- +
//if We just have one then return the first otherwise create a new Boolean expression
- 7610
+return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
},
- -
/**
- -
* Represents a literal string with placeholders and arguments.
- -
* This is necessary to ensure delayed literalization of the arguments
- -
* required for the prepared statement support and for database-specific
- +
* literalization.
* @private
- -
*
- -
* @constructs
- +
* @augments patio.sql.GenericExpression
* This builds an expression from a hash
- -
*
- -
* @param {String} str the string that contains placeholders.
- -
* @param {Array} args array of arguments that will be literalized using {@link patio.Dataset#literal}, and
- -
* replaced in the string.
- +
* @param {Boolean} [parens=false] set to true to wrap the string in parens.
* @example
- -
*
- -
* @property {String} str <b>READ ONLY</b> the string that contains placeholders.
- -
* @property {Array} args <b>READ ONLY</b> array of arguments that will be literalized using {@link patio.Dataset#literal}, and
- -
* replaced in the string.
- -
* @property {String} parens <b>READ ONLY</b> set to true to wrap the string in parens.
- -
*/
- -
constructor:function (str, args, parens) {
- 53
-parens = parens || false;
- 53
-var v;
- 53
-this.__str = str;
- 53
-this.__args = isArray(args) && args.length == 1 && isHash((v = args[0])) ? v : args;
- 53
-this.__parens = parens;
- -
},
- -
- -
/**
- +
* Converts the {@link patio.sql.PlaceHolderLiteralString} to a string.
- +
* Dataset._filterObject({a : 1}) //=> WHERE (a = 1)
- +
* Dataset._filterObject({x : {gt : 1}}) //=> WHERE (x > 1)
- +
* Dataset._filterObject({x : {gt : 1}, a : 1}) //=> WHERE ((x > 1) AND (a = 1))
- +
* Dataset._filterObject({x : {like : "name"}}) //=> WHERE (x LIKE 'name')
- +
* Dataset._filterObject({x : {iLike : "name"}}) //=> WHERE (x LIKE 'name')
- +
* Dataset._filterObject({x : {between : [1,10]}}) //=> WHERE ((x >= 1) AND (x <= 10))
- +
* Dataset._filterObject({x : {notBetween : [1,10]}}) //=> WHERE ((x < 1) OR (x > 10))
* Dataset._filterObject({x : {neq : 1}}) //=> WHERE (x != 1)
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* @param {Object} expr the expression we need to create an expression out of
* @param {String} [key=null] the key that the hash corresponds to
- -
*
- +
* @return String the SQL version of the {@link patio.sql.PlaceHolderLiteralString}.
* @return {patio.sql.Expression} an expression to use in the filter
- -
*/
- -
toString:function (ds) {
- 52
-!Dataset && (Dataset = require("./dataset"));
- 52
-ds = ds || new Dataset();
- 52
-return ds.placeholderLiteralStringSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
str:function () {
- 55
-return this.__str;
- -
},
- -
args:function () {
- 55
-return this.__args;
- +
},
- +
__filterObject:function (expr, key) {
- 3502
+var pairs = [], opts, newKey;
- 3502
+var twoArityOperators = this.TWO_ARITY_OPERATORS;
- 3502
+for (var k in expr) {
- 3541
+var v = expr[k];
- 3541
+if (isHash(v)) { //its a hash too filter it too!
- 115
+pairs.push(this.__filterObject(v, k));
- 3426
+} else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
- +
//its a two arrity operator (e.g. '=', '>')
- 118
+newKey = isString(key) ? key.split(",") : [key];
- 118
+if (newKey.length > 1) {
- +
//this represents a hash where the key represents two columns
- +
//(e.g. {"col1,col2" : 1}) => WHERE (col1 = 1 AND col2 = 1)
- 1
+pairs = pairs.concat(newKey.map(function (k) {
- +
//filter each column with the expression
- 2
+return this.__filterObject(expr, k);
- +
}, this));
- +
} else {
- 117
+newKey = [sql.stringToIdentifier(newKey[0])];
- 117
+if (k.match(/^like$/)) {
//its a like clause {col : {like : "hello"}}
- -
- -
parens:function () {
- 55
+return this.__parens;
- 3
+pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));
- 114
+} else if (k.match(/^iLike$/)) {
- +
//its a like clause {col : {iLike : "hello"}}
- 2
+pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]).concat({caseInsensitive:true}))));
- 112
+} else if (k.match(/between/i)) {
- +
//its a like clause {col : {between : [1,10]}}
- 6
+var between = sql.stringToIdentifier(newKey[0]).between(v);
- 6
+k == "notBetween" && (between = between.not());
- 6
+pairs.push(between);
- +
} else {
- +
//otherwise is just a boolean expressio
- +
//it its not a valid operator then we
- +
//BooleanExpression with throw an error
- 106
+pairs.push(new BooleanExpression(k, newKey[0], v));
- +
}
- +
}
- +
} else {
- +
//we're not a twoarity operator
- +
//so we create a boolean expression out of it
- 3308
+newKey = k.split(",");
- 3308
+if (newKey.length == 1) {
- 3302
+newKey = sql.stringToIdentifier(newKey[0]);
- +
}
- 3308
+opts = [
- +
[newKey, v]
- +
];
- 3308
+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
- 3502
return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
}
- -
}
- +
}).as(sql, "PlaceHolderLiteralString");
}).as(sql, "BooleanExpression");
- -
- 1
+var SQLFunction = define(GenericExpression, {
- 1
var Constant = define(GenericExpression, {
- -
instance:{
- -
/**@lends patio.sql.SQLFunction.prototype*/
- +
/**@lends patio.sql.Constant.prototype*/
- -
/**
- +
* Represents an SQL function call.
* Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.
*
* @constructs
- -
* @augments patio.sql.GenericExpression
- -
*
- -
* @param {...} f variable number of arguments where the first argument is the name
- -
* of the SQL function to invoke. The rest of the arguments will be literalized through
- -
* {@link patio.Dataset#literal} and placed into the SQL function call.
- -
*
- -
* @property {String} f <b>READ ONLY</b> the SQL function to call.
- -
* @property {Array} args <b>READ ONLY</b> args arguments will be literalized through
- -
* {@link patio.Dataset#literal} and placed into the SQL function call.
- -
* */
- -
constructor:function (f) {
- 1109
-var args = argsToArray(arguments).slice(1);
- 1109
-this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {
- 773
-return isString(a) ? sql.stringToIdentifier(a) : a;
- +
});
- +
* @property {String} constant <b>READ ONLY</b> the contant.
- +
*/
- +
constructor:function (constant) {
- 18
this.__constant = constant;
},
- -
/**
- +
* Converts the {@link patio.sql.SQLFunction} to a string.
* Converts the {@link patio.sql.Constant} to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.SQLFunction}.
* @return String the SQL version of the {@link patio.sql.Constant}.
*/
- -
toString:function (ds) {
- 565
-!Dataset && (Dataset = require("./dataset"));
- 565
-ds = ds || new Dataset();
- 565
+return ds.functionSql(this);
- 6
+!Dataset && (Dataset = require("./dataset"));
- 6
+ds = ds || new Dataset();
- 6
return ds.constantSql(this.__constant);
},
- -
/**@ignore*/
- -
getters:{
- -
f:function () {
- 567
-return this.__f;
- -
},
- -
- -
args:function () {
- 567
+return this.__args;
- +
constant:function () {
- 0
return this.__constant;
}
}
- -
}
- +
}).as(sql, "SQLFunction");
}).as(sql, "Constant");
- -
/**
- -
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- -
* in a numeric value in SQL.
- -
*
- +
* @name NumericExpression
- +
* @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
- +
* @auments patio.sql.Constant
* @name BooleanConstant
- -
* @memberOf patio.sql
- -
* @augments patio.sql.ComplexExpression
- -
* @augments patio.sql.BitWiseMethods
- -
* @augments patio.sql.NumericMethods
* @augments patio.sql.InequalityMethods
- -
*/
- 1
-var NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");
- -
- -
- 1
+var OrderedExpression = define(Expression, {
- 1
var BooleanConstant = define(Constant, {
- -
instance:{
- -
/**@lends patio.sql.OrderedExpression.prototype*/
- -
- -
/**
- -
* Represents a column/expression to order the result set by.
- -
* @constructs
- -
* @augments patio.sql.Expression
- -
*
- -
* @param expression the expression to order
- -
* @param {Boolean}[descending=true] set to false to order ASC
- -
* @param {String|Object} [opts=null] additional options
- -
* <ul>
- -
* <li>String: if value is "first" the null values will be first, if "last" then null values
- -
* will be last</li>
- -
* <li>Object: will pull the nulls property off of the object use use the same rules as if it
- -
* were a string</li>
- -
* </ul>
- -
* @property expression <b>READ ONLY</b> the expression to order.
- -
* @property {Boolean} [descending=true] <b>READ ONLY</b> true if decending, false otherwise.
- -
* @property {String} [nulls=null] if value is "first" the null values will be first, if "last" then null values
- -
* will be last
- -
*/
- -
constructor:function (expression, descending, opts) {
- 92
-descending = isBoolean(descending) ? descending : true;
- 92
-opts = opts || {};
- 92
-this.__expression = expression;
- 92
-this.__descending = descending;
- 92
-var nulls = isString(opts) ? opts : opts.nulls;
- 92
-this.__nulls = isString(nulls) ? nulls.toLowerCase() : null;
- -
},
- -
- -
/**
- -
* @return {patio.sql.OrderedExpression} a copy that is ordered ASC
- -
*/
- -
asc:function () {
- 0
-return new OrderedExpression(this.__expression, false, {nulls:this.__nulls});
- +
},
/**@lends patio.sql.BooleanConstant.prototype*/
- -
/**
- +
* @return {patio.sql.OrderedExpression} Return a copy that is ordered DESC
- +
* Converts the {@link patio.sql.BooleanConstant} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
* @return String the SQL version of the {@link patio.sql.BooleanConstant}.
- -
*/
- -
desc:function () {
- 0
-return new OrderedExpression(this.__expression, true, {nulls:this.__nulls});
- +
},
- +
toString:function (ds) {
- 10
+!Dataset && (Dataset = require("./dataset"));
- 10
+ds = ds || new Dataset();
- 10
+return ds.booleanConstantSql(this.__constant);
- +
}
- +
}
}).as(sql, "BooleanConstant");
- -
- -
/**
- -
* * @return {patio.sql.OrderedExpression} an inverted expression, changing ASC to DESC and NULLS FIRST to NULLS LAST.
- -
* */
- -
invert:function () {
- 17
-return new OrderedExpression(this.__expression, !this.__descending, {nulls:this._static.INVERT_NULLS[this.__nulls] || this.__nulls});
- +
},
- +
/**
- +
* Represents inverse boolean constants (currently only NOTNULL). A
- +
* special class to allow for special behavior.
- +
*
- +
* @augments patio.sql.BooleanConstant
- +
* @name NegativeBooleanConstant
- +
* @memberOf patio.sql
- +
*/
- 1
+var NegativeBooleanConstant = define(BooleanConstant, {
- +
instance:{
/**@lends patio.sql.NegativeBooleanConstant.prototype*/
- -
/**
- +
* Converts the {@link patio.sql.OrderedExpression} to a string.
* Converts the {@link patio.sql.NegativeBooleanConstant} to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.OrderedExpression}.
* @return String the SQL version of the {@link patio.sql.NegativeBooleanConstant}.
*/
- -
toString:function (ds) {
- 73
-!Dataset && (Dataset = require("./dataset"));
- 73
-ds = ds || new Dataset();
- 73
-return ds.orderedExpressionSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
expression:function () {
- 75
-return this.__expression;
- -
},
- -
descending:function () {
- 75
-return this.__descending;
- -
},
- -
nulls:function () {
- 82
-return this.__nulls;
- +
}
- 2
+!Dataset && (Dataset = require("./dataset"));
- 2
+ds = ds || new Dataset();
- 2
return ds.negativeBooleanConstantSql(this.__constant);
- -
}
- -
},
- -
static:{
- -
/**@lends patio.sql.OrderedExpression*/
- -
/**
- -
* Hash that contains the inversions for "first" and "last".
- -
* @type Object
- -
* @default {first:"last", last:"first"}
- -
*/
INVERT_NULLS:{first:"last", last:"first"}
- -
}
- +
}).as(sql, "OrderedExpression");
}).as(sql, "NegativeBooleanConstant");
- -
- 1
+var QualifiedIdentifier = define([GenericExpression, QualifyingMethods], {
- +
/**
- +
* @namespace Holds default generic constants that can be referenced. These
- +
* are included in {@link patio}
- +
* @name Constants
- +
* @memberOf patio.sql
- +
*/
- 1
+sql.Constants = {
- +
/**@lends patio.sql.Constants*/
- +
- +
/**
- +
* Constant for CURRENT DATE
- +
* @type patio.sql.Constant
- +
*/
- +
CURRENT_DATE:new Constant("CURRENT_DATE"),
- +
- +
/**
- +
* Constant for CURRENT TIME
- +
* @type patio.sql.Constant
- +
*/
- +
CURRENT_TIME:new Constant("CURRENT_TIME"),
- +
- +
/**
- +
* Constant for CURRENT TIMESTAMP
- +
* @type patio.sql.Constant
- +
*/
- +
CURRENT_TIMESTAMP:new Constant("CURRENT_TIMESTAMP"),
- +
- +
/**
- +
* Constant for TRUE
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
SQLTRUE:new BooleanConstant(1),
- +
- +
/**
- +
* Constant for TRUE
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
TRUE:new BooleanConstant(1),
- +
- +
/**
- +
* Constant for FALSE.
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
SQLFALSE:new BooleanConstant(0),
- +
- +
/**
- +
* Constant for FALSE
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
FALSE:new BooleanConstant(0),
- +
/**
- +
* Constant for NULL
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
NULL:new BooleanConstant(null),
- +
- +
/**
- +
* Constant for NOT NULL
- +
* @type patio.sql.NegativeBooleanConstant
- +
*/
- +
NOTNULL:new NegativeBooleanConstant(null)
- +
- +
};
- +
- 1
+var Constants = sql.Constants
- +
- 1
var Identifier = define([GenericExpression, QualifyingMethods], {
- -
instance:{
- +
/**@lends patio.sql.QualifiedIdentifier.prototype*/
/**@lends patio.sql.Identifier.prototype*/
- -
/**
- +
* Represents a qualified identifier (column with table or table with schema).
- +
* Represents an identifier (column or table). Can be used
- +
* to specify a String with multiple underscores that should not be
* split, or for creating an implicit identifier without using a String.
*
* @constructs
* @augments patio.sql.GenericExpression
* @augments patio.sql.QualifyingMethods
- -
*
- -
* @param table the table or schema to qualify the column or table to.
- +
* @param column the column or table to qualify.
* @param {String}value the identifier.
- -
*
- -
* @property table <b>READ ONLY</b> the table or schema to qualify the column or table to.
- +
* @property column <b>READ ONLY</b> he column or table to qualify.
* @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
- -
*/
- -
constructor:function (table, column) {
- 4451
-this.__table = table;
- 4451
+this.__column = column;
- +
constructor:function (value) {
- 16414
this.__value = value;
},
- -
/**
- +
* Converts the {@link patio.sql.QualifiedIdentifier} to a string.
* Converts the {@link patio.sql.Identifier} to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.QualifiedIdentifier}.
* @return String the SQL version of the {@link patio.sql.Identifier}.
*/
- -
toString:function (ds) {
- 4532
-!Dataset && (Dataset = require("./dataset"));
- 4532
-ds = ds || new Dataset();
- 4532
+return ds.qualifiedIdentifierSql(this);
- 21906
+!Dataset && (Dataset = require("./dataset"));
- 21906
+ds = ds || new Dataset();
- 21906
return ds.quoteIdentifier(this);
},
/**@ignore*/
- -
getters:{
- -
table:function () {
- 4554
-return this.__table;
- -
},
- -
- -
column:function () {
- 4559
+return this.__column;
- +
value:function () {
- 25154
return this.__value;
}
}
- -
}
- -
}).as(sql, "QualifiedIdentifier");
- +
}).as(sql, "Identifier");
- -
- 1
-var likeElement = function (re) {
- 875
-var ret;
- 875
-if (isRegExp(re)) {
- 80
-ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase]
- -
} else {
- 795
-ret = [re, false, false];
- -
}
- 875
-return ret;
- -
};
- -
/**
- -
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- -
* in a text/string/varchar value in SQL.
- -
*
- -
* @augments patio.sql.ComplexExpression
- -
* @augments patio.sql.StringMethods
- -
* @augments patio.sql.StringConcatenationMethods
- -
* @augments patio.sql.InequalityMethods
- -
* @augments patio.sql.NoBooleanInputMethods
- -
* @name StringExpression
- -
* @memberOf patio.sql
- -
*/
- 1
-var StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {
- -
static:{
- +
/**@lends patio.sql.StringExpression*/
- 1
+var JoinClause = define(Expression, {
- +
instance:{
/**@lends patio.sql.JoinClause.prototype*/
- -
/**
- -
* <p>Creates a SQL pattern match expression. left (l) is the SQL string we
- -
* are matching against, and ces are the patterns we are matching.
- +
* The match succeeds if any of the patterns match (SQL OR).</p>
- +
* Represents an SQL JOIN clause, used for joining tables.
- +
* Created by {@link patio.Dataset} join methods.
- +
* @constructs
* @augments patio.sql.Expression
- -
*
- -
* <p>If a regular expression is used as a pattern, an SQL regular expression will be
- -
* used, which is currently only supported on MySQL and PostgreSQL. Be aware
- -
* that MySQL and PostgreSQL regular expression syntax is similar to javascript
- -
* regular expression syntax, but it not exactly the same, especially for
- -
* advanced regular expression features. Patio just uses the source of the
- +
* regular expression verbatim as the SQL regular expression string.</p>
- +
* @param {String} joinType the type of join this JoinClause should use
- +
* @param table the table to join with
* @param tableAlias the alias to use for this join clause
- -
*
- -
* <p>If any other object is used as a regular expression, the SQL LIKE operator will
- +
* be used, and should be supported by most databases.</p>
- +
* @property {String} joinType <b>READ ONLY</b> the type of join this JoinClause should use
- +
* @property table <b>READ ONLY</b> the table to join with
- +
* @property joinType <b>READ ONLY</b> the alias to use for this join clause
- +
* */
- +
constructor:function (joinType, table, tableAlias) {
- 791
+this.__joinType = joinType;
- 791
+this.__table = table;
- 791
+this.__tableAlias = tableAlias || null;
- +
},
- +
- +
/**
* Converts the {@link patio.sql.JoinClause} to a string.
- -
*
- -
* <p>The pattern match will be case insensitive if the last argument is a hash
- -
* with a key of caseInsensitive that is not false or null. Also,
- -
* if a case insensitive regular expression is used (//i), that particular
- +
* pattern which will always be case insensitive.</p>
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- -
* @example
- -
* StringExpression.like(sql.a, 'a%') //=> "a" LIKE 'a%'
- -
* StringExpression.like(sql.a, 'a%', {caseInsensitive : true}) //=> "a" ILIKE 'a%'
- +
* StringExpression.like(sql.a, 'a%', /^a/i) //=> "a" LIKE 'a%' OR "a" ~* '^a'
* @return String the SQL version of the {@link patio.sql.JoinClause}.
- -
*/
- -
like:function (l) {
- 437
-var args = argsToArray(arguments, 1);
- 437
-var params = likeElement(l);
- 437
-var likeMap = this.likeMap;
- 437
-var lh = params[0], lre = params[1], lci = params[2];
- 437
-var last = args[args.length - 1];
- 437
-lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;
- 437
-args = args.map(function (ce) {
- 438
-var r, rre, rci;
- 438
-var ceArr = likeElement(ce);
- 438
-r = ceArr[0], rre = ceArr[1], rci = ceArr[2];
- 438
-return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r)
- -
}, this);
- 437
+return args.length == 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));
- +
toString:function (ds) {
- 17
+!Dataset && (Dataset = require("./dataset"));
- 17
+ds = ds || new Dataset();
- 17
return ds.joinClauseSql(this);
},
- -
- -
/**
- -
* Like map used to by {@link patio.sql.StringExpression.like} to create the
- -
* LIKE expression.
- -
* @type Object
- -
*/
- +
likeMap:{"truetrue":'~*', "truefalse":"~", "falsetrue":"ILIKE", "falsefalse":"LIKE"}
- +
/**@ignore*/
- +
getters:{
- +
joinType:function () {
- 927
+return this.__joinType;
},
- +
- +
table:function () {
- 928
+return this.__table;
},
- +
- +
tableAlias:function () {
- 926
+return this.__tableAlias;
- +
}
}
- -
}
- +
}).as(sql, "StringExpression");
}).as(sql, "JoinClause");
- -
- 1
-var SubScript = define(GenericExpression, {
- -
instance:{
/**@lends patio.sql.SubScript.prototype*/
- +
- 1
+var JoinOnClause = define(JoinClause, {
- +
instance:{
/**@lends patio.sql.JoinOnClause.prototype*/
- -
/**
- +
* Represents an SQL array access, with multiple possible arguments.
- +
* Represents an SQL JOIN clause with ON conditions. Created by {@link patio.Dataset} join methods.
* See {@link patio.sql.JoinClause} for other argument parameters.
- -
* @constructs
- +
* @augments patio.sql.GenericExpression
* @augments patio.sql.JoinClause
- -
*
- -
* @param arrCol the SQL array column
- -
* @param sub The array of subscripts to use (should be an array of numbers)
- -
*/
- -
constructor:function (arrCol, sub) {
- -
//The SQL array column
- 109
-this.__arrCol = arrCol;
- -
//The array of subscripts to use (should be an array of numbers)
- 109
-this.__sub = sub;
- -
},
- -
- -
/**
- -
* Create a new {@link patio.sql.Subscript} appending the given subscript(s)
- +
* the the current array of subscripts.
- +
* @param on the expression to filter with. See {@link patio.Dataset#filter}
* @property on <b>READ ONLY</b> the filter to use with joining the datasets.
- -
*/
- -
addSub:function (sub) {
- 0
+return new SubScript(this.__arrCol, this.__sub.concat(sub));
- +
constructor:function (on, joinType, table, tableAlias) {
- 761
+this.__on = on;
- 761
this._super(arguments, [joinType, table, tableAlias]);
},
- -
/**
- +
* Converts the {@link patio.sql.SubScript} to a string.
* Converts the {@link patio.sql.JoinOnClause} to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.SubScript}.
* @return String the SQL version of the {@link patio.sql.JoinOnClause}.
*/
- -
toString:function (ds) {
- 109
-!Dataset && (Dataset = require("./dataset"));
- 109
-ds = ds || new Dataset();
- 109
+return ds.subscriptSql(this);
- 813
+!Dataset && (Dataset = require("./dataset"));
- 813
+ds = ds || new Dataset();
- 813
return ds.joinOnClauseSql(this);
},
/**@ignore*/
- -
getters:{
- -
f:function () {
- 110
-return this.__arrCol;
- -
},
- -
- -
sub:function () {
- 110
+return this.__sub;
- +
on:function () {
- 813
return this.__on;
}
}
- -
}
- -
}).as(sql, "SubScript");
- -
- 1
-var STRING_METHODS = ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "quote",
- -
"replace", "search", "slice", "split", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase",
- -
"toSource", "toString", "toUpperCase", "trim", "trimLeft", "trimRight", "valueOf"];
- +
}).as(sql, "JoinOnClause");
- -
- 1
-var addStringMethod = function (op) {
- 24
-return function () {
- 4294
-return this.__str[op].apply(this.__str, arguments);
- -
}
};
- -
- 1
+var LiteralString = define([OrderedMethods, ComplexExpressionMethods, BooleanMethods, NumericMethods, StringMethods, InequalityMethods, AliasMethods], {
- 1
var JoinUsingClause = define(JoinClause, {
- -
instance:{
- +
/**@lends patio.sql.LiteralString*/
/**@lends patio.sql.JoinUsingClause.prototype*/
- -
/**
- -
* Represents a string that should be placed into a SQL query literally.
- +
* <b>This class has all methods that a normal javascript String has.</b>
- +
* Represents an SQL JOIN clause with USING conditions.
- +
* Created by {@link patio.Dataset} join methods.
- +
* See {@link patio.sql.JoinClause} for other argument parameters.
*
- -
* @constructs
- -
* @augments patio.sql.OrderedMethods
- -
* @augments patio.sql.ComplexExpressionMethods
- -
* @augments patio.sql.BooleanMethods
- -
* @augments patio.sql.NumericMethods
- -
* @augments patio.sql.StringMethods
- -
* @augments patio.sql.InequalityMethods
- +
* @augments patio.sql.AliasMethods
* @augments patio.sql.JoinClause
- -
*
- +
* @param {String} str the literal string.
- +
* @param using the column/s to use when joining.
* @property using <b>READ ONLY</b> the column/s to use when joining.
- -
*/
- -
constructor:function (str) {
- 3677
-this.__str = str;
- -
}
- -
}
- -
}).as(sql, "LiteralString");
- -
- 1
-STRING_METHODS.forEach(function (op) {
- 24
-LiteralString.prototype[op] = addStringMethod(op);
- -
}, this);
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- +
- +
constructor:function (using, joinType, table, tableAlias) {
- 8
+this.__using = using.map(function (u) {
- 9
+return isString(u) ? new Identifier(u) : u;
- +
});
- 8
+this._super(arguments, [joinType, table, tableAlias]);
},
- +
- +
/**
- +
* Converts the {@link patio.sql.JoinUsingClause} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL version of the {@link patio.sql.JoinUsingClause}.
- +
*/
- +
toString:function (ds) {
- 95
+!Dataset && (Dataset = require("./dataset"));
- 95
+ds = ds || new Dataset();
- 95
+return ds.joinUsingClauseSql(this);
},
- -
- migration.js
- |
-
-
- Coverage91.97
- SLOC607
- LOC274
- Missed22
-
- |
-
- 1
-var comb = require("comb"),
- -
hitch = comb.hitch,
- -
Promise = comb.Promise,
- -
errors = require("./errors"),
- -
MigrationError = errors.MigrationError,
- -
NotImplemented = errors.NotImplemented(),
- -
format = comb.string.format,
- -
define = comb.define,
- -
isFunction = comb.isFunction,
- -
serial = comb.serial,
- -
isNumber = comb.isNumber,
- -
when = comb.when,
- -
isUndefined = comb.isUndefined,
- -
fs = require("fs"),
- +
path = require("path");
- +
/**@ignore*/
- +
getters:{
- +
using:function () {
- 95
+return this.__using;
- +
}
- +
}
- +
}
}).as(sql, "JoinUsingClause");
- -
- 1
+var Migrator = define(null, {
- 1
var PlaceHolderLiteralString = define(GenericExpression, {
- -
instance:{
- -
/**@lends patio.migrations.Migrator.prototype*/
- -
column:null,
- -
db:null,
- -
directory:null,
- -
ds:null,
- -
files:null,
- -
table:null,
- +
target:null,
/**@lends patio.sql.PlaceHolderLiteralString.prototype*/
- -
/**
- +
* Abstract Migrator class. This class should be be instantiated directly.
- +
* Represents a literal string with placeholders and arguments.
- +
* This is necessary to ensure delayed literalization of the arguments
- +
* required for the prepared statement support and for database-specific
* literalization.
*
- -
* @constructs
- -
* @param {patio.Database} db the database to migrate
- -
* @param {String} directory directory that the migration files reside in
- -
* @param {Object} [opts={}] optional parameters.
- -
* @param {String} [opts.column] the column in the table that version information should be stored.
- -
* @param {String} [opts.table] the table that version information should be stored.
- -
* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
- +
* @param {String} [opts.current] the version that the database is currently at if the current version
- +
* @augments patio.sql.GenericExpression
- +
*
- +
* @param {String} str the string that contains placeholders.
- +
* @param {Array} args array of arguments that will be literalized using {@link patio.Dataset#literal}, and
- +
* replaced in the string.
- +
* @param {Boolean} [parens=false] set to true to wrap the string in parens.
- +
*
- +
* @property {String} str <b>READ ONLY</b> the string that contains placeholders.
- +
* @property {Array} args <b>READ ONLY</b> array of arguments that will be literalized using {@link patio.Dataset#literal}, and
- +
* replaced in the string.
* @property {String} parens <b>READ ONLY</b> set to true to wrap the string in parens.
- -
*/
- -
constructor:function (db, directory, opts) {
- 31
-this.db = db;
- 31
-this.directory = directory;
- 31
-opts = opts || {};
- 31
-this.table = opts.table || this._static.DEFAULT_SCHEMA_TABLE;
- 31
-this.column = opts.column || this._static.DEFAULT_SCHEMA_COLUMN;
- 31
+this._opts = opts;
- +
constructor:function (str, args, parens) {
- 53
+parens = parens || false;
- 53
+var v;
- 53
+this.__str = str;
- 53
+this.__args = isArray(args) && args.length == 1 && isHash((v = args[0])) ? v : args;
- 53
this.__parens = parens;
},
- -
/**
- +
* Runs the migration and returns a promise.
- +
* Converts the {@link patio.sql.PlaceHolderLiteralString} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
* @return String the SQL version of the {@link patio.sql.PlaceHolderLiteralString}.
- -
*/
- -
run:function () {
- 0
+throw new NotImplemented("patio.migrations.Migrator#run");
- +
toString:function (ds) {
- 52
+!Dataset && (Dataset = require("./dataset"));
- 52
+ds = ds || new Dataset();
- 52
return ds.placeholderLiteralStringSql(this);
},
- -
- -
getFileNames:function () {
- 50
-if (!this.__files) {
- 49
-return this._static.getFileNames(this.directory).addCallback(hitch(this, function (files) {
- 49
-this.__files = files;
- -
}));
- -
} else {
- 1
+return new Promise().callback(this.__files).promise();
- +
/**@ignore*/
- +
getters:{
- +
str:function () {
- 55
+return this.__str;
- +
},
- +
args:function () {
- 55
+return this.__args;
- +
},
- +
- +
parens:function () {
- 55
return this.__parens;
- -
}
},
- -
- -
getMigrationVersionFromFile:function (filename) {
- 476
return parseInt(path.basename(filename).split(this._static.MIGRATION_SPLITTER)[0], 10);
- -
}
- -
},
- -
- -
"static":{
- +
/**@lends patio.migrations.Migrator*/
- +
}
}).as(sql, "PlaceHolderLiteralString");
- -
- -
MIGRATION_FILE_PATTERN:/^\d+\..+\.js$/i,
- -
MIGRATION_SPLITTER:'.',
MINIMUM_TIMESTAMP:20000101,
- -
- -
getFileNames:function (directory) {
- 80
-var ret = new Promise();
- 80
-fs.readdir(directory, hitch(this, function (err, files) {
- 80
-if (err) {
- 0
-ret.errback(err);
- -
} else {
- 80
-files = files.filter(function (file) {
- 394
-return file.match(this.MIGRATION_FILE_PATTERN) !== null;
- -
}, this);
- 80
-ret.callback(files.map(function (file) {
- 394
-return path.resolve(directory, file);
- -
}));
- -
}
- -
}));
- 80
-return ret.promise();
- +
},
- 1
+var SQLFunction = define(GenericExpression, {
- +
instance:{
/**@lends patio.sql.SQLFunction.prototype*/
- -
/**
- -
* Migrates the database using migration files found in the supplied directory.
- +
* See {@link patio#migrate}
* Represents an SQL function call.
- -
*
- -
* @example
- -
* var DB = patio.connect("my://connection/string");
- -
* patio. migrate(DB, __dirname + "/timestamp_migration").then(function(){
- -
* console.log("done migrating!");
- +
* });
- +
* @constructs
* @augments patio.sql.GenericExpression
- -
*
- -
* patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).then(function(){
- -
* console.log("done migrating down!");
- +
* });
- +
* @param {...} f variable number of arguments where the first argument is the name
- +
* of the SQL function to invoke. The rest of the arguments will be literalized through
* {@link patio.Dataset#literal} and placed into the SQL function call.
- +
*
- +
* @property {String} f <b>READ ONLY</b> the SQL function to call.
- +
* @property {Array} args <b>READ ONLY</b> args arguments will be literalized through
- +
* {@link patio.Dataset#literal} and placed into the SQL function call.
- +
* */
- +
constructor:function (f) {
- 1109
+var args = argsToArray(arguments).slice(1);
- 1109
+this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {
- 773
+return isString(a) ? sql.stringToIdentifier(a) : a;
- +
});
- +
},
- +
- +
/**
* Converts the {@link patio.sql.SQLFunction} to a string.
- -
*
- -
* @param {patio.Database} db the database to migrate
- -
* @param {String} directory directory that the migration files reside in
- -
* @param {Object} [opts={}] optional parameters.
- -
* @param {String} [opts.column] the column in the table that version information should be stored.
- -
* @param {String} [opts.table] the table that version information should be stored.
- -
* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
- -
* @param {String} [opts.current] the version that the database is currently at if the current version
- +
* is not provided it is retrieved from the database.
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return {Promise} a promise that is resolved once the migration is complete.
* @return String the SQL version of the {@link patio.sql.SQLFunction}.
- -
*/
- -
run:function (db, directory, opts, cb) {
- 31
-if (isFunction(opts)) {
- 0
-cb = opts;
- 0
-opts = {};
- -
} else {
- 31
-opts = opts || {};
- -
}
- 31
-opts = opts || {};
- 31
-var ret = new Promise();
- 31
-this.__getMigrator(directory).then(function (migrator) {
- 31
-new migrator(db, directory, opts).run().then(ret);
- -
}, ret);
- 31
-ret.classic(cb);
- 31
+return ret.promise();
- +
toString:function (ds) {
- 565
+!Dataset && (Dataset = require("./dataset"));
- 565
+ds = ds || new Dataset();
- 565
return ds.functionSql(this);
},
- -
- -
// Choose the Migrator subclass to use. Uses the TimestampMigrator
- -
// // if the version number appears to be a unix time integer for a year
- -
// after 2005, otherwise uses the IntegerMigrator.
- -
__getMigrator:function (directory) {
- 31
-var ret = new Promise();
- 31
-var retClass = IntegerMigrator;
- 31
-this.getFileNames(directory).then(hitch(this, function (files) {
- 31
-var l = files.length;
- 31
-if (l) {
- 31
-for (var i = 0; i < l; i++) {
- 81
-var file = files[i];
- 81
-if (parseInt(path.basename(file).split(this.MIGRATION_SPLITTER)[0], 10) > this.MINIMUM_TIMESTAMP) {
- 18
-retClass = TimestampMigrator;
- 18
-break;
- -
}
- -
}
- -
}
- 31
+ret.callback(retClass);
- +
/**@ignore*/
- +
getters:{
- +
f:function () {
- 567
+return this.__f;
},
- -
- -
}), ret);
- 31
+return ret.promise();
- +
args:function () {
- 567
+return this.__args;
}
}
- -
}
- -
});
- +
}).as(sql, "SQLFunction");
- -
/**
- -
* @class Migrator that uses the file format {migrationName}.{version}.js, where version starts at 0.
- +
* <b>Missing migrations are not allowed</b>
- +
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
* in a numeric value in SQL.
- -
*
- -
* @augments patio.migrations.Migrator
- -
* @name IntegerMigrator
- +
* @memberOf patio.migrations
- +
* @name NumericExpression
- +
* @memberOf patio.sql
- +
* @augments patio.sql.ComplexExpression
- +
* @augments patio.sql.BitWiseMethods
- +
* @augments patio.sql.NumericMethods
* @augments patio.sql.InequalityMethods
- -
*/
- 1
-var IntegerMigrator = define(Migrator, {
- -
instance:{
- -
/**@lends patio.migrations.IntegerMigrator.prototype*/
- -
current:null,
- -
direction:null,
- +
migrations:null,
- 1
var NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");
- -
_migrationFiles:null,
- -
- -
run:function () {
- 13
-var ret = new Promise(), DB = this.db;
- 13
-serial([this._getLatestMigrationVersion.bind(this), this._getCurrentMigrationVersion.bind(this)]).then(hitch(this, function (res) {
- 11
-var target = res[0], current = res[1];
- 11
-if (current !== target) {
- 11
-var direction = this.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;
- 11
-this._getMigrations(current, target, direction).then(hitch(this, function (migrations) {
- 11
-var runMigration = hitch(this, function (index) {
- 60
-if (index >= migrations.length) {
- 11
-ret.callback(version);
- -
} else {
- 49
-var curr = migrations[index], migration = curr[0];
- 49
-version = curr[1];
- 49
-var now = new Date();
- 49
-var lv = isUp ? version : version - 1;
- 49
-DB.logInfo("Begin applying migration version %d, direction: %s", lv, direction);
- 49
-DB.transaction(hitch(this, function () {
- 49
-var ret = new Promise();
- 49
-if (!isFunction(migration[direction])) {
- 0
-this._setMigrationVersion(lv).then(ret);
- -
} else {
- 49
-when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
- 49
-this._setMigrationVersion(lv).then(ret);
- -
}), ret);
- -
}
- 49
-return ret.promise();
- -
})).then(function () {
- 49
-DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);
- 49
-runMigration(index + 1);
- -
}, ret);
- +
}
- 1
+var OrderedExpression = define(Expression, {
- +
instance:{
/**@lends patio.sql.OrderedExpression.prototype*/
- -
- -
});
- 11
-runMigration(0);
- -
}), ret);
- -
} else {
- 0
-ret.callback(target);
- +
}
- +
/**
- +
* Represents a column/expression to order the result set by.
- +
* @constructs
- +
* @augments patio.sql.Expression
- +
*
- +
* @param expression the expression to order
- +
* @param {Boolean}[descending=true] set to false to order ASC
- +
* @param {String|Object} [opts=null] additional options
- +
* <ul>
- +
* <li>String: if value is "first" the null values will be first, if "last" then null values
- +
* will be last</li>
- +
* <li>Object: will pull the nulls property off of the object use use the same rules as if it
- +
* were a string</li>
- +
* </ul>
- +
* @property expression <b>READ ONLY</b> the expression to order.
- +
* @property {Boolean} [descending=true] <b>READ ONLY</b> true if decending, false otherwise.
- +
* @property {String} [nulls=null] if value is "first" the null values will be first, if "last" then null values
- +
* will be last
- +
*/
- +
constructor:function (expression, descending, opts) {
- 92
+descending = isBoolean(descending) ? descending : true;
- 92
+opts = opts || {};
- 92
+this.__expression = expression;
- 92
+this.__descending = descending;
- 92
+var nulls = isString(opts) ? opts : opts.nulls;
- 92
+this.__nulls = isString(nulls) ? nulls.toLowerCase() : null;
},
- -
- -
}), ret);
- 13
+return ret.promise();
- +
/**
- +
* @return {patio.sql.OrderedExpression} a copy that is ordered ASC
- +
*/
- +
asc:function () {
- 0
return new OrderedExpression(this.__expression, false, {nulls:this.__nulls});
},
- -
- -
_getMigrations:function (current, target, direction) {
- 11
-var ret = new Promise(), isUp = direction === "up", migrations = [];
- 11
-when(this._getMigrationFiles()).then(function (files) {
- 11
-try {
- 11
-if ((isUp ? target : current - 1) < files.length) {
- 11
-if (isUp) {
- 8
-current++;
- -
}
- 11
-for (; isUp ? current <= target : current > target; isUp ? current++ : current--) {
- 49
-migrations.push([require(files[current]), current]);
- -
}
- -
} else {
- 0
-return ret.errback(new MigrationError("Invalid target " + target));
- -
}
- -
} catch (e) {
- 0
-return ret.errback(e);
- -
}
- 11
-ret.callback(migrations);
- -
}, ret);
- 11
+return ret.promise();
- +
/**
- +
* @return {patio.sql.OrderedExpression} Return a copy that is ordered DESC
- +
*/
- +
desc:function () {
- 0
+return new OrderedExpression(this.__expression, true, {nulls:this.__nulls});
- +
},
- +
- +
/**
- +
* * @return {patio.sql.OrderedExpression} an inverted expression, changing ASC to DESC and NULLS FIRST to NULLS LAST.
- +
* */
- +
invert:function () {
- 17
return new OrderedExpression(this.__expression, !this.__descending, {nulls:this._static.INVERT_NULLS[this.__nulls] || this.__nulls});
},
- +
- +
/**
- +
* Converts the {@link patio.sql.OrderedExpression} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL version of the {@link patio.sql.OrderedExpression}.
- +
*/
- +
toString:function (ds) {
- 73
+!Dataset && (Dataset = require("./dataset"));
- 73
+ds = ds || new Dataset();
- 73
+return ds.orderedExpressionSql(this);
},
- -
- -
_getMigrationFiles:function () {
- 19
-var ret = new Promise();
- 19
-if (!this._migrationFiles) {
- 13
-var retFiles = [];
- 13
-var directory = this.directory;
- 13
-this.getFileNames().then(hitch(this, function (files) {
- 13
-var l = files.length;
- 13
-if (l) {
- 13
-for (var i = 0; i < l; i++) {
- 59
-var file = files[i];
- 59
-var version = this.getMigrationVersionFromFile(file);
- 59
-if (isUndefined(retFiles[version])) {
- 58
-retFiles[version] = file;
- -
} else {
- 1
-return ret.errback(new MigrationError("Duplicate migration number " + version));
- -
}
- -
}
- 12
-if (isUndefined(retFiles[0])) {
- 0
-retFiles.shift();
- -
}
- 12
-for (var j = 0; j < l; j++) {
- 57
-if (isUndefined(retFiles[j])) {
- 1
-return ret.errback(new MigrationError("Missing migration for " + j));
- -
}
- -
}
- -
}
- 11
-this._migrationFiles = retFiles;
- 11
-ret.callback(retFiles);
- -
}), ret);
- -
} else {
- 6
+ret.callback(this._migrationFiles);
- +
/**@ignore*/
- +
getters:{
- +
expression:function () {
- 75
+return this.__expression;
- +
},
- +
descending:function () {
- 75
+return this.__descending;
- +
},
- +
nulls:function () {
- 82
return this.__nulls;
- -
}
- 19
+return ret.promise();
- +
}
- +
},
- +
static:{
- +
/**@lends patio.sql.OrderedExpression*/
- +
/**
- +
* Hash that contains the inversions for "first" and "last".
- +
* @type Object
- +
* @default {first:"last", last:"first"}
- +
*/
- +
INVERT_NULLS:{first:"last", last:"first"}
- +
}
- +
}).as(sql, "OrderedExpression");
- +
- 1
+var QualifiedIdentifier = define([GenericExpression, QualifyingMethods], {
- +
instance:{
- +
/**@lends patio.sql.QualifiedIdentifier.prototype*/
- +
- +
/**
- +
* Represents a qualified identifier (column with table or table with schema).
- +
*
- +
* @constructs
- +
* @augments patio.sql.GenericExpression
- +
* @augments patio.sql.QualifyingMethods
- +
*
- +
* @param table the table or schema to qualify the column or table to.
- +
* @param column the column or table to qualify.
- +
*
- +
* @property table <b>READ ONLY</b> the table or schema to qualify the column or table to.
- +
* @property column <b>READ ONLY</b> he column or table to qualify.
- +
*/
- +
constructor:function (table, column) {
- 4451
+this.__table = table;
- 4451
this.__column = column;
},
- -
- -
_getLatestMigrationVersion:function () {
- 13
-var ret = new Promise();
- 13
-if (!isUndefined(this._opts.target)) {
- 5
-ret.callback(this._opts.target);
- -
} else {
- 8
-this._getMigrationFiles().then(hitch(this, function (files) {
- 6
-var l = files[files.length - 1];
- 6
-ret.callback(l ? this.getMigrationVersionFromFile(path.basename(l)) : null);
- -
}), ret);
- -
}
- 13
+return ret.promise();
- +
/**
- +
* Converts the {@link patio.sql.QualifiedIdentifier} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL version of the {@link patio.sql.QualifiedIdentifier}.
- +
*/
- +
toString:function (ds) {
- 4532
+!Dataset && (Dataset = require("./dataset"));
- 4532
+ds = ds || new Dataset();
- 4532
return ds.qualifiedIdentifierSql(this);
},
- -
- -
_getCurrentMigrationVersion:function () {
- 11
-var ret = new Promise();
- 11
-if (!isUndefined(this._opts.current)) {
- 2
-ret.callback(this._opts.current);
- -
} else {
- 9
-when(this._getSchemaDataset()).then(hitch(this, function (ds) {
- 9
-ds.get(this.column).then(ret);
- +
}), ret);
- +
/**@ignore*/
- +
getters:{
- +
table:function () {
- 4554
+return this.__table;
- +
},
- +
- +
column:function () {
- 4559
return this.__column;
- -
}
- 11
-return ret.promise();
- +
},
- +
}
- +
}
}).as(sql, "QualifiedIdentifier");
- -
- -
_setMigrationVersion:function (version) {
- 49
-var ret = new Promise(), c = this.column;
- 49
-this._getSchemaDataset().then(function (ds) {
- 49
-var item = {};
- 49
-item[c] = version;
- 49
-ds.update(item).both(ret);
}, ret);
- -
- 49
-return ret.promise();
- +
},
- 1
+var likeElement = function (re) {
- 875
+var ret;
- 875
+if (isRegExp(re)) {
- 80
+ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase]
- +
} else {
- 795
+ret = [re, false, false];
- +
}
- 875
+return ret;
- +
};
- +
/**
- +
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- +
* in a text/string/varchar value in SQL.
- +
*
- +
* @augments patio.sql.ComplexExpression
- +
* @augments patio.sql.StringMethods
- +
* @augments patio.sql.StringConcatenationMethods
- +
* @augments patio.sql.InequalityMethods
- +
* @augments patio.sql.NoBooleanInputMethods
- +
* @name StringExpression
- +
* @memberOf patio.sql
- +
*/
- 1
+var StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {
- +
static:{
/**@lends patio.sql.StringExpression*/
- -
- -
_getSchemaDataset:function () {
- 58
-var c = this.column, table = this.table;
- 58
-var ret = new Promise();
- 58
-if (!this.__schemaDataset) {
- 11
-var ds = this.db.from(table);
- 11
-this.__createOrAlterMigrationTable().then(hitch(this, function () {
- 11
-ds.isEmpty().then(hitch(this, function (empty) {
- 11
-if (empty) {
- 0
-var item = {};
- 0
-item[c] = -1;
- 0
-this.__schemaDataset = ds;
- 0
-ds.insert(item).then(hitch(ret, "callback", ds), ret);
- -
} else {
- 11
-ds.count().then(hitch(this, function (count) {
- 11
-if (count > 1) {
- 0
-ret.errback(new Error("More than one row in migrator table"));
- -
} else {
- 11
-this.__schemaDataset = ds;
- 11
-ret.callback(ds);
- -
}
- -
}), ret);
- -
}
- -
}), ret);
- -
}), ret);
- -
} else {
- 47
-ret.callback(this.__schemaDataset);
- -
}
- 58
+return ret.promise();
- +
/**
- +
* <p>Creates a SQL pattern match expression. left (l) is the SQL string we
- +
* are matching against, and ces are the patterns we are matching.
- +
* The match succeeds if any of the patterns match (SQL OR).</p>
- +
*
- +
* <p>If a regular expression is used as a pattern, an SQL regular expression will be
- +
* used, which is currently only supported on MySQL and PostgreSQL. Be aware
- +
* that MySQL and PostgreSQL regular expression syntax is similar to javascript
- +
* regular expression syntax, but it not exactly the same, especially for
- +
* advanced regular expression features. Patio just uses the source of the
- +
* regular expression verbatim as the SQL regular expression string.</p>
- +
*
- +
* <p>If any other object is used as a regular expression, the SQL LIKE operator will
- +
* be used, and should be supported by most databases.</p>
- +
*
- +
* <p>The pattern match will be case insensitive if the last argument is a hash
- +
* with a key of caseInsensitive that is not false or null. Also,
- +
* if a case insensitive regular expression is used (//i), that particular
- +
* pattern which will always be case insensitive.</p>
- +
*
- +
* @example
- +
* StringExpression.like(sql.a, 'a%') //=> "a" LIKE 'a%'
- +
* StringExpression.like(sql.a, 'a%', {caseInsensitive : true}) //=> "a" ILIKE 'a%'
- +
* StringExpression.like(sql.a, 'a%', /^a/i) //=> "a" LIKE 'a%' OR "a" ~* '^a'
- +
*/
- +
like:function (l) {
- 437
+var args = argsToArray(arguments, 1);
- 437
+var params = likeElement(l);
- 437
+var likeMap = this.likeMap;
- 437
+var lh = params[0], lre = params[1], lci = params[2];
- 437
+var last = args[args.length - 1];
- 437
+lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;
- 437
+args = args.map(function (ce) {
- 438
+var r, rre, rci;
- 438
+var ceArr = likeElement(ce);
- 438
+r = ceArr[0], rre = ceArr[1], rci = ceArr[2];
- 438
+return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r)
- +
}, this);
- 437
return args.length == 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));
},
- -
- -
__createOrAlterMigrationTable:function () {
- 11
-var c = this.column, table = this.table, db = this.db;
- 11
-var ds = this.db.from(table);
- 11
-var ret = new Promise();
- 11
-db.tableExists(table).then(hitch(this, function (exists) {
- 11
-if (!exists) {
- 6
-db.createTable(table,
- -
function () {
- 6
-this.column(c, "integer", {"default":-1, allowNull:false});
- -
}).then(ret);
- -
} else {
- 5
-ds.columns.then(function (columns) {
- 5
-if (columns.indexOf(c) === -1) {
- 1
-db.addColumn(table, c, "integer", {"default":-1, allowNull:false})
- -
.then(ret);
- -
} else {
- 4
-ret.callback();
- -
}
- -
});
- -
}
- -
}), ret);
- 11
-return ret.promise();
- +
}
- +
/**
- +
* Like map used to by {@link patio.sql.StringExpression.like} to create the
- +
* LIKE expression.
- +
* @type Object
- +
*/
likeMap:{"truetrue":'~*', "truefalse":"~", "falsetrue":"ILIKE", "falsefalse":"LIKE"}
- -
},
- -
- -
static:{
- -
DEFAULT_SCHEMA_COLUMN:"version",
DEFAULT_SCHEMA_TABLE:"schema_info"
- -
}
- -
}).as(exports, "IntegerMigrator");
- +
}).as(sql, "StringExpression");
- -
- -
/**
- -
* @class Migrator that uses the file format {migrationName}.{timestamp}.js, where the timestamp
- -
* can be anything greater than 20000101.
- -
*
- -
* @name TimestampMigrator
- -
* @augments patio.migrations.Migrator
- -
* @memberOf patio.migrations
- -
*/
- 1
+var TimestampMigrator = define(Migrator, {
- 1
var SubScript = define(GenericExpression, {
- +
instance:{
/**@lends patio.sql.SubScript.prototype*/
- -
- -
constructor:function (db, directory, opts) {
- 18
-this._super(arguments);
- 18
-opts = opts || {};
- 18
-this.target = opts.target;
- -
},
- -
- -
run:function () {
- 18
-var ret = new Promise(), DB = this.db, column = this.column;
- 18
-serial([this.__getMirationFiles.bind(this), this._getSchemaDataset.bind(this)]).then(function (res) {
- 18
-var migrations = res[0], ds = res[1];
- 18
-var runMigration = hitch(this, function (index) {
- 86
-if (index >= migrations.length) {
- 16
-ret.callback();
- -
} else {
- 70
-var curr = migrations[index], file = curr[0], migration = curr[1], direction = curr[2];
- 70
-var now = new Date();
- 70
-DB.logInfo("Begin applying migration file %s, direction: %s", file, direction);
- 70
-DB.transaction(hitch(this, function () {
- 70
-var ret = new Promise();
- 70
-when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
- 68
-var fileLowerCase = file.toLowerCase();
- 68
-var query = {};
- 68
-query[column] = fileLowerCase;
- 68
-(direction === "up" ? ds.insert(query) : ds.filter(query).remove()).then(ret);
- -
}), ret);
- 68
-return ret.promise();
- -
})).then(function () {
- 68
-DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);
- 68
-runMigration(index + 1);
- -
}, ret);
- -
}
- -
- -
});
- 18
-runMigration(0);
- -
}, ret);
- 18
-return ret.promise();
- -
},
- -
- -
getFileNames:function () {
- 37
-var ret = new Promise();
- 37
-var sup = this._super(arguments);
- 37
-sup.then(hitch(this, function (files) {
- 37
-ret.callback(files.sort(hitch(this, function (f1, f2) {
- 178
-return this.getMigrationVersionFromFile(f1) - this.getMigrationVersionFromFile(f2);
- -
})));
- -
}), ret);
- 37
-return ret.promise();
- -
},
- -
- -
__getAppliedMigrations:function () {
- 18
-var ret = new Promise();
- 18
-if (!this.__appliedMigrations) {
- 18
-this._getSchemaDataset().then(hitch(this, function (ds) {
- 18
-when(
- -
ds.selectOrderMap(this.column),
- -
this.getFileNames()
- -
).then(hitch(this, function (res) {
- 18
-var appliedMigrations = res[0], files = res[1].map(function (f) {
- 92
-return path.basename(f).toLowerCase();
- -
});
- 18
-var l = appliedMigrations.length;
- 18
-if (l) {
- 9
-for (var i = 0; i < l; i++) {
- 39
-if (files.indexOf(appliedMigrations[i]) == -1) {
- 0
-return ret.errback("Applied migrations file not found in directory " + appliedMigrations[i]);
- -
}
- -
}
- 9
-this.__appliedMigrations = appliedMigrations;
- 9
-ret.callback(appliedMigrations);
- -
} else {
- 9
-this.__appliedMigrations = [];
- 9
-ret.callback([]);
- -
}
- -
}), ret);
- -
}), ret);
- -
} else {
- 0
-ret.callback(this.__appliedMigrations);
- -
}
- 18
+return ret.promise();
- +
/**
- +
* Represents an SQL array access, with multiple possible arguments.
- +
* @constructs
- +
* @augments patio.sql.GenericExpression
- +
*
- +
* @param arrCol the SQL array column
- +
* @param sub The array of subscripts to use (should be an array of numbers)
- +
*/
- +
constructor:function (arrCol, sub) {
- +
//The SQL array column
- 109
+this.__arrCol = arrCol;
- +
//The array of subscripts to use (should be an array of numbers)
- 109
this.__sub = sub;
},
- -
- -
__getMirationFiles:function () {
- 18
-var ret = new Promise();
- 18
-var upMigrations = [], downMigrations = [], target = this.target;
- 18
-if (!this.__migrationFiles) {
- 18
-when(
- -
this.getFileNames(),
- -
this.__getAppliedMigrations()
- -
).then(hitch(this, function (res) {
- 18
-var files = res[0], appliedMigrations = res[1];
- 18
-var l = files.length, inserts = [];
- 18
-if (l > 0) {
- 18
-try {
- 18
-for (var i = 0; i < l; i++) {
- 92
-var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
- 92
-if (!isUndefined(target)) {
- 48
-var version = this.getMigrationVersionFromFile(f);
- 48
-if (version > target || (version === 0 && target === version)) {
- 35
-if (appliedMigrations.indexOf(fLowerCase) != -1) {
- 26
-downMigrations.push([f, require(file), "down"]);
- -
}
- 13
-} else if (appliedMigrations.indexOf(fLowerCase) == -1) {
- 9
-upMigrations.push([f, require(file), "up"]);
- -
}
- 44
-} else if (appliedMigrations.indexOf(fLowerCase) == -1) {
- 35
-upMigrations.push([f, require(file), "up"]);
- -
}
- -
}
- -
} catch (e) {
- 0
-return ret.errback(e)
- -
}
- 18
-this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
- 18
-ret.callback(this.__migrationFiles);
- -
} else {
- 0
-return ret.callback();
- -
}
- -
}), ret);
- -
} else {
- 0
-ret.callback(this.__migrationFiles);
- -
}
- 18
+return ret.promise();
- +
/**
- +
* Create a new {@link patio.sql.Subscript} appending the given subscript(s)
- +
* the the current array of subscripts.
- +
*/
- +
addSub:function (sub) {
- 0
return new SubScript(this.__arrCol, this.__sub.concat(sub));
},
- -
- -
- -
// Returns the dataset for the schema_migrations table. If no such table
- -
// exists, it is automatically created.
- -
_getSchemaDataset:function () {
- 36
-var ret = new Promise();
- 36
-if (!this.__schemaDataset) {
- 18
-var ds = this.db.from(this.table);
- 18
-this.__createTable().then(hitch(this, function () {
- 18
-this.__schemaDataset = ds;
- 18
-ret.callback(ds);
- -
}), ret);
- -
} else {
- 18
-ret.callback(this.__schemaDataset);
- -
}
- 36
+return ret.promise();
- +
/**
- +
* Converts the {@link patio.sql.SubScript} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL version of the {@link patio.sql.SubScript}.
- +
*/
- +
toString:function (ds) {
- 109
+!Dataset && (Dataset = require("./dataset"));
- 109
+ds = ds || new Dataset();
- 109
return ds.subscriptSql(this);
},
- -
- -
__convertSchemaInfo:function () {
- 1
-var ret = new Promise(), c = this.column;
- 1
-var ds = this.db.from(this.table);
- 1
-this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
- 1
-this.getFileNames().then(hitch(this, function (files) {
- 1
-var l = files.length, inserts = [];
- 1
-if (l > 0) {
- 1
-for (var i = 0; i < l; i++) {
- 7
-var f = path.basename(files[i]);
- 7
-if (this.getMigrationVersionFromFile(f) <= version) {
- 5
-var insert = {};
- 5
-insert[c] = f;
- 5
-inserts.push(ds.insert(insert));
- -
}
- -
}
- -
}
- 1
-if (inserts.length) {
- 1
-when.apply(comb, inserts).then(ret);
- -
} else {
- 0
-ret.callback();
- -
}
- -
}), ret);
- -
}), ret);
- 1
-return ret.promise();
- -
- +
},
- +
/**@ignore*/
- +
getters:{
- +
f:function () {
- 110
+return this.__arrCol;
},
- -
- -
__createTable:function () {
- 18
-var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
- 18
-var ds = this.db.from(table);
- 18
-var ret = new Promise();
- 18
-when(
- -
db.tableExists(table),
- -
db.tableExists(intMigrationTable)
- -
).then(hitch(this, function (res) {
- 18
-var exists = res[0], intMigratorExists = res[1];
- 18
-if (!exists) {
- 10
-db.createTable(table,
- -
function () {
- 10
-this.column(c, String, {primaryKey:true});
- -
}).addErrback(ret);
- 10
-if (intMigratorExists) {
- 1
-db.from(intMigrationTable).all().then(hitch(this, function (versions) {
- 1
-var version;
- 1
-if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
- 1
-this.__convertSchemaInfo().then(ret);
- -
} else {
- 0
-ret.callback();
- -
}
- -
}));
- -
} else {
- 9
-ret.callback();
- -
}
- -
} else {
- 8
-ds.columns.then(hitch(this, function (columns) {
- 8
-if (columns.indexOf(c) === -1) {
- 0
-ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
- -
} else {
- 8
-ret.callback();
- -
}
- -
}), ret);
- -
}
- -
}), ret);
- 18
+return ret.promise();
- +
sub:function () {
- 110
+return this.__sub;
}
- -
}
- +
},
- +
}
}).as(sql, "SubScript");
- -
- -
static:{
- -
DEFAULT_SCHEMA_COLUMN:"filename",
- +
DEFAULT_SCHEMA_TABLE:"schema_migrations"
- 1
+var STRING_METHODS = ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "quote",
- +
"replace", "search", "slice", "split", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase",
- +
"toSource", "toString", "toUpperCase", "trim", "trimLeft", "trimRight", "valueOf"];
- +
- +
- 1
+var addStringMethod = function (op) {
- 24
+return function () {
- 4294
return this.__str[op].apply(this.__str, arguments);
- -
}
- +
}).as(exports, "TimestampMigrator");
};
- -
- 1
-exports.run = function () {
- 31
-return Migrator.run.apply(Migrator, arguments);
};
var LiteralString = define([OrderedMethods, ComplexExpressionMethods, BooleanMethods, NumericMethods, StringMethods, InequalityMethods, AliasMethods], {
instance:{
/**@lends patio.sql.LiteralString*/
/**
* Represents a string that should be placed into a SQL query literally.
* <b>This class has all methods that a normal javascript String has.</b>
* @constructs
* @augments patio.sql.OrderedMethods
* @augments patio.sql.ComplexExpressionMethods
* @augments patio.sql.BooleanMethods
* @augments patio.sql.NumericMethods
* @augments patio.sql.StringMethods
* @augments patio.sql.InequalityMethods
* @augments patio.sql.AliasMethods
*
* @param {String} str the literal string.
*/
constructor:function (str) {
this.__str = str;
}
}
}).as(sql, "LiteralString");
STRING_METHODS.forEach(function (op) {
LiteralString.prototype[op] = addStringMethod(op);
}, this);
- dataset/index.js
+ migration.js
|
- Coverage92.54
- SLOC439
- LOC67
- Missed5
+ Coverage91.97
+ SLOC607
+ LOC274
+ Missed22
|
- 1
+var comb = require("comb"),
+
- 1
var comb = require("comb"),
- -
hitch = comb.hitch,
- -
logging = comb.logging,
- -
Logger = logging.Logger,
- -
errors = require("../errors"),
- -
QueryError = errors.QueryError,
DatasetError = errors.DatasetError,
- -
Promise = comb.Promise,
- -
PromiseList = comb.PromiseList,
- -
isUndefined = comb.isUndefined,
- -
isUndefinedOrNull = comb.isUndefinedOrNull,
- -
isString = comb.isString,
- -
isInstanceOf = comb.isInstanceOf,
- -
isString = comb.isString,
- -
isFunction = comb.isFunction,
- -
isNull = comb.isNull,
- +
merge = comb.merge,
- +
errors = require("./errors"),
- +
MigrationError = errors.MigrationError,
- +
NotImplemented = errors.NotImplemented(),
format = comb.string.format,
- -
define = comb.define,
- -
graph = require("./graph"),
- -
actions = require("./actions"),
- -
features = require("./features"),
- -
query = require("./query"),
- -
sql = require("./sql"),
- -
SQL = require("../sql").sql,
- -
AliasedExpression = SQL.AliasedExpression,
- -
Identifier = SQL.Identifier,
- +
QualifiedIdentifier = SQL.QualifiedIdentifier;
- +
isFunction = comb.isFunction,
- +
serial = comb.serial,
- +
isNumber = comb.isNumber,
- +
when = comb.when,
- +
isUndefined = comb.isUndefined,
- +
fs = require("fs"),
path = require("path");
- -
- 1
-var LOGGER = comb.logger("patio.Dataset");
- 1
+define([actions, graph, features, query, sql], {
- 1
var Migrator = define(null, {
- -
instance:{
- -
- -
/**@lends patio.Dataset.prototype*/
- -
- -
/**
- -
* Class that is used for querying/retirving datasets from a database.
- -
*
- -
* <p> Dynamically genertated methods include
- -
* <ul>
- -
* <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
- -
* {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
- -
* to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
- -
* {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
- -
* <ul>
- -
* <li>Conditioned join types that accept conditions.
- -
* <ul>
- -
* <li>inner - INNER JOIN</li>
- -
* <li>fullOuter - FULL OUTER</li>
- -
* <li>rightOuter - RIGHT OUTER JOIN</li>
- -
* <li>leftOuter - LEFT OUTER JOIN</li>
- -
* <li>full - FULL JOIN</li>
- -
* <li>right - RIGHT JOIN</li>
- -
* <li>left - LEFT JOIN</li>
- -
* </ul>
- -
* </li>
- -
* <li>Unconditioned join types that do not accept join conditions
- -
* <ul>
- -
* <li>natural - NATURAL JOIN</li>
- -
* <li>naturalLeft - NATURAL LEFT JOIN</li>
- -
* <li>naturalRight - NATURAL RIGHT JOIN</li>
- -
* <li>naturalFull - NATURA FULLL JOIN</li>
- -
* <li>cross - CROSS JOIN</li>
- -
* </ul>
- -
* </li>
- -
* </ul>
- -
* </li>
- -
* </li>
- -
* </ul>
- -
*
- -
* <p>
- -
* <h4>Features:</h4>
- -
* <p>
- -
* Features that a particular {@link patio.Dataset} supports are shown in the example below.
- -
* If you wish to implement an adapter please override these values depending on the database that
- -
* you are developing the adapter for.
- -
* </p>
- -
* <pre class="code">
- -
* var ds = DB.from("test");
- -
*
- -
* //The default values returned
- -
*
- -
* //Whether this dataset quotes identifiers.
- -
* //Whether this dataset quotes identifiers.
- -
* ds.quoteIdentifiers //=>true
- -
*
- -
* //Whether this dataset will provide accurate number of rows matched for
- -
* //delete and update statements. Accurate in this case is the number of
- -
* //rows matched by the dataset's filter.
- -
* ds.providesAccurateRowsMatched; //=>true
- -
*
- -
* //Times Whether the dataset requires SQL standard datetimes (false by default,
- -
* // as most allow strings with ISO 8601 format).
- -
* ds.requiresSqlStandardDate; //=>false
- -
*
- -
* //Whether the dataset supports common table expressions (the WITH clause).
- -
* ds.supportsCte; //=>true
- -
*
- -
* //Whether the dataset supports the DISTINCT ON clause, false by default.
- -
* ds.supportsDistinctOn; //=>false
- -
*
- -
* //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
- -
* ds.supportsIntersectExcept; //=>true
- -
*
- -
* //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
- -
* ds.supportsIntersectExceptAll; //=>true
- -
*
- -
* //Whether the dataset supports the IS TRUE syntax.
- -
* ds.supportsIsTrue; //=>true
- -
*
- -
* //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
- -
* ds.supportsJoinUsing; //=>true
- -
*
- -
* //Whether modifying joined datasets is supported.
- -
* ds.supportsModifyingJoin; //=>false
- -
*
- -
* //Whether the IN/NOT IN operators support multiple columns when an
- -
* ds.supportsMultipleColumnIn; //=>true
- -
*
- -
* //Whether the dataset supports timezones in literal timestamps
- -
* ds.supportsTimestampTimezone; //=>false
- -
*
- -
* //Whether the dataset supports fractional seconds in literal timestamps
- -
* ds.supportsTimestampUsecs; //=>true
- -
*
- -
* //Whether the dataset supports window functions.
- -
* ds.supportsWindowFunctions; //=>false
- -
* </pre>
- -
* <p>
- -
* <p>
- -
* <h4>Actions</h4>
- -
* <p>
- -
* Each dataset does not actually send any query to the database until an action method has
- -
* been called upon it(with the exception of {@link patio.Dataset#graph} because columns
- -
* from the other table might need retrived in order to set up the graph). Each action
- -
* returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
- -
* that you account for errors otherwise it can be difficult to track down issues.
- -
* The list of action methods is:
- -
* <ul>
- -
* <li>{@link patio.Dataset#all}</li>
- -
* <li>{@link patio.Dataset#one}</li>
- -
* <li>{@link patio.Dataset#avg}</li>
- -
* <li>{@link patio.Dataset#count}</li>
- -
* <li>{@link patio.Dataset#columns}</li>
- -
* <li>{@link patio.Dataset#remove}</li>
- -
* <li>{@link patio.Dataset#forEach}</li>
- -
* <li>{@link patio.Dataset#empty}</li>
- -
* <li>{@link patio.Dataset#first}</li>
- -
* <li>{@link patio.Dataset#get}</li>
- -
* <li>{@link patio.Dataset#import}</li>
- -
* <li>{@link patio.Dataset#insert}</li>
- -
* <li>{@link patio.Dataset#save}</li>
- -
* <li>{@link patio.Dataset#insertMultiple}</li>
- -
* <li>{@link patio.Dataset#saveMultiple}</li>
- -
* <li>{@link patio.Dataset#interval}</li>
- -
* <li>{@link patio.Dataset#last}</li>
- -
* <li>{@link patio.Dataset#map}</li>
- -
* <li>{@link patio.Dataset#max}</li>
- -
* <li>{@link patio.Dataset#min}</li>
- -
* <li>{@link patio.Dataset#multiInsert}</li>
- -
* <li>{@link patio.Dataset#range}</li>
- -
* <li>{@link patio.Dataset#selectHash}</li>
- -
* <li>{@link patio.Dataset#selectMap}</li>
- -
* <li>{@link patio.Dataset#selectOrderMap}</li>
- -
* <li>{@link patio.Dataset#set}</li>
- -
* <li>{@link patio.Dataset#singleRecord}</li>
- -
* <li>{@link patio.Dataset#singleValue}</li>
- -
* <li>{@link patio.Dataset#sum}</li>
- -
* <li>{@link patio.Dataset#toCsv}</li>
- -
* <li>{@link patio.Dataset#toHash}</li>
- -
* <li>{@link patio.Dataset#truncate}</li>
- -
* <li>{@link patio.Dataset#update}</li>
- -
* </ul>
- -
*
- -
* </p>
- +
* </p>
- +
/**@lends patio.migrations.Migrator.prototype*/
- +
column:null,
- +
db:null,
- +
directory:null,
- +
ds:null,
- +
files:null,
- +
table:null,
- +
target:null,
- +
- +
/**
* Abstract Migrator class. This class should be be instantiated directly.
*
- +
* @constructs
- +
* @param {patio.Database} db the database to migrate
- +
* @param {String} directory directory that the migration files reside in
- +
* @param {Object} [opts={}] optional parameters.
- +
* @param {String} [opts.column] the column in the table that version information should be stored.
- +
* @param {String} [opts.table] the table that version information should be stored.
- +
* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
- +
* @param {String} [opts.current] the version that the database is currently at if the current version
- +
*/
- +
constructor:function (db, directory, opts) {
- 31
+this.db = db;
- 31
+this.directory = directory;
- 31
+opts = opts || {};
- 31
+this.table = opts.table || this._static.DEFAULT_SCHEMA_TABLE;
- 31
+this.column = opts.column || this._static.DEFAULT_SCHEMA_COLUMN;
- 31
+this._opts = opts;
- +
},
- +
- +
/**
- +
* Runs the migration and returns a promise.
- +
*/
- +
run:function () {
- 0
+throw new NotImplemented("patio.migrations.Migrator#run");
- +
},
- +
- +
getFileNames:function () {
- 50
+if (!this.__files) {
- 49
+return this._static.getFileNames(this.directory).addCallback(hitch(this, function (files) {
- 49
+this.__files = files;
- +
}));
- +
} else {
- 1
+return new Promise().callback(this.__files).promise();
- +
}
- +
},
- +
- +
getMigrationVersionFromFile:function (filename) {
- 476
+return parseInt(path.basename(filename).split(this._static.MIGRATION_SPLITTER)[0], 10);
- +
}
- +
},
- +
- +
"static":{
- +
/**@lends patio.migrations.Migrator*/
- +
- +
MIGRATION_FILE_PATTERN:/^\d+\..+\.js$/i,
- +
MIGRATION_SPLITTER:'.',
- +
MINIMUM_TIMESTAMP:20000101,
- +
- +
getFileNames:function (directory) {
- 80
+var ret = new Promise();
- 80
+fs.readdir(directory, hitch(this, function (err, files) {
- 80
+if (err) {
- 0
+ret.errback(err);
- +
} else {
- 80
+files = files.filter(function (file) {
- 394
+return file.match(this.MIGRATION_FILE_PATTERN) !== null;
- +
}, this);
- 80
+ret.callback(files.map(function (file) {
- 394
+return path.resolve(directory, file);
- +
}));
- +
}
- +
}));
- 80
+return ret.promise();
- +
},
- +
- +
/**
- +
* Migrates the database using migration files found in the supplied directory.
* See {@link patio#migrate}
- +
*
- +
* @example
- +
* var DB = patio.connect("my://connection/string");
- +
* patio. migrate(DB, __dirname + "/timestamp_migration").then(function(){
- +
* console.log("done migrating!");
* });
- -
*
- -
* @param {patio.Database} db the database this dataset should use when querying for data.
- -
* @param {Object} opts options to set on this dataset instance
- -
*
- -
* @property {Function} rowCb callback to be invoked for each row returned from the database.
- -
* the return value will be used as the result of query. The rowCb can also return a promise,
- -
* The resolved value of the promise will be used as result.
- -
*
- -
* @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
- -
* This value will be defaulted to whatever the identifierInputMethod
- -
* is on the database used in initialization.
- -
*
- -
* @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
- -
* This value will be defaulted to whatever the identifierOutputMethod
- -
* is on the database used in initialization.
- -
* @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
- -
* throws a {patio.DatasetError} tf the dataset doesn't have a table.
- -
* <pre class="code">
- -
* DB.from("table").firstSourceAlias;
- -
* //=> "table"
- -
*
- -
* DB.from("table___t").firstSourceAlias;
- -
* //=> "t"
- +
* </pre>
- +
* patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).then(function(){
- +
* console.log("done migrating down!");
* });
- -
*
- -
* @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
- -
* have a table, raises a {@link patio.erros.DatasetError}.
*<pre class="code">
- -
*
- -
* DB.from("table").firstSourceTable;
- +
* //=> "table"
- +
* @param {patio.Database} db the database to migrate
- +
* @param {String} directory directory that the migration files reside in
- +
* @param {Object} [opts={}] optional parameters.
- +
* @param {String} [opts.column] the column in the table that version information should be stored.
- +
* @param {String} [opts.table] the table that version information should be stored.
- +
* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
- +
* @param {String} [opts.current] the version that the database is currently at if the current version
* is not provided it is retrieved from the database.
- -
*
- -
* DB.from("table___t").firstSourceTable;
- -
* //=> "t"
- -
* </pre>
- -
* @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
- -
* <pre class="code">
- -
* DB.from("items").isSimpleSelectAll; //=> true
- -
* DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
- -
* </pre>
- -
* @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
- -
* @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
- -
* delete and update statements. Accurate in this case is the number of
- -
* rows matched by the dataset's filter.
- -
* @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
- -
* as most allow strings with ISO 8601 format).
- -
* @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
- -
* @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
- -
* @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
- -
* @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
- -
* @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
- -
* @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
- -
* @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
- -
* @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
- -
* @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
- -
* @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
- +
* @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
* @return {Promise} a promise that is resolved once the migration is complete.
- -
*/
- -
constructor:function (db, opts) {
- 29216
-this._super(arguments);
- 29216
-this.db = db;
- 29216
-this.__opts = {};
- 29216
-this.__rowCb = null;
- 29216
-if (db) {
- 15039
-this.__quoteIdentifiers = db.quoteIdentifiers;
- 15039
-this.__identifierInputMethod = db.identifierInputMethod;
- 15039
+this.__identifierOutputMethod = db.identifierOutputMethod;
- +
run:function (db, directory, opts, cb) {
- 31
+if (isFunction(opts)) {
- 0
+cb = opts;
- 0
+opts = {};
- +
} else {
- 31
+opts = opts || {};
- +
}
- 31
+opts = opts || {};
- 31
+var ret = new Promise();
- 31
+this.__getMigrator(directory).then(function (migrator) {
- 31
+new migrator(db, directory, opts).run().then(ret);
- +
}, ret);
- 31
+ret.classic(cb);
- 31
+return ret.promise();
- +
},
- +
- +
// Choose the Migrator subclass to use. Uses the TimestampMigrator
- +
// // if the version number appears to be a unix time integer for a year
- +
// after 2005, otherwise uses the IntegerMigrator.
- +
__getMigrator:function (directory) {
- 31
+var ret = new Promise();
- 31
+var retClass = IntegerMigrator;
- 31
+this.getFileNames(directory).then(hitch(this, function (files) {
- 31
+var l = files.length;
- 31
+if (l) {
- 31
+for (var i = 0; i < l; i++) {
- 81
+var file = files[i];
- 81
+if (parseInt(path.basename(file).split(this.MIGRATION_SPLITTER)[0], 10) > this.MINIMUM_TIMESTAMP) {
- 18
+retClass = TimestampMigrator;
- 18
+break;
- +
}
- +
}
- +
}
- 31
+ret.callback(retClass);
- +
- +
}), ret);
- 31
+return ret.promise();
- +
}
- +
}
- +
});
- +
- +
- +
/**
- +
* @class Migrator that uses the file format {migrationName}.{version}.js, where version starts at 0.
- +
* <b>Missing migrations are not allowed</b>
- +
*
- +
* @augments patio.migrations.Migrator
- +
* @name IntegerMigrator
- +
* @memberOf patio.migrations
- +
*/
- 1
+var IntegerMigrator = define(Migrator, {
- +
instance:{
- +
/**@lends patio.migrations.IntegerMigrator.prototype*/
- +
current:null,
- +
direction:null,
- +
migrations:null,
- +
- +
_migrationFiles:null,
- +
- +
run:function () {
- 13
+var ret = new Promise(), DB = this.db;
- 13
+serial([this._getLatestMigrationVersion.bind(this), this._getCurrentMigrationVersion.bind(this)]).then(hitch(this, function (res) {
- 11
+var target = res[0], current = res[1];
- 11
+if (current !== target) {
- 11
+var direction = this.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;
- 11
+this._getMigrations(current, target, direction).then(hitch(this, function (migrations) {
- 11
+var runMigration = hitch(this, function (index) {
- 60
+if (index >= migrations.length) {
- 11
+ret.callback(version);
- +
} else {
- 49
+var curr = migrations[index], migration = curr[0];
- 49
+version = curr[1];
- 49
+var now = new Date();
- 49
+var lv = isUp ? version : version - 1;
- 49
+DB.logInfo("Begin applying migration version %d, direction: %s", lv, direction);
- 49
+DB.transaction(hitch(this, function () {
- 49
+var ret = new Promise();
- 49
+if (!isFunction(migration[direction])) {
- 0
+this._setMigrationVersion(lv).then(ret);
- +
} else {
- 49
+when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
- 49
+this._setMigrationVersion(lv).then(ret);
- +
}), ret);
- +
}
- 49
+return ret.promise();
- +
})).then(function () {
- 49
+DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);
- 49
+runMigration(index + 1);
- +
}, ret);
- +
}
- +
- +
});
- 11
+runMigration(0);
- +
}), ret);
- +
} else {
- 0
+ret.callback(target);
- +
}
- +
- +
}), ret);
- 13
+return ret.promise();
- +
},
- +
- +
_getMigrations:function (current, target, direction) {
- 11
+var ret = new Promise(), isUp = direction === "up", migrations = [];
- 11
+when(this._getMigrationFiles()).then(function (files) {
- 11
+try {
- 11
+if ((isUp ? target : current - 1) < files.length) {
- 11
+if (isUp) {
- 8
+current++;
- +
}
- 11
+for (; isUp ? current <= target : current > target; isUp ? current++ : current--) {
- 49
+migrations.push([require(files[current]), current]);
- +
}
- +
} else {
- 0
+return ret.errback(new MigrationError("Invalid target " + target));
- +
}
- +
} catch (e) {
- 0
+return ret.errback(e);
- +
}
- 11
+ret.callback(migrations);
- +
}, ret);
- 11
+return ret.promise();
- +
},
- +
- +
- +
_getMigrationFiles:function () {
- 19
+var ret = new Promise();
- 19
+if (!this._migrationFiles) {
- 13
+var retFiles = [];
- 13
+var directory = this.directory;
- 13
+this.getFileNames().then(hitch(this, function (files) {
- 13
+var l = files.length;
- 13
+if (l) {
- 13
+for (var i = 0; i < l; i++) {
- 59
+var file = files[i];
- 59
+var version = this.getMigrationVersionFromFile(file);
- 59
+if (isUndefined(retFiles[version])) {
- 58
+retFiles[version] = file;
- +
} else {
- 1
+return ret.errback(new MigrationError("Duplicate migration number " + version));
- +
}
- +
}
- 12
+if (isUndefined(retFiles[0])) {
- 0
+retFiles.shift();
- +
}
- 12
+for (var j = 0; j < l; j++) {
- 57
+if (isUndefined(retFiles[j])) {
- 1
+return ret.errback(new MigrationError("Missing migration for " + j));
- +
}
- +
}
- +
}
- 11
+this._migrationFiles = retFiles;
- 11
+ret.callback(retFiles);
- +
}), ret);
- +
} else {
- 6
ret.callback(this._migrationFiles);
- +
}
- 19
return ret.promise();
},
- +
- +
_getLatestMigrationVersion:function () {
- 13
+var ret = new Promise();
- 13
+if (!isUndefined(this._opts.target)) {
- 5
+ret.callback(this._opts.target);
- +
} else {
- 8
+this._getMigrationFiles().then(hitch(this, function (files) {
- 6
+var l = files[files.length - 1];
- 6
+ret.callback(l ? this.getMigrationVersionFromFile(path.basename(l)) : null);
- +
}), ret);
- +
}
- 13
+return ret.promise();
},
- -
- -
/**
- -
* Returns a new clone of the dataset with with the given options merged into the current datasets options.
- -
* If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
- -
* columns are deleted. This method should generally not be called
- -
* directly by user code.
- -
*
- -
* @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
- -
* @return [patio.Dataset] a cloned dataset with the merged options
- -
**/
- -
mergeOptions:function (opts) {
- 14948
-opts = isUndefined(opts) ? {} : opts;
- 14948
-var ds = new this._static(this.db, {});
- 14948
-ds.rowCb = this.rowCb;
- 14948
-this._static.FEATURES.forEach(function (f) {
- 209272
-ds[f] = this[f];
- -
}, this);
- 14948
-ds.__opts = merge({}, this.__opts, opts);
- 14948
-ds.identifierInputMethod = this.identifierInputMethod;
- 14948
-ds.identifierOutputMethod = this.identifierOutputMethod;
- 14948
-var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
- 14948
-if (Object.keys(opts).some(function (o) {
- 13536
-return columnChangeOpts.indexOf(o) != -1;
- -
})) {
- 2456
+ds.__opts.columns = null;
- +
_getCurrentMigrationVersion:function () {
- 11
+var ret = new Promise();
- 11
+if (!isUndefined(this._opts.current)) {
- 2
+ret.callback(this._opts.current);
- +
} else {
- 9
+when(this._getSchemaDataset()).then(hitch(this, function (ds) {
- 9
+ds.get(this.column).then(ret);
}), ret);
- -
}
- 14948
+return ds;
- 11
return ret.promise();
},
- +
- +
_setMigrationVersion:function (version) {
- 49
+var ret = new Promise(), c = this.column;
- 49
+this._getSchemaDataset().then(function (ds) {
- 49
+var item = {};
- 49
+item[c] = version;
- 49
+ds.update(item).both(ret);
}, ret);
- -
- -
/**
- -
* Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
- -
* or {@link patio.sql.AliasedExpression}, depending on the format:
- -
*
- -
* <ul>
- -
* <li>For columns : table__column___alias.</li>
- -
* <li>For tables : schema__table___alias.</li>
- -
* </ul>
- -
* each portion of the identifier is optional. See example below
- -
*
- -
* @example
- -
*
- -
* ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
- -
* ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
- -
* ds.stringToIdentifier("table__column___alias");
- -
* //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
- -
*
- -
* @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
- -
* or {@link patio.sql.AliasedExpression}.
- -
*
- -
* @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
- -
*/
- -
stringToIdentifier:function (name) {
- 15093
-if (isString(name)) {
- 10138
-var parts = this._splitString(name);
- 10138
-var schema = parts[0], table = parts[1], alias = parts[2];
- 10138
-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))));
- 49
+return ret.promise();
- +
},
- +
- +
_getSchemaDataset:function () {
- 58
+var c = this.column, table = this.table;
- 58
+var ret = new Promise();
- 58
+if (!this.__schemaDataset) {
- 11
+var ds = this.db.from(table);
- 11
+this.__createOrAlterMigrationTable().then(hitch(this, function () {
- 11
+ds.isEmpty().then(hitch(this, function (empty) {
- 11
+if (empty) {
- 0
+var item = {};
- 0
+item[c] = -1;
- 0
+this.__schemaDataset = ds;
- 0
+ds.insert(item).then(hitch(ret, "callback", ds), ret);
- +
} else {
- 11
+ds.count().then(hitch(this, function (count) {
- 11
+if (count > 1) {
- 0
+ret.errback(new Error("More than one row in migrator table"));
- +
} else {
- 11
+this.__schemaDataset = ds;
- 11
+ret.callback(ds);
- +
}
- +
}), ret);
- +
}
- +
}), ret);
- +
}), ret);
- +
} else {
- 47
+ret.callback(this.__schemaDataset);
- +
}
- 58
+return ret.promise();
- +
},
- +
- +
__createOrAlterMigrationTable:function () {
- 11
+var c = this.column, table = this.table, db = this.db;
- 11
+var ds = this.db.from(table);
- 11
+var ret = new Promise();
- 11
+db.tableExists(table).then(hitch(this, function (exists) {
- 11
+if (!exists) {
- 6
+db.createTable(table,
- +
function () {
- 6
+this.column(c, "integer", {"default":-1, allowNull:false});
- +
}).then(ret);
- +
} else {
- 5
+ds.columns.then(function (columns) {
- 5
+if (columns.indexOf(c) === -1) {
- 1
+db.addColumn(table, c, "integer", {"default":-1, allowNull:false})
- +
.then(ret);
- +
} else {
- 4
+ret.callback();
- +
}
- +
});
- +
}
- +
}), ret);
- 11
+return ret.promise();
- +
}
- +
- +
},
- +
- +
static:{
- +
DEFAULT_SCHEMA_COLUMN:"version",
- +
DEFAULT_SCHEMA_TABLE:"schema_info"
- +
}
- +
}).as(exports, "IntegerMigrator");
- +
- +
- +
/**
- +
* @class Migrator that uses the file format {migrationName}.{timestamp}.js, where the timestamp
- +
* can be anything greater than 20000101.
- +
*
- +
* @name TimestampMigrator
- +
* @augments patio.migrations.Migrator
- +
* @memberOf patio.migrations
- +
*/
- 1
+var TimestampMigrator = define(Migrator, {
- +
instance:{
- +
- +
constructor:function (db, directory, opts) {
- 18
+this._super(arguments);
- 18
+opts = opts || {};
- 18
+this.target = opts.target;
- +
},
- +
- +
run:function () {
- 18
+var ret = new Promise(), DB = this.db, column = this.column;
- 18
+serial([this.__getMirationFiles.bind(this), this._getSchemaDataset.bind(this)]).then(function (res) {
- 18
+var migrations = res[0], ds = res[1];
- 18
+var runMigration = hitch(this, function (index) {
- 86
+if (index >= migrations.length) {
- 16
+ret.callback();
- +
} else {
- 70
+var curr = migrations[index], file = curr[0], migration = curr[1], direction = curr[2];
- 70
+var now = new Date();
- 70
+DB.logInfo("Begin applying migration file %s, direction: %s", file, direction);
- 70
+DB.transaction(hitch(this, function () {
- 70
+var ret = new Promise();
- 70
+when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
- 68
+var fileLowerCase = file.toLowerCase();
- 68
+var query = {};
- 68
+query[column] = fileLowerCase;
- 68
+(direction === "up" ? ds.insert(query) : ds.filter(query).remove()).then(ret);
- +
}), ret);
- 68
+return ret.promise();
- +
})).then(function () {
- 68
+DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);
- 68
+runMigration(index + 1);
- +
}, ret);
- +
}
- +
- +
});
- 18
+runMigration(0);
- +
}, ret);
- 18
+return ret.promise();
- +
},
- +
- +
getFileNames:function () {
- 37
+var ret = new Promise();
- 37
+var sup = this._super(arguments);
- 37
+sup.then(hitch(this, function (files) {
- 37
+ret.callback(files.sort(hitch(this, function (f1, f2) {
- 178
+return this.getMigrationVersionFromFile(f1) - this.getMigrationVersionFromFile(f2);
- +
})));
- +
}), ret);
- 37
+return ret.promise();
- +
},
- +
- +
__getAppliedMigrations:function () {
- 18
+var ret = new Promise();
- 18
+if (!this.__appliedMigrations) {
- 18
+this._getSchemaDataset().then(hitch(this, function (ds) {
- 18
+when(
- +
ds.selectOrderMap(this.column),
- +
this.getFileNames()
- +
).then(hitch(this, function (res) {
- 18
+var appliedMigrations = res[0], files = res[1].map(function (f) {
- 92
+return path.basename(f).toLowerCase();
- +
});
- 18
+var l = appliedMigrations.length;
- 18
+if (l) {
- 9
+for (var i = 0; i < l; i++) {
- 39
+if (files.indexOf(appliedMigrations[i]) == -1) {
- 0
+return ret.errback("Applied migrations file not found in directory " + appliedMigrations[i]);
- +
}
- +
}
- 9
+this.__appliedMigrations = appliedMigrations;
- 9
+ret.callback(appliedMigrations);
- +
} else {
- 9
+this.__appliedMigrations = [];
- 9
+ret.callback([]);
- +
}
- +
}), ret);
}), ret);
- -
} else {
- 4955
+return name;
- 0
ret.callback(this.__appliedMigrations);
- +
}
- 18
return ret.promise();
},
- -
- -
/**
- -
* Can either be a string or null.
- -
*
- -
*
- -
* @example
- -
* //columns
- -
* table__column___alias //=> table.column as alias
- -
* table__column //=> table.column
- -
* //tables
- -
* schema__table___alias //=> schema.table as alias
- -
* schema__table //=> schema.table
- -
*
- -
* //name and alias
- -
* columnOrTable___alias //=> columnOrTable as alias
- -
*
- -
*
- -
*
- -
* @return {String[]} an array with the elements being:
- -
* <ul>
- -
* <li>For columns :[table, column, alias].</li>
- -
* <li>For tables : [schema, table, alias].</li>
- -
* </ul>
- -
*/
- -
_splitString:function (s) {
- 19662
-var ret, m;
- 19662
-if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
- 189
-ret = m.slice(1);
- -
}
- 19473
-else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
- 28
-ret = [null, m[1], m[2]];
- -
}
- 19445
-else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
- 2079
-ret = [m[1], m[2], null];
- -
}
- -
else {
- 17366
+ret = [null, s, null];
- +
__getMirationFiles:function () {
- 18
+var ret = new Promise();
- 18
+var upMigrations = [], downMigrations = [], target = this.target;
- 18
+if (!this.__migrationFiles) {
- 18
+when(
- +
this.getFileNames(),
- +
this.__getAppliedMigrations()
- +
).then(hitch(this, function (res) {
- 18
+var files = res[0], appliedMigrations = res[1];
- 18
+var l = files.length, inserts = [];
- 18
+if (l > 0) {
- 18
+try {
- 18
+for (var i = 0; i < l; i++) {
- 92
+var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
- 92
+if (!isUndefined(target)) {
- 48
+var version = this.getMigrationVersionFromFile(f);
- 48
+if (version > target || (version === 0 && target === version)) {
- 35
+if (appliedMigrations.indexOf(fLowerCase) != -1) {
- 26
+downMigrations.push([f, require(file), "down"]);
- +
}
- 13
+} else if (appliedMigrations.indexOf(fLowerCase) == -1) {
- 9
+upMigrations.push([f, require(file), "up"]);
- +
}
- 44
+} else if (appliedMigrations.indexOf(fLowerCase) == -1) {
- 35
+upMigrations.push([f, require(file), "up"]);
- +
}
- +
}
- +
} catch (e) {
- 0
+return ret.errback(e)
- +
}
- 18
+this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
- 18
+ret.callback(this.__migrationFiles);
- +
} else {
- 0
+return ret.callback();
- +
}
- +
}), ret);
- +
} else {
- 0
ret.callback(this.__migrationFiles);
- -
}
- 19662
+return ret;
- 18
return ret.promise();
},
- -
- -
/**
- -
* @ignore
- -
**/
- -
getters:{
- -
- -
rowCb:function () {
- 23052
-return this.__rowCb;
- -
},
- -
- -
identifierInputMethod:function () {
- 14948
-return this.__identifierInputMethod;
- -
},
- -
- -
identifierOutputMethod:function () {
- 14948
-return this.__identifierOutputMethod;
- -
},
- -
- -
firstSourceAlias:function () {
- 579
-var source = this.__opts.from;
- 579
-if (isUndefinedOrNull(source) || !source.length) {
- 2
-throw new DatasetError("No source specified for the query");
- -
}
- 577
-source = source[0];
- 577
-if (isInstanceOf(source, AliasedExpression)) {
- 20
-return source.alias;
- 557
-} else if (isString(source)) {
- 0
-var parts = this._splitString(source);
- 0
-var alias = parts[2];
- 0
-return alias ? alias : source;
- -
} else {
- 557
-return source;
- -
}
},
- -
- -
firstSourceTable:function () {
- 15
-var source = this.__opts.from;
- 15
-if (isUndefinedOrNull(source) || !source.length) {
- 1
-throw new QueryError("No source specified for the query");
- -
}
- 14
-var source = source[0];
- 14
-if (isInstanceOf(source, AliasedExpression)) {
- 3
-return source.expression;
- 11
-} else if (isString(source)) {
- 0
-var parts = this._splitString(source);
- 0
-return source;
- -
} else {
- 11
-return source;
- +
}
- +
// Returns the dataset for the schema_migrations table. If no such table
- +
// exists, it is automatically created.
- +
_getSchemaDataset:function () {
- 36
+var ret = new Promise();
- 36
+if (!this.__schemaDataset) {
- 18
+var ds = this.db.from(this.table);
- 18
+this.__createTable().then(hitch(this, function () {
- 18
+this.__schemaDataset = ds;
- 18
+ret.callback(ds);
- +
}), ret);
- +
} else {
- 18
ret.callback(this.__schemaDataset);
- +
}
- 36
return ret.promise();
},
- -
- -
/**
- -
* @ignore
- -
**/
- -
setters:{
- -
/**@lends patio.Dataset.prototype*/
- -
- -
identifierInputMethod:function (meth) {
- 15038
-this.__identifierInputMethod = meth;
- +
},
- +
__convertSchemaInfo:function () {
- 1
+var ret = new Promise(), c = this.column;
- 1
+var ds = this.db.from(this.table);
- 1
+this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
- 1
+this.getFileNames().then(hitch(this, function (files) {
- 1
+var l = files.length, inserts = [];
- 1
+if (l > 0) {
- 1
+for (var i = 0; i < l; i++) {
- 7
+var f = path.basename(files[i]);
- 7
+if (this.getMigrationVersionFromFile(f) <= version) {
- 5
+var insert = {};
- 5
+insert[c] = f;
- 5
+inserts.push(ds.insert(insert));
- +
}
- +
}
- +
}
- 1
+if (inserts.length) {
- 1
+when.apply(comb, inserts).then(ret);
- +
} else {
- 0
+ret.callback();
- +
}
- +
}), ret);
- +
}), ret);
- 1
return ret.promise();
- -
- -
identifierOutputMethod:function (meth) {
- 15038
-this.__identifierOutputMethod = meth;
- +
},
},
- -
- -
rowCb:function (cb) {
- 18564
-if (isFunction(cb) || isNull(cb)) {
- 18559
+this.__rowCb = cb;
- +
__createTable:function () {
- 18
+var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
- 18
+var ds = this.db.from(table);
- 18
+var ret = new Promise();
- 18
+when(
- +
db.tableExists(table),
- +
db.tableExists(intMigrationTable)
- +
).then(hitch(this, function (res) {
- 18
+var exists = res[0], intMigratorExists = res[1];
- 18
+if (!exists) {
- 10
+db.createTable(table,
- +
function () {
- 10
+this.column(c, String, {primaryKey:true});
- +
}).addErrback(ret);
- 10
+if (intMigratorExists) {
- 1
+db.from(intMigrationTable).all().then(hitch(this, function (versions) {
- 1
+var version;
- 1
+if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
- 1
+this.__convertSchemaInfo().then(ret);
- +
} else {
- 0
+ret.callback();
- +
}
- +
}));
- +
} else {
- 9
+ret.callback();
}
- -
} else {
- 5
+throw new DatasetError("rowCb mus be a function");
- 8
+ds.columns.then(hitch(this, function (columns) {
- 8
+if (columns.indexOf(c) === -1) {
- 0
+ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
- +
} else {
- 8
+ret.callback();
- +
}
}), ret);
- -
}
- +
}
- +
}), ret);
- 18
return ret.promise();
}
},
- -
static:{
- -
COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
- -
COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
- +
COLUMN_REF_RE3:/^(\w+)__(\w+)$/
- +
DEFAULT_SCHEMA_COLUMN:"filename",
DEFAULT_SCHEMA_TABLE:"schema_migrations"
- -
}
- +
}).as(module);
}).as(exports, "TimestampMigrator");
- -
- 1
+exports.run = function () {
- 31
+return Migrator.run.apply(Migrator, arguments);
};
- 1
-var comb = require("comb"),
- -
define = comb.define,
- -
array = comb.array,
- -
intersect = array.intersect,
- -
compact = array.compact,
- -
string = comb.string,
- -
format = string.format,
- -
argsToArray = comb.argsToArray,
- -
isInstanceOf = comb.isInstanceOf,
- -
isArray = comb.isArray,
- -
isNumber = comb.isNumber,
- -
isDate = comb.isDate,
- -
isNull = comb.isNull,
- -
isBoolean = comb.isBoolean,
- -
isFunction = comb.isFunction,
- -
isUndefined = comb.isUndefined,
- -
isObject = comb.isObject,
- -
isHash = comb.isHash,
- -
merge = comb.merge,
- -
isUndefinedOrNull = comb.isUndefinedOrNull,
- -
isString = comb.isString,
- -
sql = require("../sql").sql,
- -
Expression = sql.Expression,
- -
ComplexExpression = sql.ComplexExpression,
- -
AliasedExpression = sql.AliasedExpression,
- -
Identifier = sql.Identifier,
- -
QualifiedIdentifier = sql.QualifiedIdentifier,
- -
OrderedExpression = sql.OrderedExpression,
- -
CaseExpression = sql.CaseExpression,
- -
SubScript = sql.SubScript,
- -
NumericExpression = sql.NumericExpression,
- -
ColumnAll = sql.ColumnAll,
- -
Cast = sql.Cast,
- -
StringExpression = sql.StringExpression,
- -
BooleanExpression = sql.BooleanExpression,
- -
SQLFunction = sql.SQLFunction,
- -
LiteralString = sql.LiteralString,
- -
PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
- +
QueryError = require("../errors").QueryError, patio;
- +
define = comb.define,
- +
array = comb.array,
- +
intersect = array.intersect,
- +
compact = array.compact,
- +
string = comb.string,
- +
format = string.format,
- +
argsToArray = comb.argsToArray,
- +
isInstanceOf = comb.isInstanceOf,
- +
isArray = comb.isArray,
- +
isNumber = comb.isNumber,
- +
isDate = comb.isDate,
- +
isNull = comb.isNull,
- +
isBoolean = comb.isBoolean,
- +
isFunction = comb.isFunction,
- +
isUndefined = comb.isUndefined,
- +
isObject = comb.isObject,
- +
isHash = comb.isHash,
- +
isEmpty = comb.isEmpty,
- +
merge = comb.merge,
- +
isUndefinedOrNull = comb.isUndefinedOrNull,
- +
isString = comb.isString,
- +
sql = require("../sql").sql,
- +
Expression = sql.Expression,
- +
ComplexExpression = sql.ComplexExpression,
- +
AliasedExpression = sql.AliasedExpression,
- +
Identifier = sql.Identifier,
- +
QualifiedIdentifier = sql.QualifiedIdentifier,
- +
OrderedExpression = sql.OrderedExpression,
- +
CaseExpression = sql.CaseExpression,
- +
SubScript = sql.SubScript,
- +
NumericExpression = sql.NumericExpression,
- +
ColumnAll = sql.ColumnAll,
- +
Cast = sql.Cast,
- +
StringExpression = sql.StringExpression,
- +
BooleanExpression = sql.BooleanExpression,
- +
SQLFunction = sql.SQLFunction,
- +
LiteralString = sql.LiteralString,
- +
PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
QueryError = require("../errors").QueryError, patio;
- 1
var Dataset;
- @@ -14323,7 +14349,7 @@
- 59
return new QualifiedIdentifier(table, e);
- 217
} else if (isInstanceOf(e, OrderedExpression)) {
- 2
-return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending,
- +
{nulls:e.nulls});
{nulls:e.nulls});
- 215
} else if (isInstanceOf(e, AliasedExpression)) {
- 72
return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias);
- 143
@@ -14491,9 +14517,9 @@} else if (isInstanceOf(e, CaseExpression)) {
- 1440
var columns = this.__opts.columns, ret = "";
- 1440
if (columns && columns.length) {
- 1387
-ret = " (" + columns.map(
- -
function (c) {
- 6691
-return c.toString(this);
- +
}, this).join(this._static.COMMA_SEPARATOR) + ")";
- +
function (c) {
- 6691
+return c.toString(this);
}, this).join(this._static.COMMA_SEPARATOR) + ")";
}
- 1440
return ret;
- @@ -14605,8 +14631,8 @@
},
- 0
return this.complexExpressionSql("EQ", args);
} else {
- 0
-return this.complexExpressionSql("OR",
- -
[BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
- +
null)]);
- +
[BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
null)]);
}
- 6454
@@ -14637,8 +14663,8 @@} else if (["IN", "NOTIN"].indexOf(op) != -1) {
//literal so that if values is an array of two element arrays, it
//will be treated as a value list instead of a condition specifier.
- 4
-return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
- -
ComplexExpression.IN_OPERATORS[op],
- +
valArray ? this._arraySql(vals) : this.literal(vals));
- +
ComplexExpression.IN_OPERATORS[op],
valArray ? this._arraySql(vals) : this.literal(vals));
}
}
- @@ -14652,13 +14678,13 @@
else {
}
} else {
- 10
-return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
- +
ComplexExpression.IN_OPERATORS[op], this.literal(vals));
ComplexExpression.IN_OPERATORS[op], this.literal(vals));
}
}
- 6437
} else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
- 5427
var l = args[0];
- 5427
-return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
- +
this.literal(args[1]));
this.literal(args[1]));
- 1010
} else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
- 976
return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
- 34
@@ -14701,7 +14727,7 @@} else if (op == "NOT") {
}
- 925
var tref = this.__tableRef(table);
- 925
-return string.format(" %s %s", this._joinTypeSql(jc.joinType),
- +
tableAlias ? this.__asSql(tref, tableAlias) : tref);
tableAlias ? this.__asSql(tref, tableAlias) : tref);
},
- @@ -14774,11 +14800,11 @@
/**
*/
qualifiedIdentifierSql:function (qcr) {
- 4532
-return [qcr.table, qcr.column].map(
- -
function (x) {
- 9064
-return [QualifiedIdentifier, Identifier, String].some(function (c) {
- 25671
-return x instanceof c
- -
}) ? this.literal(x) : this.quoteIdentifier(x)
- +
}, this).join('.');
- +
function (x) {
- 9064
+return [QualifiedIdentifier, Identifier, String].some(function (c) {
- 25671
+return x instanceof c
- +
}) ? this.literal(x) : this.quoteIdentifier(x)
}, this).join('.');
},
- @@ -14813,12 +14839,12 @@
/**
- 33693
var i = this.__identifierInputMethod;
- 33693
v = v.toString(this);
- 33693
-return !isUndefinedOrNull(i) ?
- -
isFunction(v[i]) ?
- -
v[i]() :
- -
isFunction(comb[i]) ?
- -
comb[i](v)
- -
: v
- +
: v;
- +
isFunction(v[i]) ?
- +
v[i]() :
- +
isFunction(comb[i]) ?
- +
comb[i](v)
- +
: v
: v;
},
- @@ -14831,12 +14857,12 @@
/**
- 19885
(v == '' && (v = 'untitled'));
- 19885
var i = this.__identifierOutputMethod;
- 19885
-return !isUndefinedOrNull(i) ?
- -
isFunction(v[i]) ?
- -
v[i]() :
- -
isFunction(comb[i]) ?
- -
comb[i](v)
- -
: v
- +
: v;
- +
isFunction(v[i]) ?
- +
v[i]() :
- +
isFunction(comb[i]) ?
- +
comb[i](v)
- +
: v
: v;
},
- @@ -14932,9 +14958,9 @@
/**
*/
_joinTypeSql:function (joinType) {
- 922
-return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g,
- -
function (m) {
- 1127
-return m.toUpperCase() + " ";
- +
}
- +
function (m) {
- 1127
+return m.toUpperCase() + " ";
}
).trimRight() + " JOIN";
},
- @@ -15060,10 +15086,10 @@
- 6059
var table = parts[0], column = parts[1], alias = parts[2];
- 6059
if (!alias) {
- 6059
-return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'"
- +
+ v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
+ v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
} else {
- 0
-return this.literal(new AliasedExpression(column
- +
&& table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
&& table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
}
},
- @@ -15356,9 +15382,9 @@
- 0
throw new QueryError("No source specified for the query");
}
- 6657
-return " " + source.map(
- -
function (s) {
- 6971
-return this.__tableRef(s);
- +
}, this).join(this._static.COMMA_SEPARATOR);
- +
function (s) {
- 6971
+return this.__tableRef(s);
}, this).join(this._static.COMMA_SEPARATOR);
},
- @@ -15904,7 +15930,7 @@
/**
- 2734
var a = [];
- 2734
var ret = new Promise().classic(cb);
- 2734
-this.forEach(hitch(this, function (r) {
- 2936
+a.push(r);
- 2947
a.push(r);
})).then(hitch(this, function () {
- 2731
this.postLoad(a);
- 2731
@@ -17150,10 +17176,10 @@if (block) {
*
*/
- -
constructor:function () {
- 3218
-if (comb.isUndefinedOrNull(this.__associations)) {
- 3055
+this.__associations = {};
- 3107
+if (comb.isUndefinedOrNull(this.__associations)) {
- 3000
this.__associations = {};
- -
}
- 3218
+this._super(arguments);
- 3107
this._super(arguments);
},
- @@ -18080,240 +18106,29 @@
reload:function () {
* });
* }
- -
*
- -
*
- -
* @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
- -
*
- -
* @throws {patio.ModelError} if name is not a function or string.
- -
* @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
- -
*/
- -
validate:function (name) {
- 41
-this.__initValidation();
- 41
-var ret;
- 41
-if (isFunction(name)) {
- 1
-name.apply(this, [this.__getValidator.bind(this)]);
- 1
-ret = this;
- 40
-} else if (isString(name)) {
- 40
-ret = this.__getValidator(name);
- -
} else {
- 0
-throw new ModelError("name is must be a string or function when validating");
- -
}
- 41
-return ret;
- -
}
- -
}
- -
- -
}).as(module);
- database/logging.js
- |
-
-
- Coverage97.96
- SLOC193
- LOC49
- Missed1
-
- |
-
- 1
-var comb = require("comb"),
- -
define = comb.define,
- -
Promise = comb.Promise,
- -
isFunction = comb.isFunction,
- -
logging = comb.logging,
- -
Logger = logging.Logger,
- -
hitch = comb.hitch,
- -
format = comb.string.format,
- -
QueryError = require("../errors").QueryError;
- -
- -
- 1
-var LOGGER = Logger.getLogger("patio.Database");
- -
- 1
-define(null, {
- -
instance:{
- -
/**@lends patio.Database.prototype*/
- -
- -
/**
- -
* Logs an INFO level message to the "patio.Database" logger.
- -
*/
- -
logInfo:function () {
- 8337
-if (LOGGER.isInfo) {
- 8337
-LOGGER.info.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a DEBUG level message to the "patio.Database" logger.
- -
*/
- -
logDebug:function () {
- 8027
-if (LOGGER.isDebug) {
- 8027
-LOGGER.debug.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs an ERROR level message to the "patio.Database" logger.
- -
*/
- -
logError:function (error) {
- 75
-if (LOGGER.isError) {
- 75
-LOGGER.error.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a WARN level message to the "patio.Database" logger.
- -
*/
- -
logWarn:function () {
- 1
-if (LOGGER.isWarn) {
- 1
-LOGGER.warn.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a TRACE level message to the "patio.Database" logger.
- -
*/
- -
logTrace:function () {
- 1
-if (LOGGER.isTrace) {
- 1
-LOGGER.trace.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a FATAL level message to the "patio.Database" logger.
- -
*/
- -
logFatal:function () {
- 1
-if (LOGGER.isFatal) {
- 1
-LOGGER.fatal.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/* Yield to the block, logging any errors at error level to all loggers,
- -
* and all other queries with the duration at warn or info level.
- -
* */
- -
__logAndExecute:function (sql, args, cb) {
- 8101
-if (isFunction(args)) {
- 8100
-cb = args;
- 8100
-args = null;
- -
}
- -
- 8101
-if (args) {
- 0
-sql = format("%s; %j", sql, args);
- -
}
- 8101
-sql = sql.trim();
- 8101
-var start = new Date();
- 8101
-var ret = new Promise();
- 8101
-if (isFunction(cb)) {
- 8100
-this.logInfo("Executing; %s", sql);
- 8100
-cb().then(hitch(this, function () {
- 8026
-this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
- 8026
-ret.callback.apply(ret, arguments);
- -
}), hitch(this, function (err) {
- 74
-err = new QueryError(format("%s: %s", err.message, sql));
- 74
-this.logError(err);
- 74
-ret.errback.apply(ret, [err]);
- -
}));
- -
} else {
- 1
-throw new QueryError("CB is required");
- -
}
- 8100
-return ret.promise();
- -
},
- -
- -
/*Log the given SQL and then execute it on the connection, used by
- -
*the transaction code.
- -
* */
- -
__logConnectionExecute:function (conn, sql) {
- 2079
-return this.__logAndExecute(sql, hitch(this, function () {
- 2079
-return conn[this.connectionExecuteMethod](sql);
- -
}));
- -
},
- -
- -
- -
getters:{
- -
/**@lends patio.Database.prototype*/
- -
/**
- -
* The "patio.Database" logger.
- -
* @field
- -
*/
- -
logger:function () {
- 2
-return LOGGER;
- -
}
- -
}
- -
},
- -
- -
"static":{
- -
/**@lends patio.Database*/
- -
/**
- -
* Logs an INFO level message to the "patio.Database" logger.
- -
*/
- -
logInfo:function () {
- 1
-if (LOGGER.isInfo) {
- 1
-LOGGER.info.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a DEBUG level message to the "patio.Database" logger.
- -
*/
- -
logDebug:function () {
- 1
-if (LOGGER.isDebug) {
- 1
-LOGGER.debug.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a ERROR level message to the "patio.Database" logger.
- -
*/
- -
logError:function () {
- 1
-if (LOGGER.isError) {
- 1
-LOGGER.error.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a WARN level message to the "patio.Database" logger.
- -
*/
- -
logWarn:function () {
- 1
-if (LOGGER.isWarn) {
- 1
-LOGGER.warn.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a TRACE level message to the "patio.Database" logger.
- -
*/
- -
logTrace:function () {
- 1
-if (LOGGER.isTrace) {
- 1
-LOGGER.trace.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- +
* Logs a FATAL level message to the "patio.Database" logger.
- +
*
- +
* @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
- +
*
- +
* @throws {patio.ModelError} if name is not a function or string.
* @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
- -
*/
- -
logFatal:function () {
- 1
-if (LOGGER.isFatal) {
- 1
-LOGGER.fatal.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
getters:{
- -
/**@lends patio.Database*/
- -
/**
- -
* The "patio.Database" logger.
- -
* @field
- -
*/
- -
logger:function () {
- 1
+return LOGGER;
- +
validate:function (name) {
- 41
+this.__initValidation();
- 41
+var ret;
- 41
+if (isFunction(name)) {
- 1
+name.apply(this, [this.__getValidator.bind(this)]);
- 1
+ret = this;
- 40
+} else if (isString(name)) {
- 40
+ret = this.__getValidator(name);
- +
} else {
- 0
throw new ModelError("name is must be a string or function when validating");
- +
}
- 41
return ret;
}
}
- -
}).as(module);
}).as(module);
},
/**
* Selects the columns if only if there is not already select sources.
*
* @example
*
* var ds = DB.from("items"); //SELECT * FROM items
*
* ds.select("a"); //SELECT a FROM items;
* ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
* ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
*
* @param cols columns to select if there is not already select sources.
* @return {patio.Dataset} a cloned dataset with the appropriate select sources.
*/
selectIfNoSource:function (cols) {
var ret;
if (!this.hasSelectSource) {
ret = this.select.apply(this, arguments);
} else {
ret = this.mergeOptions();
}
return ret;
},
/**
* Returns a copy of the dataset with the given columns added
* to the existing selected columns. If no columns are currently selected,
* it will select the columns given in addition to *.
as(module);
+ database/logging.js
+ |
+
+
+ Coverage97.96
+ SLOC193
+ LOC49
+ Missed1
+
+ |
+
- 1
+var comb = require("comb"),
- +
define = comb.define,
- +
Promise = comb.Promise,
- +
isFunction = comb.isFunction,
- +
logging = comb.logging,
- +
Logger = logging.Logger,
- +
hitch = comb.hitch,
- +
format = comb.string.format,
- +
QueryError = require("../errors").QueryError;
- +
- +
- 1
+var LOGGER = Logger.getLogger("patio.Database");
- +
- 1
+define(null, {
- +
instance:{
- +
/**@lends patio.Database.prototype*/
- +
- +
/**
- +
* Logs an INFO level message to the "patio.Database" logger.
- +
*/
- +
logInfo:function () {
- 8337
+if (LOGGER.isInfo) {
- 8337
+LOGGER.info.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a DEBUG level message to the "patio.Database" logger.
- +
*/
- +
logDebug:function () {
- 8027
+if (LOGGER.isDebug) {
- 8027
+LOGGER.debug.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs an ERROR level message to the "patio.Database" logger.
- +
*/
- +
logError:function (error) {
- 75
+if (LOGGER.isError) {
- 75
+LOGGER.error.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a WARN level message to the "patio.Database" logger.
- +
*/
- +
logWarn:function () {
- 1
+if (LOGGER.isWarn) {
- 1
+LOGGER.warn.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a TRACE level message to the "patio.Database" logger.
- +
*/
- +
logTrace:function () {
- 1
+if (LOGGER.isTrace) {
- 1
+LOGGER.trace.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a FATAL level message to the "patio.Database" logger.
- +
*/
- +
logFatal:function () {
- 1
+if (LOGGER.isFatal) {
- 1
+LOGGER.fatal.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/* Yield to the block, logging any errors at error level to all loggers,
- +
* and all other queries with the duration at warn or info level.
- +
* */
- +
__logAndExecute:function (sql, args, cb) {
- 8101
+if (isFunction(args)) {
- 8100
+cb = args;
- 8100
+args = null;
- +
}
- +
- 8101
+if (args) {
- 0
+sql = format("%s; %j", sql, args);
- +
}
- 8101
+sql = sql.trim();
- 8101
+var start = new Date();
- 8101
+var ret = new Promise();
- 8101
+if (isFunction(cb)) {
- 8100
+this.logInfo("Executing; %s", sql);
- 8100
+cb().then(hitch(this, function () {
- 8026
+this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
- 8026
+ret.callback.apply(ret, arguments);
- +
}), hitch(this, function (err) {
- 74
+err = new QueryError(format("%s: %s", err.message, sql));
- 74
+this.logError(err);
- 74
+ret.errback.apply(ret, [err]);
- +
}));
- +
} else {
- 1
+throw new QueryError("CB is required");
- +
}
- 8100
+return ret.promise();
- +
},
- +
- +
/*Log the given SQL and then execute it on the connection, used by
- +
*the transaction code.
- +
* */
- +
__logConnectionExecute:function (conn, sql) {
- 2079
+return this.__logAndExecute(sql, hitch(this, function () {
- 2079
+return conn[this.connectionExecuteMethod](sql);
- +
}));
- +
},
- +
- +
- +
getters:{
- +
/**@lends patio.Database.prototype*/
- +
/**
- +
* The "patio.Database" logger.
- +
* @field
- +
*/
- +
logger:function () {
- 2
+return LOGGER;
- +
}
- +
}
- +
},
- +
- +
"static":{
- +
/**@lends patio.Database*/
- +
/**
- +
* Logs an INFO level message to the "patio.Database" logger.
- +
*/
- +
logInfo:function () {
- 1
+if (LOGGER.isInfo) {
- 1
+LOGGER.info.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a DEBUG level message to the "patio.Database" logger.
- +
*/
- +
logDebug:function () {
- 1
+if (LOGGER.isDebug) {
- 1
+LOGGER.debug.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a ERROR level message to the "patio.Database" logger.
- +
*/
- +
logError:function () {
- 1
+if (LOGGER.isError) {
- 1
+LOGGER.error.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a WARN level message to the "patio.Database" logger.
- +
*/
- +
logWarn:function () {
- 1
+if (LOGGER.isWarn) {
- 1
+LOGGER.warn.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a TRACE level message to the "patio.Database" logger.
- +
*/
- +
logTrace:function () {
- 1
+if (LOGGER.isTrace) {
- 1
+LOGGER.trace.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a FATAL level message to the "patio.Database" logger.
- +
*/
- +
logFatal:function () {
- 1
+if (LOGGER.isFatal) {
- 1
+LOGGER.fatal.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
getters:{
- +
/**@lends patio.Database*/
- +
/**
- +
* The "patio.Database" logger.
- +
* @field
- +
*/
- +
logger:function () {
- 1
+return LOGGER;
- +
}
- +
}
- +
}
- +
}).as(module);
- Coverage89.22
- SLOC21912
- LOC5351
- Missed577
+ Coverage89.04
+ SLOC21962
+ LOC5365
+ Missed588
|
- Coverage72.53
- SLOC1076
- LOC91
- Missed25
+ Coverage71.74
+ SLOC1078
+ LOC92
+ Missed26
|
@@ -1682,8 +1682,10 @@
- associations/_Association.js
+ dataset/index.js
|
- Coverage87.18
- SLOC515
- LOC156
- Missed20
+ Coverage86.11
+ SLOC457
+ LOC72
+ Missed10
|
- 1
-var comb = require("comb-proxy"),
- +
define = comb.define,
+
- 1
+var comb = require("comb"),
- +
hitch = comb.hitch,
- +
logging = comb.logging,
- +
Logger = logging.Logger,
- +
errors = require("../errors"),
- +
QueryError = errors.QueryError,
- +
DatasetError = errors.DatasetError,
- +
Promise = comb.Promise,
PromiseList = comb.PromiseList,
isUndefined = comb.isUndefined,
- -
isUndefinedOrNull = comb.isUndefinedOrNull,
isBoolean = comb.isBoolean,
- -
isString = comb.isString,
- -
isHash = comb.isHash,
isFunction = comb.isFunction,
- -
isInstanceOf = comb.isInstanceOf,
- -
Promise = comb.Promise,
- -
PromiseList = comb.PromiseList,
- -
hitch = comb.hitch,
- -
array = comb.array,
- -
isArray = comb.isArray,
- -
Middleware = comb.plugins.Middleware,
- -
PatioError = require("../errors").PatioError;
- -
- 1
-var fetch = {
- -
LAZY:"lazy",
- -
EAGER:"eager"
- +
};
- +
isString = comb.isString,
- +
isFunction = comb.isFunction,
- +
isNull = comb.isNull,
- +
merge = comb.merge,
- +
define = comb.define,
- +
graph = require("./graph"),
- +
actions = require("./actions"),
- +
features = require("./features"),
- +
query = require("./query"),
- +
sql = require("./sql"),
- +
SQL = require("../sql").sql,
- +
AliasedExpression = SQL.AliasedExpression,
- +
Identifier = SQL.Identifier,
QualifiedIdentifier = SQL.QualifiedIdentifier;
- -
- -
/**
- -
* @class
- -
* Base class for all associations.
- -
*
- -
* </br>
- -
* <b>NOT to be instantiated directly</b>
- -
* Its just documented for reference.
- -
*
- -
* @constructs
- -
* @param {Object} options
- -
* @param {String} options.model a string to look up the model that we are associated with
- -
* @param {Function} options.filter a callback to find association if a filter is defined then
- -
* the association is read only
- -
* @param {Object} options.key object with left key and right key
- -
* @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
- -
* @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
- -
* the associations are automatically filled, if fetch.Lazy is supplied
- -
* then a promise is returned and is called back with the loaded models
- -
* @property {Model} model the model associatied with this association.
- -
* @name Association
- -
* @memberOf patio.associations
- -
* */
- 1
+define(Middleware, {
- 1
+var LOGGER = comb.logger("patio.Dataset");
- 1
define([actions, graph, features, query, sql], {
- -
instance:{
- -
/**@lends patio.associations.Association.prototype*/
- -
- -
type:"",
- -
- -
//Our associated model
- -
_model:null,
- -
- -
/**
- -
* Fetch type
- -
*/
- -
fetchType:fetch.LAZY,
- -
- -
/**how to order our association*/
- -
orderBy:null,
- -
- -
/**Our filter method*/
- -
filter:null,
- -
- -
__hooks:null,
- -
- -
isOwner:true,
- -
- -
createSetter:true,
- -
- -
isCascading:false,
- -
- -
supportsStringKey:true,
- -
- -
supportsHashKey:true,
- -
supportsCompositeKey:true,
- -
- +
supportsLeftAndRightKey:true,
/**@lends patio.Dataset.prototype*/
- +
/**
* Class that is used for querying/retirving datasets from a database.
- -
*
- -
*Method to call to look up association,
- -
*called after the model has been filtered
- -
**/
- -
_fetchMethod:"all",
- -
- -
- -
constructor:function (options, patio, filter) {
- 55
-options = options || {};
- 55
-if (!options.model) {
- 0
-throw new Error("Model is required for " + this.type + " association");
- -
}
- 55
-this._model = options.model;
- 55
-this.patio = patio;
- 55
-this.__opts = options;
- 55
-!isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
- 55
-this.filter = filter;
- 55
-this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
- 55
-this.__hooks =
- -
{before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
- 55
-var hooks = ["Add", "Remove", "Set", "Load"];
- 55
-["before", "after"].forEach(function (h) {
- 110
-hooks.forEach(function (a) {
- 440
-var hookName = h + a, hook;
- 440
-if (isFunction((hook = options[hookName]))) {
- 0
-this.__hooks[h][a.toLowerCase()] = hook;
- -
}
- -
}, this);
- -
}, this);
- 55
-this.fetchType = options.fetchType || fetch.LAZY;
- -
},
- -
- -
_callHook:function (hook, action, args) {
- 0
-var func = this.__hooks[hook][action], ret;
- 0
-if (isFunction(func)) {
- 0
-ret = func.apply(this, args);
- -
}
- 0
-return ret;
- -
},
- -
- -
_clearAssociations:function (model) {
- 125
-if (!this.readOnly) {
- 125
-delete model.__associations[this.name];
- -
}
- -
},
- -
- -
_forceReloadAssociations:function (model) {
- 243
-if (!this.readOnly) {
- 243
-delete model.__associations[this.name];
- 243
-return model[this.name];
- -
}
- -
},
- -
- -
/**
- -
* @return {Boolean} true if the association is eager.
- -
*/
- -
isEager:function () {
- 2114
-return this.fetchType == fetch.EAGER;
- -
},
- -
- -
_checkAssociationKey:function (parent) {
- 785
-var q = {};
- 785
-this._setAssociationKeys(parent, q);
- 785
-return Object.keys(q).every(function (k) {
- 785
-return q[k] != null
- -
});
- -
},
- -
- -
_getAssociationKey:function () {
- 5205
-var options = this.__opts, key, ret = [], lk, rk;
- 5205
-if (!isUndefinedOrNull((key = options.key))) {
- 722
-if (this.supportsStringKey && isString(key)) {
- -
//normalize the key first!
- 356
-ret = [
- -
[this.isOwner ? this.defaultLeftKey : key],
- -
[this.isOwner ? key : this.defaultRightKey]
- -
];
- 366
-} else if (this.supportsHashKey && isHash(key)) {
- 366
-var leftKey = Object.keys(key)[0];
- 366
-var rightKey = key[leftKey];
- 366
-ret = [
- -
[leftKey],
- -
[rightKey]
- -
];
- 0
-} else if (this.supportsCompositeKey && isArray(key)) {
- 0
-ret = [
- -
[key],
- -
null
- -
];
- -
}
- 4483
-} else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
- -
&& !isUndefinedOrNull((rk = options.rightKey)))) {
- 140
-ret = [
- -
array.toArray(lk), array.toArray(rk)
- -
];
- -
} else {
- -
//todo handle composite primary keys
- 4343
-ret = [
- -
[this.defaultLeftKey],
- -
[this.defaultRightKey]
- -
];
- -
}
- 5205
-return ret;
- -
},
- -
- -
- -
_setAssociationKeys:function (parent, model, val) {
- 1573
-var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
- 1573
-if (leftKey && rightKey) {
- 1573
-for (var i = 0; i < leftKey.length; i++) {
- 1573
-model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
- -
}
- -
} else {
- 0
-leftKey.forEach(function (k) {
- 0
-model[k] = !isUndefined(val) ? val : parent[k];
- -
});
- -
}
- -
},
- -
- -
_setDatasetOptions:function (ds) {
- 974
-var options = this.__opts || {};
- 974
-var order, limit, distinct, select, query;
- -
//allow for multi key ordering
- 974
-if (!isUndefined((select = this.select))) {
- 0
-ds = ds.select.apply(ds, array.toArray(select));
- -
}
- 974
-if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
- 0
-ds = ds.filter(query);
- -
}
- 974
-if (isFunction(this.filter)) {
- 334
-var ret = this.filter.apply(this, [ds]);
- 334
-if (isInstanceOf(ret, ds._static)) {
- 334
-ds = ret;
- -
}
- -
}
- 974
-if (!isUndefined((distinct = options.distinct))) {
- 0
-ds = ds.limit.apply(ds, array.toArray(distinct));
- -
}
- 974
-if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
- 0
-ds = ds.order.apply(ds, array.toArray(order));
- -
}
- 974
-if (!isUndefined((limit = options.limit))) {
- 0
-ds = ds.limit.apply(ds, array.toArray(limit));
- -
}
- 974
-return ds;
- -
- -
},
- -
- +
/**
- +
* <p> Dynamically genertated methods include
- +
* <ul>
- +
* <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
- +
* {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
- +
* to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
- +
* {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
- +
* <ul>
- +
* <li>Conditioned join types that accept conditions.
- +
* <ul>
- +
* <li>inner - INNER JOIN</li>
- +
* <li>fullOuter - FULL OUTER</li>
- +
* <li>rightOuter - RIGHT OUTER JOIN</li>
- +
* <li>leftOuter - LEFT OUTER JOIN</li>
- +
* <li>full - FULL JOIN</li>
- +
* <li>right - RIGHT JOIN</li>
- +
* <li>left - LEFT JOIN</li>
- +
* </ul>
- +
* </li>
- +
* <li>Unconditioned join types that do not accept join conditions
- +
* <ul>
- +
* <li>natural - NATURAL JOIN</li>
- +
* <li>naturalLeft - NATURAL LEFT JOIN</li>
- +
* <li>naturalRight - NATURAL RIGHT JOIN</li>
- +
* <li>naturalFull - NATURA FULLL JOIN</li>
- +
* <li>cross - CROSS JOIN</li>
- +
* </ul>
- +
* </li>
- +
* </ul>
- +
* </li>
- +
* </li>
- +
* </ul>
- +
*
- +
* <p>
- +
* <h4>Features:</h4>
- +
* <p>
- +
* Features that a particular {@link patio.Dataset} supports are shown in the example below.
- +
* If you wish to implement an adapter please override these values depending on the database that
- +
* you are developing the adapter for.
- +
* </p>
- +
* <pre class="code">
- +
* var ds = DB.from("test");
- +
*
- +
* //The default values returned
- +
*
- +
* //Whether this dataset quotes identifiers.
- +
* //Whether this dataset quotes identifiers.
- +
* ds.quoteIdentifiers //=>true
- +
*
- +
* //Whether this dataset will provide accurate number of rows matched for
- +
* //delete and update statements. Accurate in this case is the number of
- +
* //rows matched by the dataset's filter.
- +
* ds.providesAccurateRowsMatched; //=>true
- +
*
- +
* //Times Whether the dataset requires SQL standard datetimes (false by default,
- +
* // as most allow strings with ISO 8601 format).
- +
* ds.requiresSqlStandardDate; //=>false
- +
*
- +
* //Whether the dataset supports common table expressions (the WITH clause).
- +
* ds.supportsCte; //=>true
- +
*
- +
* //Whether the dataset supports the DISTINCT ON clause, false by default.
- +
* ds.supportsDistinctOn; //=>false
- +
*
- +
* //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
- +
* ds.supportsIntersectExcept; //=>true
- +
*
- +
* //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
- +
* ds.supportsIntersectExceptAll; //=>true
- +
*
- +
* //Whether the dataset supports the IS TRUE syntax.
- +
* ds.supportsIsTrue; //=>true
- +
*
- +
* //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
- +
* ds.supportsJoinUsing; //=>true
- +
*
- +
* //Whether modifying joined datasets is supported.
- +
* ds.supportsModifyingJoin; //=>false
- +
*
- +
* //Whether the IN/NOT IN operators support multiple columns when an
- +
* ds.supportsMultipleColumnIn; //=>true
- +
*
- +
* //Whether the dataset supports timezones in literal timestamps
- +
* ds.supportsTimestampTimezone; //=>false
- +
*
- +
* //Whether the dataset supports fractional seconds in literal timestamps
- +
* ds.supportsTimestampUsecs; //=>true
- +
*
- +
* //Whether the dataset supports window functions.
- +
* ds.supportsWindowFunctions; //=>false
- +
* </pre>
- +
* <p>
- +
* <p>
- +
* <h4>Actions</h4>
- +
* <p>
- +
* Each dataset does not actually send any query to the database until an action method has
- +
* been called upon it(with the exception of {@link patio.Dataset#graph} because columns
- +
* from the other table might need retrived in order to set up the graph). Each action
- +
* returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
- +
* that you account for errors otherwise it can be difficult to track down issues.
- +
* The list of action methods is:
- +
* <ul>
- +
* <li>{@link patio.Dataset#all}</li>
- +
* <li>{@link patio.Dataset#one}</li>
- +
* <li>{@link patio.Dataset#avg}</li>
- +
* <li>{@link patio.Dataset#count}</li>
- +
* <li>{@link patio.Dataset#columns}</li>
- +
* <li>{@link patio.Dataset#remove}</li>
- +
* <li>{@link patio.Dataset#forEach}</li>
- +
* <li>{@link patio.Dataset#empty}</li>
- +
* <li>{@link patio.Dataset#first}</li>
- +
* <li>{@link patio.Dataset#get}</li>
- +
* <li>{@link patio.Dataset#import}</li>
- +
* <li>{@link patio.Dataset#insert}</li>
- +
* <li>{@link patio.Dataset#save}</li>
- +
* <li>{@link patio.Dataset#insertMultiple}</li>
- +
* <li>{@link patio.Dataset#saveMultiple}</li>
- +
* <li>{@link patio.Dataset#interval}</li>
- +
* <li>{@link patio.Dataset#last}</li>
- +
* <li>{@link patio.Dataset#map}</li>
- +
* <li>{@link patio.Dataset#max}</li>
- +
* <li>{@link patio.Dataset#min}</li>
- +
* <li>{@link patio.Dataset#multiInsert}</li>
- +
* <li>{@link patio.Dataset#range}</li>
- +
* <li>{@link patio.Dataset#selectHash}</li>
- +
* <li>{@link patio.Dataset#selectMap}</li>
- +
* <li>{@link patio.Dataset#selectOrderMap}</li>
- +
* <li>{@link patio.Dataset#set}</li>
- +
* <li>{@link patio.Dataset#singleRecord}</li>
- +
* <li>{@link patio.Dataset#singleValue}</li>
- +
* <li>{@link patio.Dataset#sum}</li>
- +
* <li>{@link patio.Dataset#toCsv}</li>
- +
* <li>{@link patio.Dataset#toHash}</li>
- +
* <li>{@link patio.Dataset#truncate}</li>
- +
* <li>{@link patio.Dataset#update}</li>
- +
* </ul>
- +
*
- +
* </p>
- +
* </p>
- +
*
- +
* @constructs
- +
*
- +
*
- +
* @param {patio.Database} db the database this dataset should use when querying for data.
- +
* @param {Object} opts options to set on this dataset instance
- +
*
- +
* @property {Function} rowCb callback to be invoked for each row returned from the database.
- +
* the return value will be used as the result of query. The rowCb can also return a promise,
- +
* The resolved value of the promise will be used as result.
- +
*
- +
* @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
- +
* This value will be defaulted to whatever the identifierInputMethod
- +
* is on the database used in initialization.
- +
*
- +
* @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
- +
* This value will be defaulted to whatever the identifierOutputMethod
- +
* is on the database used in initialization.
- +
* @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
- +
* throws a {patio.DatasetError} tf the dataset doesn't have a table.
- +
* <pre class="code">
- +
* DB.from("table").firstSourceAlias;
- +
* //=> "table"
- +
*
- +
* DB.from("table___t").firstSourceAlias;
- +
* //=> "t"
- +
* </pre>
- +
*
- +
* @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
- +
* have a table, raises a {@link patio.erros.DatasetError}.
- +
*<pre class="code">
- +
*
- +
* DB.from("table").firstSourceTable;
- +
* //=> "table"
- +
*
- +
* DB.from("table___t").firstSourceTable;
- +
* //=> "t"
- +
* </pre>
- +
* @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
- +
* <pre class="code">
- +
* DB.from("items").isSimpleSelectAll; //=> true
- +
* DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
- +
* </pre>
- +
* @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
- +
* @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
- +
* delete and update statements. Accurate in this case is the number of
- +
* rows matched by the dataset's filter.
- +
* @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
- +
* as most allow strings with ISO 8601 format).
- +
* @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
- +
* @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
- +
* @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
- +
* @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
- +
* @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
- +
* @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
- +
* @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
- +
* @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
- +
* @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
- +
* @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
- +
* @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
- +
* @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset.
- +
* @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources
- +
* @property {Boolean} hasSelectSource true if this dataset already has a select sources.
- +
*/
- +
constructor:function (db, opts) {
- 29216
+this._super(arguments);
- 29216
+this.db = db;
- 29216
+this.__opts = {};
- 29216
+this.__rowCb = null;
- 29216
+if (db) {
- 15039
+this.__quoteIdentifiers = db.quoteIdentifiers;
- 15039
+this.__identifierInputMethod = db.identifierInputMethod;
- 15039
+this.__identifierOutputMethod = db.identifierOutputMethod;
- +
}
- +
},
- +
- +
- +
/**
- +
* Returns a new clone of the dataset with with the given options merged into the current datasets options.
- +
* If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
- +
* columns are deleted. This method should generally not be called
- +
* directly by user code.
- +
*
- +
* @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
- +
* @return [patio.Dataset] a cloned dataset with the merged options
- +
**/
- +
mergeOptions:function (opts) {
- 14948
+opts = isUndefined(opts) ? {} : opts;
- 14948
+var ds = new this._static(this.db, {});
- 14948
+ds.rowCb = this.rowCb;
- 14948
+this._static.FEATURES.forEach(function (f) {
- 209272
+ds[f] = this[f];
- +
}, this);
- 14948
+ds.__opts = merge({}, this.__opts, opts);
- 14948
+ds.identifierInputMethod = this.identifierInputMethod;
- 14948
+ds.identifierOutputMethod = this.identifierOutputMethod;
- 14948
+var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
- 14948
+if (Object.keys(opts).some(function (o) {
- 13536
+return columnChangeOpts.indexOf(o) != -1;
- +
})) {
- 2456
+ds.__opts.columns = null;
- +
}
- 14948
+return ds;
- +
},
- +
- +
- +
/**
- +
* Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
- +
* or {@link patio.sql.AliasedExpression}, depending on the format:
- +
*
- +
* <ul>
- +
* <li>For columns : table__column___alias.</li>
- +
* <li>For tables : schema__table___alias.</li>
- +
* </ul>
- +
* each portion of the identifier is optional. See example below
- +
*
- +
* @example
- +
*
- +
* ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
- +
* ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
- +
* ds.stringToIdentifier("table__column___alias");
- +
* //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
- +
*
- +
* @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
- +
* or {@link patio.sql.AliasedExpression}.
- +
*
- +
* @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
- +
*/
- +
stringToIdentifier:function (name) {
- 15093
+if (isString(name)) {
- 10138
+var parts = this._splitString(name);
- 10138
+var schema = parts[0], table = parts[1], alias = parts[2];
- 10138
+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 {
- 4955
+return name;
- +
}
- +
},
- +
- +
/**
- +
* Can either be a string or null.
- +
*
- +
*
- +
* @example
- +
* //columns
- +
* table__column___alias //=> table.column as alias
- +
* table__column //=> table.column
- +
* //tables
- +
* schema__table___alias //=> schema.table as alias
- +
* schema__table //=> schema.table
- +
*
- +
* //name and alias
- +
* columnOrTable___alias //=> columnOrTable as alias
- +
*
- +
*
- +
*
- +
* @return {String[]} an array with the elements being:
- +
* <ul>
- +
* <li>For columns :[table, column, alias].</li>
- +
* <li>For tables : [schema, table, alias].</li>
- +
* </ul>
- +
*/
- +
_splitString:function (s) {
- 19662
+var ret, m;
- 19662
+if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
- 189
+ret = m.slice(1);
- +
}
- 19473
+else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
- 28
+ret = [null, m[1], m[2]];
- +
}
- 19445
+else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
- 2079
+ret = [m[1], m[2], null];
- +
}
- +
else {
- 17366
+ret = [null, s, null];
- +
}
- 19662
+return ret;
- +
},
- +
- +
/**
- +
* @ignore
- +
**/
- +
getters:{
- +
- +
rowCb:function () {
- 23052
+return this.__rowCb;
- +
},
- +
- +
identifierInputMethod:function () {
- 14948
+return this.__identifierInputMethod;
- +
},
- +
- +
identifierOutputMethod:function () {
- 14948
+return this.__identifierOutputMethod;
- +
},
- +
- +
firstSourceAlias:function () {
- 579
+var source = this.__opts.from;
- 579
+if (isUndefinedOrNull(source) || !source.length) {
- 2
+throw new DatasetError("No source specified for the query");
- +
}
- 577
+source = source[0];
- 577
+if (isInstanceOf(source, AliasedExpression)) {
- 20
+return source.alias;
- 557
+} else if (isString(source)) {
- 0
+var parts = this._splitString(source);
- 0
+var alias = parts[2];
- 0
+return alias ? alias : source;
- +
} else {
- 557
+return source;
- +
}
- +
},
- +
- +
firstSourceTable:function () {
- 15
+var source = this.__opts.from;
- 15
+if (isUndefinedOrNull(source) || !source.length) {
- 1
+throw new QueryError("No source specified for the query");
- +
}
- 14
+var source = source[0];
- 14
+if (isInstanceOf(source, AliasedExpression)) {
- 3
+return source.expression;
- 11
+} else if (isString(source)) {
- 0
+var parts = this._splitString(source);
- 0
+return source;
- +
} else {
- 11
+return source;
- +
}
- +
},
- +
- +
sourceList:function () {
- 0
+return (this.__opts.from || []).map(this.stringToIdentifier, this);
- +
},
- +
- +
joinSourceList:function () {
- 0
+return (this.__opts.join || []).map(function (join) {
- 0
+return this.stringToIdentifier(join.tableAlias || join.table);
- +
}, this);
- +
},
- +
- +
hasSelectSource:function () {
- 0
+var select = this.__opts.select;
- 0
+return !(isUndefinedOrNull(select) || select.length === 0);
- +
}
- +
},
- +
- +
/**
- +
* @ignore
- +
**/
- +
setters:{
- +
/**@lends patio.Dataset.prototype*/
- +
- +
identifierInputMethod:function (meth) {
- 15038
+this.__identifierInputMethod = meth;
- +
},
- +
- +
identifierOutputMethod:function (meth) {
- 15038
+this.__identifierOutputMethod = meth;
- +
},
- +
- +
rowCb:function (cb) {
- 18564
+if (isFunction(cb) || isNull(cb)) {
- 18559
+this.__rowCb = cb;
- +
} else {
- 5
+throw new DatasetError("rowCb mus be a function");
- +
}
- +
}
- +
}
- +
},
- +
- +
static:{
- +
COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
- +
COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
- +
COLUMN_REF_RE3:/^(\w+)__(\w+)$/
- +
}
- +
}).as(module);
- +
+ associations/_Association.js
+ |
+
+
+ Coverage87.18
+ SLOC515
+ LOC156
+ Missed20
+
+ |
+
- 1
+var comb = require("comb-proxy"),
- +
define = comb.define,
- +
isUndefined = comb.isUndefined,
- +
isUndefinedOrNull = comb.isUndefinedOrNull,
- +
isBoolean = comb.isBoolean,
- +
isString = comb.isString,
- +
isHash = comb.isHash,
- +
isFunction = comb.isFunction,
- +
isInstanceOf = comb.isInstanceOf,
- +
Promise = comb.Promise,
- +
PromiseList = comb.PromiseList,
- +
hitch = comb.hitch,
- +
array = comb.array,
- +
isArray = comb.isArray,
- +
Middleware = comb.plugins.Middleware,
- +
PatioError = require("../errors").PatioError;
- +
- 1
+var fetch = {
- +
LAZY:"lazy",
- +
EAGER:"eager"
- +
};
- +
- +
- +
/**
- +
* @class
- +
* Base class for all associations.
- +
*
- +
* </br>
- +
* <b>NOT to be instantiated directly</b>
- +
* Its just documented for reference.
- +
*
- +
* @constructs
- +
* @param {Object} options
- +
* @param {String} options.model a string to look up the model that we are associated with
- +
* @param {Function} options.filter a callback to find association if a filter is defined then
- +
* the association is read only
- +
* @param {Object} options.key object with left key and right key
- +
* @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
- +
* @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
- +
* the associations are automatically filled, if fetch.Lazy is supplied
- +
* then a promise is returned and is called back with the loaded models
- +
* @property {Model} model the model associatied with this association.
- +
* @name Association
- +
* @memberOf patio.associations
- +
* */
- 1
+define(Middleware, {
- +
instance:{
- +
/**@lends patio.associations.Association.prototype*/
- +
- +
type:"",
- +
- +
//Our associated model
- +
_model:null,
- +
- +
/**
- +
* Fetch type
- +
*/
- +
fetchType:fetch.LAZY,
- +
- +
/**how to order our association*/
- +
orderBy:null,
- +
- +
/**Our filter method*/
- +
filter:null,
- +
- +
__hooks:null,
- +
- +
isOwner:true,
- +
- +
createSetter:true,
- +
- +
isCascading:false,
- +
- +
supportsStringKey:true,
- +
- +
supportsHashKey:true,
- +
- +
supportsCompositeKey:true,
- +
- +
supportsLeftAndRightKey:true,
- +
- +
/**
- +
*
- +
*Method to call to look up association,
- +
*called after the model has been filtered
- +
**/
- +
_fetchMethod:"all",
- +
- +
- +
constructor:function (options, patio, filter) {
- 55
+options = options || {};
- 55
+if (!options.model) {
- 0
+throw new Error("Model is required for " + this.type + " association");
- +
}
- 55
+this._model = options.model;
- 55
+this.patio = patio;
- 55
+this.__opts = options;
- 55
+!isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
- 55
+this.filter = filter;
- 55
+this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
- 55
+this.__hooks =
- +
{before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
- 55
+var hooks = ["Add", "Remove", "Set", "Load"];
- 55
+["before", "after"].forEach(function (h) {
- 110
+hooks.forEach(function (a) {
- 440
+var hookName = h + a, hook;
- 440
+if (isFunction((hook = options[hookName]))) {
- 0
+this.__hooks[h][a.toLowerCase()] = hook;
- +
}
- +
}, this);
- +
}, this);
- 55
+this.fetchType = options.fetchType || fetch.LAZY;
- +
},
- +
- +
_callHook:function (hook, action, args) {
- 0
+var func = this.__hooks[hook][action], ret;
- 0
+if (isFunction(func)) {
- 0
+ret = func.apply(this, args);
- +
}
- 0
+return ret;
- +
},
- +
- +
_clearAssociations:function (model) {
- 125
+if (!this.readOnly) {
- 125
+delete model.__associations[this.name];
- +
}
- +
},
- +
- +
_forceReloadAssociations:function (model) {
- 243
+if (!this.readOnly) {
- 243
+delete model.__associations[this.name];
- 243
+return model[this.name];
- +
}
- +
},
- +
- +
/**
- +
* @return {Boolean} true if the association is eager.
- +
*/
- +
isEager:function () {
- 2114
+return this.fetchType == fetch.EAGER;
- +
},
- +
- +
_checkAssociationKey:function (parent) {
- 785
+var q = {};
- 785
+this._setAssociationKeys(parent, q);
- 785
+return Object.keys(q).every(function (k) {
- 785
+return q[k] != null
- +
});
- +
},
- +
- +
_getAssociationKey:function () {
- 5205
+var options = this.__opts, key, ret = [], lk, rk;
- 5205
+if (!isUndefinedOrNull((key = options.key))) {
- 722
+if (this.supportsStringKey && isString(key)) {
- +
//normalize the key first!
- 356
+ret = [
- +
[this.isOwner ? this.defaultLeftKey : key],
- +
[this.isOwner ? key : this.defaultRightKey]
- +
];
- 366
+} else if (this.supportsHashKey && isHash(key)) {
- 366
+var leftKey = Object.keys(key)[0];
- 366
+var rightKey = key[leftKey];
- 366
+ret = [
- +
[leftKey],
- +
[rightKey]
- +
];
- 0
+} else if (this.supportsCompositeKey && isArray(key)) {
- 0
+ret = [
- +
[key],
- +
null
- +
];
- +
}
- 4483
+} else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
- +
&& !isUndefinedOrNull((rk = options.rightKey)))) {
- 140
+ret = [
- +
array.toArray(lk), array.toArray(rk)
- +
];
- +
} else {
- +
//todo handle composite primary keys
- 4343
+ret = [
- +
[this.defaultLeftKey],
- +
[this.defaultRightKey]
- +
];
- +
}
- 5205
+return ret;
- +
},
- +
- +
- +
_setAssociationKeys:function (parent, model, val) {
- 1573
+var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
- 1573
+if (leftKey && rightKey) {
- 1573
+for (var i = 0; i < leftKey.length; i++) {
- 1573
+model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
- +
}
- +
} else {
- 0
+leftKey.forEach(function (k) {
- 0
+model[k] = !isUndefined(val) ? val : parent[k];
- +
});
- +
}
- +
},
- +
- +
_setDatasetOptions:function (ds) {
- 974
+var options = this.__opts || {};
- 974
+var order, limit, distinct, select, query;
- +
//allow for multi key ordering
- 974
+if (!isUndefined((select = this.select))) {
- 0
+ds = ds.select.apply(ds, array.toArray(select));
- +
}
- 974
+if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
- 0
+ds = ds.filter(query);
- +
}
- 974
+if (isFunction(this.filter)) {
- 334
+var ret = this.filter.apply(this, [ds]);
- 334
+if (isInstanceOf(ret, ds._static)) {
- 334
+ds = ret;
- +
}
- +
}
- 974
+if (!isUndefined((distinct = options.distinct))) {
- 0
+ds = ds.limit.apply(ds, array.toArray(distinct));
- +
}
- 974
+if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
- 0
+ds = ds.order.apply(ds, array.toArray(order));
- +
}
- 974
+if (!isUndefined((limit = options.limit))) {
- 0
+ds = ds.limit.apply(ds, array.toArray(limit));
- +
}
- 974
+return ds;
- +
- +
},
- +
/**
*Filters our associated dataset to load our association.
*
- @@ -6274,9 +6751,9 @@
*@return {Dataset} the dataset with all filters applied.
@@ -6474,24 +6951,24 @@ - Coverage88.36 - SLOC1061 - LOC318 + Coverage88.47 + SLOC1066 + LOC321 Missed37* @borrows patio.Dataset#leftJoin as leftJoin
* */
- -
constructor:function (options, fromDb) {
- 3218
-if (this.synced) {
- 3218
-this.__emitter = new EventEmitter();
- 3218
-this._super(arguments);
- 3218
-this.patio = patio || require("./index");
- 3218
-fromDb = isBoolean(fromDb) ? fromDb : false;
- 3218
-this.__changed = {};
- 3218
-this.__values = {};
- 3218
-if (fromDb) {
- 1753
-this._hook("pre", "load");
- 1753
-this.__isNew = false;
- 1753
-this.__setFromDb(options, true);
- 1753
-if (this._static.emitOnLoad) {
- 1753
-this.emit("load", this);
- 1753
+this._static.emit("load", this);
- 3107
+if (this.synced) {
- 3107
+this.__emitter = new EventEmitter();
- 3107
+this._super(arguments);
- 3107
+this.patio = patio || require("./index");
- 3107
+fromDb = isBoolean(fromDb) ? fromDb : false;
- 3107
+this.__changed = {};
- 3107
+this.__values = {};
- 3107
+if (fromDb) {
- 1754
+this._hook("pre", "load");
- 1754
+this.__isNew = false;
- 1754
+this.__setFromDb(options, true);
- 1754
+if (this._static.emitOnLoad) {
- 1754
+this.emit("load", this);
- 1754
this._static.emit("load", this);
}
- -
} else {
- 1465
-this.__isNew = true;
- 1465
+this.__set(options);
- 1353
+this.__isNew = true;
- 1353
this.__set(options);
}
} else {
- 0
@@ -6499,26 +6976,31 @@throw new ModelError("Model " + this.tableName + " has not been synced");
},
- -
__set:function (values, ignore) {
- 1587
-values = values || {};
- 1587
-this.__ignore = ignore === true;
- 1587
+Object.keys(values).forEach(function (attribute) {
- 1475
+values = values || {};
- 1475
+this.__ignore = ignore === true;
- 1475
Object.keys(values).forEach(function (attribute) {
- 6632
var value = values[attribute];
//check if the column is a constrained value and is allowed to be set
- 6632
!ignore && this._checkIfColumnIsConstrained(attribute);
- 6632
this[attribute] = value;
- -
}, this);
- 1587
+this.__ignore = false;
- 1475
this.__ignore = false;
},
- -
__setFromDb:function (values, ignore) {
- 3056
-values = values || {};
- 3056
-this.__ignore = ignore === true;
- 3056
-Object.keys(values).forEach(function (column) {
- 25828
+var value = values[column];
- 3057
+values = values || {};
- 3057
+this.__ignore = ignore === true;
- 3057
+var schema = this.schema;
- 3057
+Object.keys(values).forEach(function (column) {
- 25830
var value = values[column];
- -
// Typecast value retrieved from db
- 25828
+this.__values[column] = this._typeCastValue(column, value, ignore);
- 25830
+if (schema.hasOwnProperty(column)) {
- 25149
+this.__values[column] = this._typeCastValue(column, value, ignore);
- +
} else {
- 681
+this[column] = value;
}
- -
}, this);
- 3056
+this.__ignore = false;
- 3057
this.__ignore = false;
},
- @@ -6576,11 +7058,11 @@
},
- -
_getColumnValue:function (name) {
- 5426
-var val = this.__values[name];
- 5426
-var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
- 5426
+var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
- 5427
+var val = this.__values[name];
- 5427
+var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
- 5427
var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
- -
- 5426
+return columnValue;
- 5427
return columnValue;
},
- @@ -6600,25 +7082,25 @@
_setColumnValue:function (name, val) {
//typecast_value method, so database adapters can override/augment the handling
//for database specific column types.
- -
_typeCastValue:function (column, value, fromDatabase) {
- 33384
-var colSchema, clazz = this._static;
- 33384
-if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
- 32704
-var type = colSchema.type;
- 32704
+if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
- 32705
+var colSchema, clazz = this._static;
- 32705
+if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
- 32705
+var type = colSchema.type;
- 32705
if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
- 3
value = null;
- -
}
- 32704
-var raiseOnError = clazz.raiseOnTypecastError;
- 32704
+if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
- 32705
+var raiseOnError = clazz.raiseOnTypecastError;
- 32705
if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
- 0
throw new ModelError("null is not allowed for the " + column + " column.");
- -
}
- 32704
-try {
- 32704
+value = clazz.db.typecastValue(type, value);
- 32705
+try {
- 32705
value = clazz.db.typecastValue(type, value);
} catch (e) {
- 0
if (raiseOnError === true) {
- 0
throw e;
}
}
- -
}
- 33384
+return value;
- 32705
return value;
},
- @@ -6690,8 +7172,8 @@
/**
- 0
return emitter.listeners.apply(emitter, arguments);
},
- -
emit:function () {
- 11119
-var emitter = this.__emitter;
- 11119
+return emitter.emit.apply(emitter, arguments);
- 11120
+var emitter = this.__emitter;
- 11120
return emitter.emit.apply(emitter, arguments);
},
- @@ -6732,7 +7214,7 @@
},
- -
schema:function () {
- 73400
+return this._static.schema;
- 75099
return this._static.schema;
},
- @@ -6740,7 +7222,7 @@
columns:function () {
},
- -
synced:function () {
- 12891
+return this._static.synced;
- 12780
return this._static.synced;
}
- @@ -7049,7 +7531,7 @@
}
_defineColumnGetter:function (name) {
- 779
-this.prototype.__defineGetter__(name, function () {
- 5426
+return this._getColumnValue(name);
- 5427
return this._getColumnValue(name);
});
},
- @@ -7105,14 +7587,14 @@
* @type patio.Database
*/
- -
db:function () {
- 46373
-var db = this.__db;
- 46373
+if (!db) {
- 46374
+var db = this.__db;
- 46374
if (!db) {
- 86
db = this.__db = patio.defaultDatabase;
- -
}
- 46373
+if (!db) {
- 46374
if (!db) {
- 0
throw new ModelError("patio has not been connected to a database");
- -
}
- 46373
+return db;
- 46374
return db;
},
- @@ -7159,8 +7641,8 @@
/**
* @type Object
*/
- -
schema:function () {
- 73404
-if (this.synced) {
- 73404
+return this.__schema;
- 75103
+if (this.synced) {
- 75103
return this.__schema;
} else {
- 0
throw new ModelError("Model has not been synced yet");
- @@ -9060,21 +9542,21 @@
}
- 1
-var virtualRow = function (name) {
- 1069
-var WILDCARD = new LiteralString('*');
- 1069
-var QUESTION_MARK = new LiteralString('?');
- 1069
-var COMMA_SEPARATOR = new LiteralString(', ');
- 1069
-var DOUBLE_UNDERSCORE = '__';
- -
- 1069
-var parts = name.split(DOUBLE_UNDERSCORE);
- 1069
-var table = parts[0], column = parts[1];
- 1069
-var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
- 1069
+var prox = methodMissing(ident, function (m) {
- 875
+var WILDCARD = new LiteralString('*');
- 875
+var QUESTION_MARK = new LiteralString('?');
- 875
+var COMMA_SEPARATOR = new LiteralString(', ');
- 875
+var DOUBLE_UNDERSCORE = '__';
- +
- 875
+var parts = name.split(DOUBLE_UNDERSCORE);
- 875
+var table = parts[0], column = parts[1];
- 875
+var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
- 875
var prox = methodMissing(ident, function (m) {
- 4
return function () {
- 3
var args = argsToArray(arguments);
- 3
return SQLFunction.fromArgs([m, name].concat(args));
}
- -
}, column ? QualifiedIdentifier : Identifier);
- 1069
+var ret = createFunctionWrapper(prox, function (m) {
- 875
var ret = createFunctionWrapper(prox, function (m) {
- 548
var args = argsToArray(arguments);
- 548
if (args.length) {
- 542
@@ -9084,8 +9566,8 @@return SQLFunction.fromArgs([name].concat(args));
}, function () {
- 0
return SQLFunction.fromArgs(arguments);
- -
});
- 1069
-ret.__proto__ = ident;
- 1069
+return ret;
- 875
+ret.__proto__ = ident;
- 875
return ret;
};
- 1
@@ -9472,3395 +9954,2938 @@var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
- 442
return args.length > 1 ? PlaceHolderLiteralString.fromArgs(args) : new LiteralString(s);
},
- -
- -
/**
- -
* Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.
- -
*
- -
* @example
- -
*
- -
* sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)
- -
*
- -
*/
- -
"case":function (hash, /*args**/opts) {
- 2
-var args = argsToArray(arguments, 1);
- 2
-return CaseExpression.fromArgs([hashToArray(hash)].concat(args));
- -
},
- -
- -
/**
- -
* Creates a {@link patio.sql.StringExpression}
- -
*
- -
* Return a {@link patio.sql.StringExpression} representing an SQL string made up of the
- -
* concatenation of this array's elements. If an joiner is passed
- -
* it is used in between each element of the array in the SQL
- -
* concatenation.
- -
*
- -
* @example
- -
* patio.sql.sqlStringJoin(["a"]); //=> a
- -
* //you can use sql.* as a shortcut to get an identifier
- -
* patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b
- -
* patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'
- -
* patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b
- -
*/
- -
sqlStringJoin:function (arr, joiner) {
- 6
-joiner = joiner || null;
- 6
-var args;
- 6
-arr = arr.map(function (a) {
- 12
-return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)
- -
});
- 6
-if (joiner) {
- 4
-var newJoiner = [];
- 4
-for (var i = 0; i < arr.length; i++) {
- 9
-newJoiner.push(joiner);
- -
}
- 4
-args = array.flatten(array.zip(arr, newJoiner));
- 4
-args.pop();
- -
} else {
- 2
-args = arr;
- -
}
- 6
-args = args.map(function (a) {
- 17
-return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;
- -
});
- 6
-return StringExpression.fromArgs(["||"].concat(args));
- -
},
- -
- -
Year:Year,
- -
TimeStamp:TimeStamp,
- -
Time:Time,
- -
DateTime:DateTime,
- -
Float:Float,
- -
Decimal:Decimal
- -
- -
};
- -
- 1
-sql.__defineGetter__("patio", function () {
- 0
-!patio && (patio = require("./index"));
- 0
-return patio;
- -
});
- -
- 1
-exports.sql = methodMissing(sql, function (name) {
- 1069
-return virtualRow(name);
- -
});
- -
- 1
-var OPERTATOR_INVERSIONS = {
- -
AND:"OR",
- -
OR:"AND",
- -
GT:"lte",
- -
GTE:"lt",
- -
LT:"gte",
- -
LTE:"gt",
- -
EQ:"neq",
- -
NEQ:"eq",
- -
LIKE:'NOT LIKE',
- -
"NOT LIKE":"LIKE",
- -
'!~*':'~*',
- -
'~*':'!~*',
- -
"~":'!~',
- -
"IN":'NOTIN',
- -
"NOTIN":"IN",
- -
"IS":'IS NOT',
- -
"ISNOT":"IS",
- -
NOT:"NOOP",
- -
NOOP:"NOT",
- -
ILIKE:'NOT ILIKE',
- -
NOTILIKE:"ILIKE"
- -
};
- -
- -
// Standard mathematical operators used in +NumericMethods+
- 1
-var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};
- -
- -
// Bitwise mathematical operators used in +NumericMethods+
- 1
-var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};
- -
- -
- 1
-var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};
- -
- -
//Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+
- 1
-var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};
- -
- -
//Operators that use IN/NOT IN for inclusion/exclusion
- 1
-var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};
- -
- -
//Operators that use IS, used for special casing to override literal true/false values
- 1
-var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};
- -
- -
//Operator symbols that take exactly two arguments
- 1
-var TWO_ARITY_OPERATORS = merge({
- -
EQ:'=',
- -
NEQ:'!=', LIKE:"LIKE",
- -
"NOT LIKE":'NOT LIKE',
- -
ILIKE:"ILIKE",
- -
"NOT ILIKE":'NOT ILIKE',
- -
"~":"~",
- -
'!~':"!~",
- -
'~*':"~*",
- -
'!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);
- -
- -
//Operator symbols that take one or more arguments
- 1
-var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);
- -
- -
//Operator symbols that take only a single argument
- 1
-var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};
- -
- -
/**
- -
* @class Mixin to provide alias methods to an expression.
- -
*
- -
* @name AliasMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var AliasMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.AliasMethods.prototype*/
- -
- -
/**
- -
* Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression
- -
* to the given alias.
- -
*
- -
* @example
- -
*
- -
* sql.identifier("column").as("alias");
- -
* //=> "column" AS "alias"
- -
*
- -
* @param {String} alias the alias to assign to the expression.
- -
*
- -
* @return {patio.sql.AliasedExpression} the aliased expression.
- -
*/
- -
as:function (alias) {
- 552
-return new AliasedExpression(this, alias);
- -
}
- -
- -
}
- -
}).as(sql, "AliasMethods");
- -
- 1
-var bitWiseMethod = function (op) {
- 5
-return function (expression) {
- 0
-if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
- -
}
- -
else {
- 0
-return new BooleanExpression(op, this, expression);
- -
}
- -
}
- -
};
- -
- -
/**
- -
* @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These
- -
* methods are only on {@link patio.sql.NumericExpression}
- -
*
- -
* @example
- -
* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
- -
* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
- -
* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
- -
* sql.a.sqlNumber.leftShift("b") // "a" << "b"
- -
* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
- -
*
- -
* @name BitWiseMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var BitWiseMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.BitWiseMethods.prototype*/
- -
- -
/**
- -
* Bitwise and
- -
*
- -
* @example
- -
* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
- -
*/
- -
bitWiseAnd:bitWiseMethod("bitWiseAnd"),
- -
- -
/**
- -
* Bitwise or
- -
*
- -
* @example
- -
* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
- -
*/
- -
bitWiseOr:bitWiseMethod("bitWiseOr"),
- -
- -
/**
- -
* Exclusive Or
- -
*
- -
* @example
- -
*
- -
* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
- -
*/
- -
exclusiveOr:bitWiseMethod("exclusiveOr"),
- -
- -
/**
- -
* Bitwise shift left
- -
*
- -
* @example
- -
*
- -
* sql.a.sqlNumber.leftShift("b") // "a" << "b"
- -
*/
- -
leftShift:bitWiseMethod("leftShift"),
- -
- -
/**
- -
* Bitwise shift right
- -
*
- -
* @example
- -
*
- -
* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
- -
*/
- -
rightShift:bitWiseMethod("rightShift")
- -
}
- +
}).as(sql, "BitWiseMethods");
- +
/**
- +
* Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.
- +
*
- +
* @example
- +
*
- +
* sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)
- +
*
- +
*/
- +
"case":function (hash, /*args**/opts) {
- 2
+var args = argsToArray(arguments, 1);
- 2
+return CaseExpression.fromArgs([hashToArray(hash)].concat(args));
},
- -
- 1
-var booleanMethod = function (op) {
- 2
-return function (expression) {
- 7
-if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");
- -
}
- -
else {
- 7
+return new BooleanExpression(op, this, expression);
- +
/**
- +
* Creates a {@link patio.sql.StringExpression}
- +
*
- +
* Return a {@link patio.sql.StringExpression} representing an SQL string made up of the
- +
* concatenation of this array's elements. If an joiner is passed
- +
* it is used in between each element of the array in the SQL
- +
* concatenation.
- +
*
- +
* @example
- +
* patio.sql.sqlStringJoin(["a"]); //=> a
- +
* //you can use sql.* as a shortcut to get an identifier
- +
* patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b
- +
* patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'
- +
* patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b
- +
*/
- +
sqlStringJoin:function (arr, joiner) {
- 6
+joiner = joiner || null;
- 6
+var args;
- 6
+arr = arr.map(function (a) {
- 12
+return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)
- +
});
- 6
+if (joiner) {
- 4
+var newJoiner = [];
- 4
+for (var i = 0; i < arr.length; i++) {
- 9
+newJoiner.push(joiner);
- +
}
- 4
+args = array.flatten(array.zip(arr, newJoiner));
- 4
+args.pop();
- +
} else {
- 2
args = arr;
- -
}
- -
}
- -
};
- -
- -
/**
- -
* @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators
- -
* that are defined on objects that can be used in a boolean context in SQL
- -
* ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).
- -
*
- -
* @example
- -
* sql.a.and(sql.b) //=> "a" AND "b"
- -
* sql.a.or(sql.b) //=> "a" OR "b"
- -
* sql.a.not() //=> NOT "a"
- -
*
- -
* @name BooleanMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var BooleanMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.BooleanMethods.prototype*/
- -
- -
/**
- -
*
- -
* @function
- -
* Logical AND
- -
*
- -
* @example
- -
*
- -
* sql.a.and(sql.b) //=> "a" AND "b"
- -
*
- -
* @return {patio.sql.BooleanExpression} a ANDed boolean expression.
- -
*/
- +
and:booleanMethod("and"),
- 6
+args = args.map(function (a) {
- 17
+return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;
- +
});
- 6
+return StringExpression.fromArgs(["||"].concat(args));
},
- -
- -
/**
- -
* @function
- -
* Logical OR
- -
*
- -
* @example
- -
*
- -
* sql.a.or(sql.b) //=> "a" OR "b"
- -
*
- -
* @return {patio.sql.BooleanExpression} a ORed boolean expression
- -
*/
- +
or:booleanMethod("or"),
- +
Year:Year,
- +
TimeStamp:TimeStamp,
- +
Time:Time,
- +
DateTime:DateTime,
- +
Float:Float,
Decimal:Decimal
- -
- -
/**
- -
* Logical NOT
- -
*
- -
* @example
- -
*
- -
* sql.a.not() //=> NOT "a"
- -
*
- -
* @return {patio.sql.BooleanExpression} a inverted boolean expression.
- -
*/
- -
not:function () {
- 5
-return BooleanExpression.invert(this);
- +
}
};
- -
- -
}
- +
}).as(sql, "BooleanMethods");
- 1
+sql.__defineGetter__("patio", function () {
- 0
+!patio && (patio = require("./index"));
- 0
+return patio;
});
- -
- -
/**
- -
* @class Defines case methods
- -
*
- -
* @name CastMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var CastMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.CastMethods.prototype*/
- -
/**
- -
* Cast the reciever to the given SQL type.
- -
*
- -
* @example
- -
*
- -
* sql.a.cast("integer") //=> CAST(a AS integer)
- -
* sql.a.cast(String) //=> CAST(a AS varchar(255))
- -
*
- -
* @return {patio.sql.Cast} the casted expression
- -
*/
- -
cast:function (type) {
- 2
-return new Cast(this, type);
- +
},
- 1
+exports.sql = methodMissing(sql, function (name) {
- 875
+return virtualRow(name);
});
- -
- -
/**
- -
* Cast the reciever to the given SQL type (or the database's default Number type if none given.
- -
*
- -
* @example
- -
*
- -
* sql.a.castNumeric() //=> CAST(a AS integer)
- -
* sql.a.castNumeric("double") //=> CAST(a AS double precision)
- -
*
- -
* @param type the numeric type to cast to
- -
*
- -
* @return {patio.sql.NumericExpression} a casted numberic expression
- -
*/
- -
castNumeric:function (type) {
- 0
-return this.cast(type || "integer").sqlNumber;
- +
},
- 1
+var OPERTATOR_INVERSIONS = {
- +
AND:"OR",
- +
OR:"AND",
- +
GT:"lte",
- +
GTE:"lt",
- +
LT:"gte",
- +
LTE:"gt",
- +
EQ:"neq",
- +
NEQ:"eq",
- +
LIKE:'NOT LIKE',
- +
"NOT LIKE":"LIKE",
- +
'!~*':'~*',
- +
'~*':'!~*',
- +
"~":'!~',
- +
"IN":'NOTIN',
- +
"NOTIN":"IN",
- +
"IS":'IS NOT',
- +
"ISNOT":"IS",
- +
NOT:"NOOP",
- +
NOOP:"NOT",
- +
ILIKE:'NOT ILIKE',
- +
NOTILIKE:"ILIKE"
};
- -
- -
/**
- -
* Cast the reciever to the given SQL type (or the database's default String type if none given),
- -
* and return the result as a {@link patio.sql.StringExpression}.
- -
*
- -
* @example
- -
*
- -
* sql.a.castString() //=> CAST(a AS varchar(255))
- -
* sql.a.castString("text") //=> CAST(a AS text)
- -
* @param type the string type to cast to
- -
*
- -
* @return {patio.sql.StringExpression} the casted string expression
- -
*/
- -
castString:function (type) {
- 0
-return this.cast(type || String).sqlString;
- -
}
- -
}
- +
}).as(sql, "CastMethods");
- +
// Standard mathematical operators used in +NumericMethods+
- 1
var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};
- +
- +
// Bitwise mathematical operators used in +NumericMethods+
- 1
var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};
- -
- -
/**
- -
* @class Provides methods to assist in assigning a SQL type to
- -
* particular types, i.e. Boolean, Function, Number or String.
- -
*
- -
* @name ComplexExpressionMethods
- -
* @memberOf patio.sql
- -
* @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.
- -
* @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.
- -
* @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.
- -
* <pre class="code">
- -
* sql.a.not("a") //=> NOT "a"
- -
* sql.a.sqlNumber.not() //=> ~"a"
- -
* </pre>
- -
* @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.
- -
* <pre class="code">
- -
* sql.a.plus(sql.b); //=> "a" + "b"
- -
* sql.a.sqlString.concat(sql.b) //=> "a" || "b"
- -
* </pre>
- -
*/
- 1
-var ComplexExpressionMethods = define(null, {
- -
instance:{
- -
/**@ignore*/
getters:{
- -
- -
/**
- -
* @ignore
- -
*/
- -
sqlBoolean:function () {
- 0
-return new BooleanExpression("noop", this);
- +
},
- 1
var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};
- +
- +
//Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+
- 1
var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};
- -
- -
/**
- -
* @ignore
- -
*/
- -
sqlFunction:function () {
- 13
-return new SQLFunction(this);
- +
},
- +
//Operators that use IN/NOT IN for inclusion/exclusion
- 1
var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};
- +
- +
//Operators that use IS, used for special casing to override literal true/false values
- 1
var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};
- -
- -
/**
- -
* @ignore
- -
*/
- -
sqlNumber:function () {
- 50
-return new NumericExpression("noop", this);
- +
},
- +
//Operator symbols that take exactly two arguments
- 1
+var TWO_ARITY_OPERATORS = merge({
- +
EQ:'=',
- +
NEQ:'!=', LIKE:"LIKE",
- +
"NOT LIKE":'NOT LIKE',
- +
ILIKE:"ILIKE",
- +
"NOT ILIKE":'NOT ILIKE',
- +
"~":"~",
- +
'!~':"!~",
- +
'~*':"~*",
'!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);
- -
- -
/**
- -
* @ignore
- -
*/
- -
sqlString:function () {
- 0
-return new StringExpression("noop", this);
- -
}
- -
}
- -
}
- +
}).as(sql, "ComplexExpressionMethods");
- +
//Operator symbols that take one or more arguments
- 1
var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);
- -
- 1
-var inequalityMethod = function (op) {
- 6
-return function (expression) {
- 88
-if (isInstanceOf(expression, BooleanExpression)
- -
|| isBoolean(expression)
- -
|| isNull(expression)
- -
|| (isHash(expression))
- -
|| isArray(expression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
- -
} else {
- 88
-return new BooleanExpression(op, this, expression);
- -
}
- -
}
- +
};
- +
//Operator symbols that take only a single argument
- 1
var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};
- -
/**
- -
* @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
- -
* used in a numeric or string context in SQL.
- -
*
- -
* @example
- -
* sql.a.gt("b") //=> a > "b"
- -
* sql.a.lt("b") //=> a > "b"
- -
* sql.a.gte("b") //=> a >= "b"
- -
* sql.a.lte("b") //=> a <= "b"
- +
* sql.a.eq("b") //=> a = "b"
* @class Mixin to provide alias methods to an expression.
- -
*
- +
* @name InequalityMethods
* @name AliasMethods
* @memberOf patio.sql
- -
*/
- 1
+var InequalityMethods = define(null, {
- 1
var AliasMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.InequalityMethods.prototype*/
- -
- -
/**
- -
* @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.
- -
* @example
- -
*
- -
* sql.a.gt("b") //=> a > "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
gt:inequalityMethod("gt"),
- -
/**
- -
* @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- -
* @example
- -
*
- -
* sql.a.gte("b") //=> a >= "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
gte:inequalityMethod("gte"),
- -
/**
- -
* @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- -
* @example
- -
*
- -
* sql.a.lt("b") //=> a < "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
lt:inequalityMethod("lt"),
- -
/**
- -
* @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- -
* @example
- -
*
- -
* sql.a.lte("b") //=> a <= "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
lte:inequalityMethod("lte"),
- -
/**
- -
* @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- -
* @example
- -
*
- -
* sql.a.eq("b") //=> a = "b"
- -
*
- -
* @return {patio.sql.BooleanExpression}
- -
*/
- -
eq:inequalityMethod("eq"),
- -
- +
neq:inequalityMethod("neq"),
/**@lends patio.sql.AliasMethods.prototype*/
- -
/**
- -
* @private
- -
*
- +
* Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
- +
* Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression
* to the given alias.
*
* @example
- -
*
- -
* sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))
- +
* sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))
- +
* sql.identifier("column").as("alias");
* //=> "column" AS "alias"
- -
*
- +
* @param {Object} items a two element array where the first element it the item to be gte and the second item lte.
* @param {String} alias the alias to assign to the expression.
- -
*
- +
* @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
* @return {patio.sql.AliasedExpression} the aliased expression.
- -
*/
- -
between:function (items) {
- 6
+return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))
- +
as:function (alias) {
- 552
return new AliasedExpression(this, alias);
- -
}
- -
}
}).as(sql, "InequalityMethods");
- -
- -
/**
- -
* @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},
- -
* so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}
- -
* or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>
- -
*
- -
* @name NoBooleanInputMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var NoBooleanInputMethods = define(null, {
- -
instance:{
- -
constructor:function (op) {
- 22
-var args = argsToArray(arguments, 1);
- 22
-args.forEach(function (expression) {
- 26
-if ((isInstanceOf(expression, BooleanExpression))
- -
|| isBoolean(expression)
- -
|| isNull(expression)
- -
|| (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))
- -
|| isArray(expression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
- -
}
- -
});
- 22
-this._super(arguments);
}
- -
}
- +
}).as(sql, "NoBooleanInputMethods");
}).as(sql, "AliasMethods");
- -
- 1
-var numericMethod = function (op) {
- 4
-return function (expression) {
- 12
+if (isInstanceOf(expression, BooleanExpression, StringExpression)) {
- 1
+var bitWiseMethod = function (op) {
- 5
+return function (expression) {
- 0
if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {
- 0
-throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
- -
} else {
- 12
+return new NumericExpression(op, this, expression);
- +
}
- +
else {
- 0
return new BooleanExpression(op, this, expression);
}
}
};
- -
- -
/**
- -
* @class This mixin includes the standard mathematical methods (+, -, *, and /)
- +
* that are defined on objects that can be used in a numeric context in SQL.
- +
* @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These
* methods are only on {@link patio.sql.NumericExpression}
*
- -
* @example
- -
* sql.a.plus(sql.b) //=> "a" + "b"
- -
* sql.a.minus(sql.b) //=> "a" - "b"
- -
* sql.a.multiply(sql.b) //=> "a" * "b"
- +
* sql.a.divide(sql.b) //=> "a" / "b"
- +
* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
- +
* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
- +
* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
- +
* sql.a.sqlNumber.leftShift("b") // "a" << "b"
* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
- -
*
- +
* @name NumericMethods
* @name BitWiseMethods
* @memberOf patio.sql
- -
*/
- 1
+var NumericMethods = define(null, {
- 1
var BitWiseMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.NumericMethods.prototype*/
- -
- -
- -
/**
- -
* @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @example
- -
*
- -
* sql.a.plus(sql.b) //=> "a" + "b"
- -
*
- -
* @return {patio.sql.NumericExpression}
- -
*/
- +
plus:numericMethod("plus"),
/**@lends patio.sql.BitWiseMethods.prototype*/
- -
/**
- +
* @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.
* Bitwise and
*
- -
* @example
- -
*
- -
* sql.a.minus(sql.b) //=> "a" - "b"
- -
*
- +
* @return {patio.sql.NumericExpression}
* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"
- -
*/
- +
minus:numericMethod("minus"),
bitWiseAnd:bitWiseMethod("bitWiseAnd"),
- -
/**
- +
* @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
* Bitwise or
*
- -
* @example
- -
*
- -
* sql.a.divide(sql.b) //=> "a" / "b"
- -
*
- +
* @return {patio.sql.NumericExpression}
* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"
- -
*/
- +
divide:numericMethod("divide"),
bitWiseOr:bitWiseMethod("bitWiseOr"),
- -
/**
- +
* @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
* Exclusive Or
*
* @example
- -
*
- -
* sql.a.multiply(sql.b) //=> "a" * "b"
- -
*
- +
* @return {patio.sql.NumericExpression}
* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"
- -
*/
- -
multiply:numericMethod("multiply")
- -
}
- -
}).as(sql, "NumericMethods");
- -
- -
- -
/**
- -
* @class This mixin provides ordering methods ("asc", "desc") to expression.
- -
*
- -
* @example
- -
*
- -
* sql.name.asc(); //=> name ASC
- -
* sql.price.desc(); //=> price DESC
- -
* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
- -
* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
- -
*
- -
* @name OrderedMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var OrderedMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.OrderedMethods.prototype*/
exclusiveOr:bitWiseMethod("exclusiveOr"),
- -
/**
- +
* Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
* Bitwise shift left
- -
*
- -
* @example
- -
* sql.name.asc(); //=> name ASC
- +
* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
* @example
- -
*
- -
* @param {Object} [options] options to use when sorting
- -
* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
- -
* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
- +
* @return {patio.sql.OrderedExpression}
* sql.a.sqlNumber.leftShift("b") // "a" << "b"
- -
*/
- -
asc:function (options) {
- 7
-return new OrderedExpression(this, false, options);
- +
},
leftShift:bitWiseMethod("leftShift"),
- -
/**
- -
* Mark the receiving SQL column as sorting in a descending fashion.
- +
* @example
* Bitwise shift right
- -
*
- -
* sql.price.desc(); //=> price DESC
- +
* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
* @example
- -
*
- -
* @param {Object} [options] options to use when sorting
- -
* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
- -
* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
- +
* @return {patio.sql.OrderedExpression}
* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"
- -
*/
- -
desc:function (options) {
- 26
-return new OrderedExpression(this, true, options);
- +
}
rightShift:bitWiseMethod("rightShift")
- -
}
- +
}).as(sql, "OrderedMethods");
}).as(sql, "BitWiseMethods");
- +
- 1
+var booleanMethod = function (op) {
- 2
+return function (expression) {
- 7
+if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
- 0
+throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");
- +
}
- +
else {
- 7
+return new BooleanExpression(op, this, expression);
- +
}
- +
}
};
- -
/**
- +
* @class This mixin provides methods related to qualifying expression.
- +
* @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators
- +
* that are defined on objects that can be used in a boolean context in SQL
* ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).
*
- +
* @example
- +
* sql.a.and(sql.b) //=> "a" AND "b"
- +
* sql.a.or(sql.b) //=> "a" OR "b"
* sql.a.not() //=> NOT "a"
- -
*
- -
* sql.column.qualify("table") //=> "table"."column"
- -
* sql.table.qualify("schema") //=> "schema"."table"
- -
* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
- -
*
- +
* @name QualifyingMethods
* @name BooleanMethods
* @memberOf patio.sql
- -
*/
- 1
+var QualifyingMethods = define(null, {
- 1
var BooleanMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.QualifyingMethods.prototype*/
/**@lends patio.sql.BooleanMethods.prototype*/
- -
/**
- +
* Qualify the receiver with the given qualifier (table for column/schema for table).
- +
*
- +
* @function
* Logical AND
*
- -
* @example
- -
* sql.column.qualify("table") //=> "table"."column"
- -
* sql.table.qualify("schema") //=> "schema"."table"
* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
- -
*
- +
* @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.
* sql.a.and(sql.b) //=> "a" AND "b"
- -
*
- +
* @return {patio.sql.QualifiedIdentifier}
* @return {patio.sql.BooleanExpression} a ANDed boolean expression.
- -
*/
- -
qualify:function (qualifier) {
- 576
-return new QualifiedIdentifier(qualifier, this);
- +
},
and:booleanMethod("and"),
- -
/**
- +
* Use to create a .* expression.
- +
* @function
* Logical OR
*
- -
* @example
- -
* sql.table.all() //=> "table".*
* sql.table.qualify("schema").all() //=> "schema"."table".*
- +
*
* sql.a.or(sql.b) //=> "a" OR "b"
- -
*
- +
* @return {patio.sql.ColumnAll}
* @return {patio.sql.BooleanExpression} a ORed boolean expression
- -
*/
- -
all:function () {
- 3
-return new ColumnAll(this);
- +
}
or:booleanMethod("or"),
- +
- +
/**
- +
* Logical NOT
- +
*
- +
* @example
- +
*
- +
* sql.a.not() //=> NOT "a"
- +
*
- +
* @return {patio.sql.BooleanExpression} a inverted boolean expression.
- +
*/
- +
not:function () {
- 5
+return BooleanExpression.invert(this);
}
- -
}
- -
}).as(sql, "QualifyingMethods");
- +
}).as(sql, "BooleanMethods");
- -
/**
- -
* @class This mixin provides SQL string methods such as (like and iLike).
- -
*
- -
* @example
- -
*
- -
* sql.a.like("A%"); //=> "a" LIKE 'A%'
- -
* sql.a.iLike("A%"); //=> "a" LIKE 'A%'
- +
* sql.a.like(/^a/); //=> "a" ~* '^a'
* @class Defines case methods
- -
*
- +
* @name StringMethods
* @name CastMethods
* @memberOf patio.sql
- -
*/
- 1
+var StringMethods = define(null, {
- 1
var CastMethods = define(null, {
- -
instance:{
- -
/**@lends patio.sql.StringMethods.prototype*/
- +
/**@lends patio.sql.CastMethods.prototype*/
- -
/**
- -
* Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver
- +
* with the given patterns. See {@link patio.sql.StringExpression#like}.
* Cast the reciever to the given SQL type.
*
- -
* @example
* sql.a.iLike("A%"); //=> "a" LIKE 'A%'
- -
*
- +
* @return {patio.sql.BooleanExpression}
- +
* sql.a.cast("integer") //=> CAST(a AS integer)
- +
* sql.a.cast(String) //=> CAST(a AS varchar(255))
- +
*
* @return {patio.sql.Cast} the casted expression
- -
*/
- -
ilike:function (expression) {
- 310
-expression = argsToArray(arguments);
- 310
-return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([
- -
{caseInsensitive:true}
- +
]));
- +
cast:function (type) {
- 2
return new Cast(this, type);
},
- -
/**
- -
* Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with
- +
* the given patterns. See {@link patio.sql.StringExpression#like}.
* Cast the reciever to the given SQL type (or the database's default Number type if none given.
*
- -
* @example
- -
* sql.a.like(/^a/); //=> "a" ~* '^a'
* sql.a.like("A%"); //=> "a" LIKE 'A%'
- -
*
- +
* @param expression
- +
* sql.a.castNumeric() //=> CAST(a AS integer)
- +
* sql.a.castNumeric("double") //=> CAST(a AS double precision)
- +
*
- +
* @param type the numeric type to cast to
- +
*
* @return {patio.sql.NumericExpression} a casted numberic expression
- -
*/
- -
like:function (expression) {
- 11
-expression = argsToArray(arguments);
- 11
-return StringExpression.like.apply(StringExpression, [this].concat(expression));
- -
}
- -
}
- -
}).as(sql, "StringMethods");
- -
- -
/**
- -
* @class This mixin provides string concatenation methods ("concat");
- -
*
- -
* @example
- -
*
- -
* sql.x.sqlString.concat("y"); //=> "x" || "y"
- -
*
- -
* @name StringConcatenationMethods
- -
* @memberOf patio.sql
- -
*/
- 1
-var StringConcatenationMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.StringConcatenationMethods.prototype*/
- +
castNumeric:function (type) {
- 0
+return this.cast(type || "integer").sqlNumber;
},
- -
/**
- -
* Return a {@link patio.sql.StringExpression} representing the concatenation of this expression
- +
* with the given argument.
- +
* Cast the reciever to the given SQL type (or the database's default String type if none given),
* and return the result as a {@link patio.sql.StringExpression}.
*
* @example
- -
*
- +
* sql.x.sqlString.concat("y"); //=> "x" || "y"
- +
* sql.a.castString() //=> CAST(a AS varchar(255))
- +
* sql.a.castString("text") //=> CAST(a AS text)
* @param type the string type to cast to
- -
*
- +
* @param expression expression to concatenate this expression with.
* @return {patio.sql.StringExpression} the casted string expression
- -
*/
- -
concat:function (expression) {
- 0
+return new StringExpression("||", this, expression);
- +
castString:function (type) {
- 0
return this.cast(type || String).sqlString;
}
- -
}
- +
}).as(sql, "StringConcatenationMethods");
- +
}).as(sql, "CastMethods");
- -
/**
- -
* @class This mixin provides the ability to access elements within a SQL array.
- -
*
- -
* @example
- -
* sql.array.sqlSubscript(1) //=> array[1]
- -
* sql.array.sqlSubscript(1, 2) //=> array[1, 2]
- +
* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
- +
* @class Provides methods to assist in assigning a SQL type to
* particular types, i.e. Boolean, Function, Number or String.
- -
*
- +
* @name SubscriptMethods
* @name ComplexExpressionMethods
- +
* @memberOf patio.sql
- +
* @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.
- +
* @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.
- +
* @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.
- +
* <pre class="code">
- +
* sql.a.not("a") //=> NOT "a"
- +
* sql.a.sqlNumber.not() //=> ~"a"
- +
* </pre>
- +
* @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.
- +
* <pre class="code">
- +
* sql.a.plus(sql.b); //=> "a" + "b"
- +
* sql.a.sqlString.concat(sql.b) //=> "a" || "b"
* </pre>
- -
*/
- 1
+var SubscriptMethods = define(null, {
- 1
var ComplexExpressionMethods = define(null, {
- +
instance:{
- +
/**@ignore*/
getters:{
- -
- -
/**
- -
* Return a {@link patio.sql.Subscript} with the given arguments, representing an
- -
* SQL array access.
- -
*
- -
* @example
- -
* sql.array.sqlSubscript(1) //=> array[1]
- -
* sql.array.sqlSubscript(1, 2) //=> array[1, 2]
- -
* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
- -
*
- -
* @param subscript
- -
*/
- -
sqlSubscript:function (subscript) {
- 108
-var args = argsToArray(arguments);
- 108
+return new SubScript(this, flatten(args));
- +
/**
- +
* @ignore
- +
*/
- +
sqlBoolean:function () {
- 0
+return new BooleanExpression("noop", this);
- +
},
- +
- +
- +
/**
- +
* @ignore
- +
*/
- +
sqlFunction:function () {
- 13
+return new SQLFunction(this);
- +
},
- +
- +
- +
/**
- +
* @ignore
- +
*/
- +
sqlNumber:function () {
- 50
+return new NumericExpression("noop", this);
- +
},
- +
- +
/**
- +
* @ignore
- +
*/
- +
sqlString:function () {
- 0
+return new StringExpression("noop", this);
}
}
- -
}
- +
}).as(sql, "SubScriptMethods");
}).as(sql, "ComplexExpressionMethods");
- +
- 1
+var inequalityMethod = function (op) {
- 6
+return function (expression) {
- 88
+if (isInstanceOf(expression, BooleanExpression)
- +
|| isBoolean(expression)
- +
|| isNull(expression)
- +
|| (isHash(expression))
- +
|| isArray(expression)) {
- 0
+throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
- +
} else {
- 88
+return new BooleanExpression(op, this, expression);
- +
}
- +
}
};
- -
/**
- +
* @class This is the parent of all expressions.
- +
* @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
* used in a numeric or string context in SQL.
- -
*
- +
* @name Expression
- +
* @example
- +
* sql.a.gt("b") //=> a > "b"
- +
* sql.a.lt("b") //=> a > "b"
- +
* sql.a.gte("b") //=> a >= "b"
- +
* sql.a.lte("b") //=> a <= "b"
- +
* sql.a.eq("b") //=> a = "b"
- +
*
* @name InequalityMethods
* @memberOf patio.sql
- -
*/
- 1
-var Expression = define(null, {
- +
- 1
var InequalityMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.Expression.prototype*/
/**@lends patio.sql.InequalityMethods.prototype*/
- -
/**
- +
* Returns the string representation of this expression
- +
* @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.
* @example
- -
*
- -
* @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.
- +
* @return {String} a string literal version of this expression.
- +
* sql.a.gt("b") //=> a > "b"
- +
*
* @return {patio.sql.BooleanExpression}
- -
*/
- -
sqlLiteral:function (ds) {
- 0
-return this.toString(ds);
- -
}
- -
- -
},
- -
- -
static:{
- -
/**@lends patio.sql.Expression*/
- +
gt:inequalityMethod("gt"),
- -
/**
- +
* This is a helper method that will take in an array of arguments and return an expression.
* @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.
*
* @example
- -
*
- +
* QualifiedIdentifier.fromArgs(["table", "column"]);
* sql.a.gte("b") //=> a >= "b"
- -
*
- +
* @param {*[]} args array of arguments to pass into the constructor of the function.
- +
* @return {patio.sql.BooleanExpression}
- +
*/
- +
gte:inequalityMethod("gte"),
- +
/**
* @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.
- -
*
- +
* @return {patio.sql.Expression} an expression.
- +
* @example
- +
*
- +
* sql.a.lt("b") //=> a < "b"
- +
*
* @return {patio.sql.BooleanExpression}
- -
*/
- -
fromArgs:function (args) {
- 2410
-var ret;
- 2410
-try {
- 2410
-ret = new this();
- -
} catch (ignore) {
- -
}
- 2410
-this.apply(ret, args);
- 2410
-return ret;
- +
},
- +
lt:inequalityMethod("lt"),
- +
/**
- +
* @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.
- +
*
- +
* @example
- +
*
- +
* sql.a.lte("b") //=> a <= "b"
- +
*
- +
* @return {patio.sql.BooleanExpression}
- +
*/
- +
lte:inequalityMethod("lte"),
- +
/**
- +
* @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.
- +
*
- +
* @example
- +
*
- +
* sql.a.eq("b") //=> a = "b"
- +
*
- +
* @return {patio.sql.BooleanExpression}
- +
*/
- +
eq:inequalityMethod("eq"),
- +
neq:inequalityMethod("neq"),
- -
/**
- -
* Helper to determine if something is a condition specifier. Returns true if the object
- +
* is a Hash or is an array of two element arrays.
- +
* @private
- +
*
* Creates a boolean expression where the key is '>=' value 1 and '<=' value two.
*
- -
* @example
- -
* Expression.isConditionSpecifier({a : "b"}); //=> true
- -
* Expression.isConditionSpecifier("a"); //=> false
- -
* Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true
* Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false
- -
*
- -
* @param {*} obj object to test if it is a condition specifier
- +
* @return {Boolean} true if the object is a Hash or is an array of two element arrays.
- +
* sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))
- +
* sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))
- +
*
- +
* @param {Object} items a two element array where the first element it the item to be gte and the second item lte.
- +
*
* @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.
- -
*/
- -
isConditionSpecifier:function (obj) {
- 22715
-return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
- 9896
-return isArray(i) && i.length === 2;
- +
}));
- +
between:function (items) {
- 6
return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))
}
- -
}
- -
- +
}).as(sql, "Expression");
}).as(sql, "InequalityMethods");
- -
/**
- -
* @class Base class for all GenericExpressions
- -
*
- -
* @augments patio.sql.Expression
- -
* @augments patio.sql.AliasMethods
- -
* @augments patio.sql.BooleanMethods
- -
* @augments patio.sql.CastMethods
- -
* @augments patio.sql.ComplexExpressionMethods
- -
* @augments patio.sql.InequalityMethods
- -
* @augments patio.sql.NumericMethods
- -
* @augments patio.sql.OrderedMethods
- -
* @augments patio.sql.StringMethods
- +
* @augments patio.sql.SubscriptMethods
- +
* @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},
- +
* so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}
* or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>
- -
*
- +
* @name GenericExpression
* @name NoBooleanInputMethods
* @memberOf patio.sql
- -
*/
- 1
-var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");
- -
- -
- 1
-var AliasedExpression = define(Expression, {
- -
instance:{
- -
/**@lends patio.sql.AliasedExpression.prototype*/
- -
- -
/**
- -
* This class reperesents an Aliased Expression
- -
*
- -
* @constructs
- -
* @augments patio.sql.Expression
- -
*
- -
* @param expression the expression to alias.
- -
* @param alias the alias to alias the expression to.
- -
*
- -
* @property expression the expression being aliased
- -
* @property alias the alias of the expression
- -
*
- -
*/
- -
constructor:function (expression, alias) {
- 945
-this.expression = expression;
- 945
-this.alias = alias;
- +
},
- 1
+var NoBooleanInputMethods = define(null, {
- +
instance:{
- +
constructor:function (op) {
- 22
+var args = argsToArray(arguments, 1);
- 22
+args.forEach(function (expression) {
- 26
+if ((isInstanceOf(expression, BooleanExpression))
- +
|| isBoolean(expression)
- +
|| isNull(expression)
- +
|| (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))
- +
|| isArray(expression)) {
- 0
+throw new ExpressionError("Cannot apply " + op + " to a boolean expression");
- +
}
- +
});
- 22
+this._super(arguments);
- +
}
- +
}
}).as(sql, "NoBooleanInputMethods");
- -
- -
/**
- -
* Converts the aliased expression to a string
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- -
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- -
* @return String the SQL alias fragment.
- -
*/
- -
toString:function (ds) {
- 877
-!Dataset && (Dataset = require("./dataset"));
- 877
-ds = ds || new Dataset();
- 877
-return ds.aliasedExpressionSql(this);
- +
}
- 1
+var numericMethod = function (op) {
- 4
+return function (expression) {
- 12
+if (isInstanceOf(expression, BooleanExpression, StringExpression)) {
- 0
+throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");
- +
} else {
- 12
return new NumericExpression(op, this, expression);
}
- -
}
- +
).as(sql, "AliasedExpression");
};
- -
- 1
+var CaseExpression = define(GenericExpression, {
- +
/**
- +
* @class This mixin includes the standard mathematical methods (+, -, *, and /)
- +
* that are defined on objects that can be used in a numeric context in SQL.
- +
*
- +
* @example
- +
* sql.a.plus(sql.b) //=> "a" + "b"
- +
* sql.a.minus(sql.b) //=> "a" - "b"
- +
* sql.a.multiply(sql.b) //=> "a" * "b"
- +
* sql.a.divide(sql.b) //=> "a" / "b"
- +
*
- +
* @name NumericMethods
- +
* @memberOf patio.sql
- +
*/
- 1
var NumericMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.CaseExpression.prototype*/
- +
/**@lends patio.sql.NumericMethods.prototype*/
- -
/**
- -
* Create an object with the given conditions and
- -
* default value. An expression can be provided to
- -
* test each condition against, instead of having
- +
* all conditions represent their own boolean expression.
* @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @constructs
- -
* @augments patio.sql.GenericExpression
- -
* @param {Array|Object} conditions conditions to create the case expression from
- -
* @param def default value
- +
* @param expression expression to create the CASE expression from
* @example
- -
*
- -
* @property {Boolean} hasExpression returns true if this case expression has a expression
- -
* @property conditions the conditions of the {@link patio.sql.CaseExpression}.
- -
* @property def the default value of the {@link patio.sql.CaseExpression}.
- -
* @property expression the expression of the {@link patio.sql.CaseExpression}.
- +
* @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.
- +
* sql.a.plus(sql.b) //=> "a" + "b"
- +
*
* @return {patio.sql.NumericExpression}
- -
*/
- -
constructor:function (conditions, def, expression) {
- 8
-if (Expression.isConditionSpecifier(conditions)) {
- 4
-this.conditions = toArray(conditions);
- 4
-this.def = def;
- 4
-this.expression = expression;
- 4
-this.noExpression = isUndefined(expression);
- -
}
- +
},
plus:numericMethod("plus"),
- -
/**
- -
* Converts the case expression to a string
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
* @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @return String the SQL case expression fragment.
- -
*/
- -
toString:function (ds) {
- 2
-!Dataset && (Dataset = require("./dataset"));
- 2
-ds = ds || new Dataset();
- 2
-return ds.caseExpressionSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
/**@ignore*/
- -
hasExpression:function () {
- 2
-return !this.noExpression;
- -
}
- -
}
- -
}
- -
}).as(sql, "CaseExpression");
- -
- -
- 1
-var Cast = define(GenericExpression, {
- -
instance:{
- +
/**@lends patio.sql.Cast*/
- +
* @example
- +
*
- +
* sql.a.minus(sql.b) //=> "a" - "b"
- +
*
- +
* @return {patio.sql.NumericExpression}
- +
*/
minus:numericMethod("minus"),
- -
/**
- -
* Represents a cast of an SQL expression to a specific type.
- -
* @constructs
- +
* @augments patio.sql.GenericExpression
* @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @param expr the expression to CAST.
- +
* @param type the type to CAST the expression to.
* @example
- -
*
- -
* @property expr the expression to CAST.
- +
* @property type the type to CAST the expression to.
- +
* sql.a.divide(sql.b) //=> "a" / "b"
- +
*
* @return {patio.sql.NumericExpression}
- -
*/
- -
constructor:function (expr, type) {
- 3
-this.expr = expr;
- 3
-this.type = type;
- +
},
divide:numericMethod("divide"),
- -
/**
- +
* Converts the cast expression to a string
* @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
* @example
- -
*
- +
* @return String the SQL cast expression fragment.
- +
* sql.a.multiply(sql.b) //=> "a" * "b"
- +
*
* @return {patio.sql.NumericExpression}
- -
*/
- -
toString:function (ds) {
- 2
-!Dataset && (Dataset = require("./dataset"));
- 2
-ds = ds || new Dataset();
- 2
-return ds.castSql(this.expr, this.type);
- +
}
multiply:numericMethod("multiply")
- -
}
- +
}).as(sql, "Cast");
}).as(sql, "NumericMethods");
- -
- 1
+var ColumnAll = define(Expression, {
- +
/**
- +
* @class This mixin provides ordering methods ("asc", "desc") to expression.
- +
*
- +
* @example
- +
*
- +
* sql.name.asc(); //=> name ASC
- +
* sql.price.desc(); //=> price DESC
- +
* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
- +
* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
- +
*
- +
* @name OrderedMethods
- +
* @memberOf patio.sql
- +
*/
- 1
var OrderedMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.ColumnAll.prototype*/
/**@lends patio.sql.OrderedMethods.prototype*/
- -
/**
- -
* Represents all columns in a given table, table.* in SQL
- -
* @constructs
- -
*
- +
* @augments patio.sql.Expression
* Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
- -
*
- +
* @param table the table this expression is for.
- +
* @example
- +
* sql.name.asc(); //=> name ASC
* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST
- -
*
- +
* @property table the table this all column expression represents.
- +
* @param {Object} [options] options to use when sorting
- +
* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
- +
* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
* @return {patio.sql.OrderedExpression}
- -
*/
- -
constructor:function (table) {
- 20
+this.table = table;
- +
asc:function (options) {
- 7
return new OrderedExpression(this, false, options);
},
- -
/**
- +
* Converts the ColumnAll expression to a string
- +
* Mark the receiving SQL column as sorting in a descending fashion.
* @example
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* sql.price.desc(); //=> price DESC
* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST
- -
*
- +
* @return String the SQL columnAll expression fragment.
- +
* @param {Object} [options] options to use when sorting
- +
* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered
- +
* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).
* @return {patio.sql.OrderedExpression}
- -
*/
- -
toString:function (ds) {
- 19
-!Dataset && (Dataset = require("./dataset"));
- 19
-ds = ds || new Dataset();
- 19
+return ds.columnAllSql(this);
- +
desc:function (options) {
- 26
return new OrderedExpression(this, true, options);
}
- -
}
- +
}).as(sql, "ColumnAll");
}).as(sql, "OrderedMethods");
- -
- 1
+var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {
- +
- +
/**
- +
* @class This mixin provides methods related to qualifying expression.
- +
*
- +
* @example
- +
*
- +
* sql.column.qualify("table") //=> "table"."column"
- +
* sql.table.qualify("schema") //=> "schema"."table"
- +
* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
- +
*
- +
* @name QualifyingMethods
- +
* @memberOf patio.sql
- +
*/
- 1
var QualifyingMethods = define(null, {
- -
instance:{
- +
/**@lends patio.sql.ComplexExpression.prototype*/
/**@lends patio.sql.QualifyingMethods.prototype*/
- -
/**
- -
* Represents a complex SQL expression, with a given operator and one
- -
* or more attributes (which may also be ComplexExpressions, forming
- -
* a tree).
- -
*
- -
* This is an abstract class that is not that useful by itself. The
- -
* subclasses @link patio.sql.BooleanExpression},
- -
* {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should
- +
* be used instead of this class directly.
* Qualify the receiver with the given qualifier (table for column/schema for table).
- -
*
- -
* @constructs
- -
* @augments patio.sql.Expression
- -
* @augments patio.sql.AliasMethods
- -
* @augments patio.sql.CastMethods
- -
* @augments patio.sql.OrderedMethods
- +
* @augments patio.sql.SubscriptMethods
- +
* @example
- +
* sql.column.qualify("table") //=> "table"."column"
- +
* sql.table.qualify("schema") //=> "schema"."table"
* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"
- -
*
- -
* @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.
- +
* @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.
* @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.
- -
*
- -
* @param {...} op The operator and arguments for this object to the ones given.
- -
* <p>
- -
* Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,
- -
* other than the second arg for an IN/NOT IN operator.</li>
- +
* </p>
* @return {patio.sql.QualifiedIdentifier}
- -
*/
- -
constructor:function (op) {
- 7952
-if (op) {
- 7164
-var args = argsToArray(arguments,1 );
- -
//make a copy of the args
- 7164
-var origArgs = args.slice(0);
- 7164
-args.forEach(function (a, i) {
- 14676
-if (Expression.isConditionSpecifier(a)) {
- 6
-args[i] = BooleanExpression.fromValuePairs(a);
- -
}
- -
});
- 7164
-op = op.toUpperCase();
- -
- 7164
-if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
- 1164
-if (args.length < 1) {
- 0
-throw new ExpressionError("The " + op + " operator requires at least 1 argument")
- -
}
- 1164
-var oldArgs = args.slice(0);
- 1164
-args = [];
- 1164
-oldArgs.forEach(function (a) {
- 2739
-a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
- -
});
- -
- 6000
-} else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
- 5937
-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.
- 5937
-if (IN_OPERATORS[op]) {
- 23
-args[1] = origArgs[1]
- -
}
- 63
-} else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
- 63
-if (args.length != 1) {
- 0
-throw new ExpressionError("The " + op + " operator requires only one argument");
- -
}
- -
} else {
- 0
-throw new ExpressionError("Invalid operator " + op);
- -
}
- 7164
-this.op = op;
- 7164
-this.args = args;
- +
}
- +
qualify:function (qualifier) {
- 576
return new QualifiedIdentifier(qualifier, this);
},
- -
/**
- +
* Converts the ComplexExpression to a string.
* Use to create a .* expression.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* @example
- +
* sql.table.all() //=> "table".*
* sql.table.qualify("schema").all() //=> "schema"."table".*
- -
*
- +
* @return String the SQL version of the {@link patio.sql.ComplexExpression}.
- +
*
* @return {patio.sql.ColumnAll}
- -
*/
- -
toString:function (ds) {
- 6761
-!Dataset && (Dataset = require("./dataset"));
- 6761
-ds = ds || new Dataset();
- 6761
+return ds.complexExpressionSql(this.op, this.args);
- +
all:function () {
- 3
return new ColumnAll(this);
- -
}
},
- -
- -
static:{
/**@lends patio.sql.ComplexExpression*/
- -
- -
/**
- -
* Hash of operator inversions
- -
* @type Object
- -
* @default {
- -
* AND:"OR",
- -
* OR:"AND",
- -
* GT:"lte",
- -
* GTE:"lt",
- -
* LT:"gte",
- -
* LTE:"gt",
- -
* EQ:"neq",
- -
* NEQ:"eq",
- -
* LIKE:'NOT LIKE',
- -
* "NOT LIKE":"LIKE",
- -
* '!~*':'~*',
- -
* '~*':'!~*',
- -
* "~":'!~',
- -
* "IN":'NOTIN',
- -
* "NOTIN":"IN",
- -
* "IS":'IS NOT',
- -
* "ISNOT":"IS",
- -
* NOT:"NOOP",
- -
* NOOP:"NOT",
- -
* ILIKE:'NOT ILIKE',
- -
* NOTILIKE:"ILIKE"
- -
* }
- -
*/
- +
OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,
- +
}
- +
}).as(sql, "QualifyingMethods");
- +
- +
- +
/**
- +
* @class This mixin provides SQL string methods such as (like and iLike).
- +
*
- +
* @example
- +
*
- +
* sql.a.like("A%"); //=> "a" LIKE 'A%'
- +
* sql.a.iLike("A%"); //=> "a" LIKE 'A%'
- +
* sql.a.like(/^a/); //=> "a" ~* '^a'
- +
*
- +
* @name StringMethods
- +
* @memberOf patio.sql
- +
*/
- 1
+var StringMethods = define(null, {
- +
instance:{
/**@lends patio.sql.StringMethods.prototype*/
- -
/**
- +
* Default mathematical operators.
- +
* Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver
- +
* with the given patterns. See {@link patio.sql.StringExpression#like}.
- +
*
- +
* @example
* sql.a.iLike("A%"); //=> "a" LIKE 'A%'
- -
*
- -
* @type Object
- +
* @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}
* @return {patio.sql.BooleanExpression}
- -
*/
- +
MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,
- +
ilike:function (expression) {
- 310
+expression = argsToArray(arguments);
- 310
+return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([
- +
{caseInsensitive:true}
- +
]));
},
- -
/**
- +
* Default bitwise operators.
- +
* Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with
* the given patterns. See {@link patio.sql.StringExpression#like}.
- -
*
- -
* @type Object
- -
* @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}
- -
*/
- -
BITWISE_OPERATORS:BITWISE_OPERATORS,
- -
/**
- +
* Default inequality operators.
- +
* @example
- +
* sql.a.like(/^a/); //=> "a" ~* '^a'
* sql.a.like("A%"); //=> "a" LIKE 'A%'
- -
*
- -
* @type Object
- +
* @default {GT:">",GTE:">=",LT:"<",LTE:"<="}
* @param expression
- -
*/
- +
INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,
- +
like:function (expression) {
- 11
+expression = argsToArray(arguments);
- 11
+return StringExpression.like.apply(StringExpression, [this].concat(expression));
- +
}
- +
}
- +
}).as(sql, "StringMethods");
- +
- +
/**
- +
* @class This mixin provides string concatenation methods ("concat");
- +
*
- +
* @example
- +
*
- +
* sql.x.sqlString.concat("y"); //=> "x" || "y"
- +
*
- +
* @name StringConcatenationMethods
- +
* @memberOf patio.sql
- +
*/
- 1
+var StringConcatenationMethods = define(null, {
- +
instance:{
/**@lends patio.sql.StringConcatenationMethods.prototype*/
- -
/**
- +
* Default boolean operators.
- +
* Return a {@link patio.sql.StringExpression} representing the concatenation of this expression
* with the given argument.
- -
*
- -
* @type Object
- +
* @default {AND:"AND",OR:"OR"}
- +
* @example
- +
*
- +
* sql.x.sqlString.concat("y"); //=> "x" || "y"
- +
*
* @param expression expression to concatenate this expression with.
- -
*/
- +
BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,
- +
concat:function (expression) {
- 0
+return new StringExpression("||", this, expression);
- +
}
- +
}
- +
}).as(sql, "StringConcatenationMethods");
- +
- +
/**
- +
* @class This mixin provides the ability to access elements within a SQL array.
- +
*
- +
* @example
- +
* sql.array.sqlSubscript(1) //=> array[1]
- +
* sql.array.sqlSubscript(1, 2) //=> array[1, 2]
- +
* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
- +
*
- +
* @name SubscriptMethods
- +
* @memberOf patio.sql
- +
*/
- 1
+var SubscriptMethods = define(null, {
instance:{
- -
/**
- +
* Default IN operators.
- +
* Return a {@link patio.sql.Subscript} with the given arguments, representing an
* SQL array access.
- -
*
- -
* @type Object
- -
* @default {IN:"IN",NOTIN:'NOT IN'}
- -
*/
- -
IN_OPERATORS:IN_OPERATORS,
- -
/**
- +
* Default IS operators.
- +
* @example
- +
* sql.array.sqlSubscript(1) //=> array[1]
- +
* sql.array.sqlSubscript(1, 2) //=> array[1, 2]
* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]
- -
*
- -
* @type Object
- +
* @default {IS:"IS", ISNOT:'IS NOT'}
* @param subscript
- -
*/
- +
IS_OPERATORS:IS_OPERATORS,
- +
sqlSubscript:function (subscript) {
- 108
+var args = argsToArray(arguments);
- 108
+return new SubScript(this, flatten(args));
- +
}
- +
}
- +
}).as(sql, "SubScriptMethods");
- +
- +
- +
/**
- +
* @class This is the parent of all expressions.
- +
*
- +
* @name Expression
- +
* @memberOf patio.sql
- +
*/
- 1
+var Expression = define(null, {
- +
- +
instance:{
- +
/**@lends patio.sql.Expression.prototype*/
- -
/**
- +
* Default two arity operators.
* Returns the string representation of this expression
- -
*
- -
* @type Object
- -
* @default {
- -
* EQ:'=',
- -
* NEQ:'!=', LIKE:"LIKE",
- -
* "NOT LIKE":'NOT LIKE',
- -
* ILIKE:"ILIKE",
- -
* "NOT ILIKE":'NOT ILIKE',
- -
* "~":"~",
- -
* '!~':"!~",
- -
* '~*':"~*",
- -
* '!~*':"!~*",
- -
* GT:">",
- -
* GTE:">=",
- -
* LT:"<",
- -
* LTE:"<=",
- -
* bitWiseAnd:"&",
- -
* bitWiseOr:"|",
- -
* exclusiveOr:"^",
- -
* leftShift:"<<",
- -
* rightShift:">>",
- -
* IS:"IS",
- -
* ISNOT:'IS NOT',
- -
* IN:"IN",
- -
* NOTIN:'NOT IN'
- +
* }
- +
* @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.
* @return {String} a string literal version of this expression.
- -
*/
- +
TWO_ARITY_OPERATORS:TWO_ARITY_OPERATORS,
- +
sqlLiteral:function (ds) {
- 0
+return this.toString(ds);
- +
}
- +
- +
},
- +
- +
static:{
/**@lends patio.sql.Expression*/
- -
/**
- +
* Default N(multi) arity operators.
* This is a helper method that will take in an array of arguments and return an expression.
- -
*
- -
* @type Object
- -
* @default {
- -
* "||":"||",
- -
* AND:"AND",
- -
* OR:"OR",
- -
* PLUS:"+",
- -
* MINUS:"-",
- -
* DIVIDE:"/", MULTIPLY:"*"
- +
* }
- +
* @example
- +
*
- +
* QualifiedIdentifier.fromArgs(["table", "column"]);
- +
*
- +
* @param {*[]} args array of arguments to pass into the constructor of the function.
- +
*
* @return {patio.sql.Expression} an expression.
- -
*/
- +
N_ARITY_OPERATORS:N_ARITY_OPERATORS,
- +
fromArgs:function (args) {
- 2216
+var ret;
- 2216
+try {
- 2216
+ret = new this();
- +
} catch (ignore) {
- +
}
- 2216
+this.apply(ret, args);
- 2216
+return ret;
},
- -
/**
- +
* Default ONE operators.
- +
* Helper to determine if something is a condition specifier. Returns true if the object
* is a Hash or is an array of two element arrays.
- -
*
- -
* @type Object
- -
* @default {
- -
* "NOT":"NOT",
- -
* "NOOP":"NOOP"
- +
* }
- +
* @example
- +
* Expression.isConditionSpecifier({a : "b"}); //=> true
- +
* Expression.isConditionSpecifier("a"); //=> false
- +
* Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true
- +
* Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false
- +
*
- +
* @param {*} obj object to test if it is a condition specifier
* @return {Boolean} true if the object is a Hash or is an array of two element arrays.
- -
*/
- +
ONE_ARITY_OPERATORS:ONE_ARITY_OPERATORS
- +
isConditionSpecifier:function (obj) {
- 22715
+return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {
- 9896
+return isArray(i) && i.length === 2;
- +
}));
}
- -
}
- +
}).as(sql, "ComplexExpression");
- +
- +
}).as(sql, "Expression");
- +
- +
/**
- +
* @class Base class for all GenericExpressions
- +
*
- +
* @augments patio.sql.Expression
- +
* @augments patio.sql.AliasMethods
- +
* @augments patio.sql.BooleanMethods
- +
* @augments patio.sql.CastMethods
- +
* @augments patio.sql.ComplexExpressionMethods
- +
* @augments patio.sql.InequalityMethods
- +
* @augments patio.sql.NumericMethods
- +
* @augments patio.sql.OrderedMethods
- +
* @augments patio.sql.StringMethods
- +
* @augments patio.sql.SubscriptMethods
- +
*
- +
* @name GenericExpression
- +
* @memberOf patio.sql
- +
*/
- 1
+var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");
- +
- +
- 1
+var AliasedExpression = define(Expression, {
- +
instance:{
- +
/**@lends patio.sql.AliasedExpression.prototype*/
- +
- +
/**
- +
* This class reperesents an Aliased Expression
- +
*
- +
* @constructs
- +
* @augments patio.sql.Expression
- +
*
- +
* @param expression the expression to alias.
- +
* @param alias the alias to alias the expression to.
- +
*
- +
* @property expression the expression being aliased
- +
* @property alias the alias of the expression
- +
*
- +
*/
- +
constructor:function (expression, alias) {
- 945
+this.expression = expression;
- 945
+this.alias = alias;
- +
},
- +
- +
/**
- +
* Converts the aliased expression to a string
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL alias fragment.
- +
*/
- +
toString:function (ds) {
- 877
+!Dataset && (Dataset = require("./dataset"));
- 877
+ds = ds || new Dataset();
- 877
+return ds.aliasedExpressionSql(this);
- +
}
- +
}
- +
}
).as(sql, "AliasedExpression");
- -
- -
/**
- -
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- -
* in a boolean value in SQL.
- -
*
- -
* @augments patio.sql.ComplexExpression
- -
* @augments patio.sql.BooleanMethods
- -
* @name BooleanExpression
- -
* @memberOf patio.sql
- -
*/
- 1
-var BooleanExpression = define([ComplexExpression, BooleanMethods], {
- -
static:{
- +
/**@lends patio.sql.BooleanExpression*/
- 1
+var CaseExpression = define(GenericExpression, {
- +
instance:{
/**@lends patio.sql.CaseExpression.prototype*/
- -
/**
- -
* Invert the expression, if possible. If the expression cannot
- -
* be inverted, it throws an {@link patio.error.ExpressionError}. An inverted expression should match
- -
* everything that the uninverted expression did not match, and vice-versa, except for possible issues with
- -
* SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
- -
*
- -
* @example
- +
* BooleanExpression.invert(sql.a) //=> NOT "a"
- +
* Create an object with the given conditions and
- +
* default value. An expression can be provided to
- +
* test each condition against, instead of having
* all conditions represent their own boolean expression.
- -
*
- -
* @param {patio.sql.BooleanExpression} expression
- +
* the expression to invert.
- +
* @constructs
- +
* @augments patio.sql.GenericExpression
- +
* @param {Array|Object} conditions conditions to create the case expression from
- +
* @param def default value
* @param expression expression to create the CASE expression from
- -
*
- +
* @return {patio.sql.BooleanExpression} the inverted expression.
- +
* @property {Boolean} hasExpression returns true if this case expression has a expression
- +
* @property conditions the conditions of the {@link patio.sql.CaseExpression}.
- +
* @property def the default value of the {@link patio.sql.CaseExpression}.
- +
* @property expression the expression of the {@link patio.sql.CaseExpression}.
* @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.
- -
*/
- -
invert:function (expression) {
- 95
-if (isInstanceOf(expression, BooleanExpression)) {
- 90
-var op = expression.op, newArgs;
- 90
-if (op == "AND" || op == "OR") {
- 3
-newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args.map(function (arg) {
- 6
-return BooleanExpression.invert(arg);
- -
}));
- 3
-return BooleanExpression.fromArgs(newArgs);
- -
} else {
- 87
-newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);
- 87
-return BooleanExpression.fromArgs(newArgs);
- -
}
- 5
-} else if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
- 0
-throw new ExpressionError(format("Cannot invert %4j", [expression]));
- -
} else {
- 5
+return new BooleanExpression("NOT", expression);
- +
constructor:function (conditions, def, expression) {
- 8
+if (Expression.isConditionSpecifier(conditions)) {
- 4
+this.conditions = toArray(conditions);
- 4
+this.def = def;
- 4
+this.expression = expression;
- 4
this.noExpression = isUndefined(expression);
}
- +
},
- -
/**
- -
* Take pairs of values (e.g. a hash or array of two element arrays)
- -
* and converts it to a {@link patio.sql.BooleanExpression}. The operator and args
- +
* used depends on the case of the right (2nd) argument:
* Converts the case expression to a string
- -
*
- -
* <pre class='code'>
- -
* BooleanExpression.fromValuePairs({a : [1,2,3]}) //=> a IN (1,2,3)
- -
* BooleanExpression.fromValuePairs({a : true}); // a IS TRUE;
- -
* BooleanExpression.fromValuePairs({a : /^A/i}); // a *~ '^A'
- +
* </pre>
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- -
* If multiple arguments are given, they are joined with the op given (AND
- -
* by default, OR possible). If negate is set to true,
- -
* all subexpressions are inverted before used.
- -
* <pre class='code'>
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}) //=> a IN (1,2,3) AND b IS TRUE
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
- -
* </pre>
- -
* @param {Object} a object to convert to a {@link patio.sql.BooleanExpression}
- -
* @param {String} [op="AND"] Boolean operator to join each subexpression with.
- -
* <pre class="code">
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
- -
* </pre>
- -
* @param {Boolean} [negate=false] set to try to invert the {@link patio.sql.BooleanExpression}.
- -
* <pre class="code">
- -
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
- -
* </pre>
- +
* @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
* @return String the SQL case expression fragment.
- -
*/
- -
fromValuePairs:function (a, op, negate) {
- 7610
-!Dataset && (Dataset = require("./dataset"));
- 7610
-op = op || "AND", negate = negate || false;
- 7610
-var pairArr = [];
- 7610
-var isArr = isArray(a) && Expression.isConditionSpecifier(a);
- 7610
-if (isHash(a)) {
- 3385
-pairArr.push(this.__filterObject(a));
- -
} else {
- 4225
-for (var k in a) {
- 5203
-var v = isArr ? a[k][1] : a[k], ret;
- 5203
-k = isArr ? a[k][0] : k;
- 5203
-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);
- 5186
-} else if (isInstanceOf(v, NegativeBooleanConstant)) {
- 0
-ret = new BooleanExpression("ISNOT", k, v.constant);
- 5186
-} else if (isInstanceOf(v, BooleanConstant)) {
- 0
-ret = new BooleanExpression("IS", k, v.constant);
- 5186
-} else if (isNull(v) || isBoolean(v)) {
- 240
-ret = new BooleanExpression("IS", k, v);
- 4946
-} else if (isHash(v)) {
- 0
-ret = BooleanExpression.__filterObject(v, k);
- 4946
-} else if (isRegExp(v)) {
- 69
-ret = StringExpression.like(sql.stringToIdentifier(k), v);
- -
} else {
- 4877
-ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
- -
}
- 5203
-negate && (ret = BooleanExpression.invert(ret));
- 5203
-pairArr.push(ret);
- -
}
- -
}
- -
//if We just have one then return the first otherwise create a new Boolean expression
- 7610
+return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
- +
toString:function (ds) {
- 2
+!Dataset && (Dataset = require("./dataset"));
- 2
+ds = ds || new Dataset();
- 2
return ds.caseExpressionSql(this);
},
- +
- +
/**@ignore*/
- +
getters:{
- +
/**@ignore*/
- +
hasExpression:function () {
- 2
+return !this.noExpression;
- +
}
- +
}
- +
}
- +
}).as(sql, "CaseExpression");
- +
- +
- 1
+var Cast = define(GenericExpression, {
- +
instance:{
- +
/**@lends patio.sql.Cast*/
- -
/**
- -
* @private
- -
*
- +
* This builds an expression from a hash
- +
* Represents a cast of an SQL expression to a specific type.
- +
* @constructs
* @augments patio.sql.GenericExpression
- -
*
- +
* @example
- +
* @param expr the expression to CAST.
* @param type the type to CAST the expression to.
- -
*
- -
* Dataset._filterObject({a : 1}) //=> WHERE (a = 1)
- -
* Dataset._filterObject({x : {gt : 1}}) //=> WHERE (x > 1)
- -
* Dataset._filterObject({x : {gt : 1}, a : 1}) //=> WHERE ((x > 1) AND (a = 1))
- -
* Dataset._filterObject({x : {like : "name"}}) //=> WHERE (x LIKE 'name')
- -
* Dataset._filterObject({x : {iLike : "name"}}) //=> WHERE (x LIKE 'name')
- -
* Dataset._filterObject({x : {between : [1,10]}}) //=> WHERE ((x >= 1) AND (x <= 10))
- -
* Dataset._filterObject({x : {notBetween : [1,10]}}) //=> WHERE ((x < 1) OR (x > 10))
- +
* Dataset._filterObject({x : {neq : 1}}) //=> WHERE (x != 1)
- +
* @property expr the expression to CAST.
- +
* @property type the type to CAST the expression to.
- +
*/
- +
constructor:function (expr, type) {
- 3
+this.expr = expr;
- 3
+this.type = type;
- +
},
- +
- +
/**
* Converts the cast expression to a string
- -
*
- -
* @param {Object} expr the expression we need to create an expression out of
- +
* @param {String} [key=null] the key that the hash corresponds to
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return {patio.sql.Expression} an expression to use in the filter
* @return String the SQL cast expression fragment.
- -
*/
- -
__filterObject:function (expr, key) {
- 3502
-var pairs = [], opts, newKey;
- 3502
-var twoArityOperators = this.TWO_ARITY_OPERATORS;
- 3502
-for (var k in expr) {
- 3541
-var v = expr[k];
- 3541
-if (isHash(v)) { //its a hash too filter it too!
- 115
-pairs.push(this.__filterObject(v, k));
- 3426
-} else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
- -
//its a two arrity operator (e.g. '=', '>')
- 118
-newKey = isString(key) ? key.split(",") : [key];
- 118
-if (newKey.length > 1) {
- -
//this represents a hash where the key represents two columns
- -
//(e.g. {"col1,col2" : 1}) => WHERE (col1 = 1 AND col2 = 1)
- 1
-pairs = pairs.concat(newKey.map(function (k) {
- -
//filter each column with the expression
- 2
-return this.__filterObject(expr, k);
- -
}, this));
- -
} else {
- 117
-newKey = [sql.stringToIdentifier(newKey[0])];
- 117
-if (k.match(/^like$/)) {
- -
//its a like clause {col : {like : "hello"}}
- -
- 3
-pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));
- 114
-} else if (k.match(/^iLike$/)) {
- -
//its a like clause {col : {iLike : "hello"}}
- 2
-pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]).concat({caseInsensitive:true}))));
- 112
-} else if (k.match(/between/i)) {
- -
//its a like clause {col : {between : [1,10]}}
- 6
-var between = sql.stringToIdentifier(newKey[0]).between(v);
- 6
-k == "notBetween" && (between = between.not());
- 6
-pairs.push(between);
- -
} else {
- -
//otherwise is just a boolean expressio
- -
//it its not a valid operator then we
- -
//BooleanExpression with throw an error
- 106
-pairs.push(new BooleanExpression(k, newKey[0], v));
- -
}
- -
}
- -
} else {
- -
//we're not a twoarity operator
- -
//so we create a boolean expression out of it
- 3308
-newKey = k.split(",");
- 3308
-if (newKey.length == 1) {
- 3302
-newKey = sql.stringToIdentifier(newKey[0]);
- -
}
- 3308
-opts = [
- -
[newKey, v]
- -
];
- 3308
-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
- 3502
+return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
- +
toString:function (ds) {
- 2
+!Dataset && (Dataset = require("./dataset"));
- 2
+ds = ds || new Dataset();
- 2
return ds.castSql(this.expr, this.type);
}
- -
}
- +
}).as(sql, "BooleanExpression");
}).as(sql, "Cast");
- -
- 1
+var Constant = define(GenericExpression, {
- 1
var ColumnAll = define(Expression, {
- -
instance:{
- +
/**@lends patio.sql.Constant.prototype*/
- +
/**@lends patio.sql.ColumnAll.prototype*/
- -
/**
- -
* Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.
- +
*
* Represents all columns in a given table, table.* in SQL
- -
* @constructs
- -
* @augments patio.sql.GenericExpression
- +
* @property {String} constant <b>READ ONLY</b> the contant.
- +
*
- +
* @augments patio.sql.Expression
- +
*
- +
* @param table the table this expression is for.
- +
*
* @property table the table this all column expression represents.
- -
*/
- -
constructor:function (constant) {
- 18
+this.__constant = constant;
- +
constructor:function (table) {
- 20
this.table = table;
},
- -
/**
- +
* Converts the {@link patio.sql.Constant} to a string.
* Converts the ColumnAll expression to a string
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.Constant}.
* @return String the SQL columnAll expression fragment.
*/
- -
toString:function (ds) {
- 6
-!Dataset && (Dataset = require("./dataset"));
- 6
-ds = ds || new Dataset();
- 6
-return ds.constantSql(this.__constant);
- -
},
- -
- -
getters:{
- -
constant:function () {
- 0
-return this.__constant;
- +
}
- 19
+!Dataset && (Dataset = require("./dataset"));
- 19
+ds = ds || new Dataset();
- 19
return ds.columnAllSql(this);
}
- -
}
- +
}).as(sql, "Constant");
}).as(sql, "ColumnAll");
- -
- -
/**
- -
* @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
- -
* @auments patio.sql.Constant
- -
* @name BooleanConstant
- -
* @memberOf patio.sql
- -
*/
- 1
+var BooleanConstant = define(Constant, {
- 1
var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {
- -
instance:{
- +
/**@lends patio.sql.BooleanConstant.prototype*/
/**@lends patio.sql.ComplexExpression.prototype*/
- -
/**
- +
* Converts the {@link patio.sql.BooleanConstant} to a string.
- +
* Represents a complex SQL expression, with a given operator and one
- +
* or more attributes (which may also be ComplexExpressions, forming
* a tree).
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* This is an abstract class that is not that useful by itself. The
- +
* subclasses @link patio.sql.BooleanExpression},
- +
* {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should
* be used instead of this class directly.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.BooleanConstant}.
- +
* @constructs
- +
* @augments patio.sql.Expression
- +
* @augments patio.sql.AliasMethods
- +
* @augments patio.sql.CastMethods
- +
* @augments patio.sql.OrderedMethods
- +
* @augments patio.sql.SubscriptMethods
- +
*
- +
* @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.
- +
* @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.
- +
*
- +
* @param {...} op The operator and arguments for this object to the ones given.
- +
* <p>
- +
* Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,
- +
* other than the second arg for an IN/NOT IN operator.</li>
* </p>
- -
*/
- -
toString:function (ds) {
- 10
-!Dataset && (Dataset = require("./dataset"));
- 10
-ds = ds || new Dataset();
- 10
-return ds.booleanConstantSql(this.__constant);
- -
}
- -
}
- +
}).as(sql, "BooleanConstant");
- +
constructor:function (op) {
- 7952
+if (op) {
- 7164
+var args = argsToArray(arguments,1 );
- +
//make a copy of the args
- 7164
+var origArgs = args.slice(0);
- 7164
+args.forEach(function (a, i) {
- 14676
+if (Expression.isConditionSpecifier(a)) {
- 6
+args[i] = BooleanExpression.fromValuePairs(a);
- +
}
- +
});
- 7164
op = op.toUpperCase();
- -
- -
/**
- -
* Represents inverse boolean constants (currently only NOTNULL). A
- -
* special class to allow for special behavior.
- -
*
- -
* @augments patio.sql.BooleanConstant
- -
* @name NegativeBooleanConstant
- -
* @memberOf patio.sql
- -
*/
- 1
-var NegativeBooleanConstant = define(BooleanConstant, {
- -
instance:{
- +
/**@lends patio.sql.NegativeBooleanConstant.prototype*/
- 7164
+if (N_ARITY_OPERATORS.hasOwnProperty(op)) {
- 1164
+if (args.length < 1) {
- 0
+throw new ExpressionError("The " + op + " operator requires at least 1 argument")
- +
}
- 1164
+var oldArgs = args.slice(0);
- 1164
+args = [];
- 1164
+oldArgs.forEach(function (a) {
- 2739
+a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);
- +
});
- +
- 6000
+} else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {
- 5937
+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.
- 5937
+if (IN_OPERATORS[op]) {
- 23
+args[1] = origArgs[1]
- +
}
- 63
+} else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {
- 63
+if (args.length != 1) {
- 0
+throw new ExpressionError("The " + op + " operator requires only one argument");
- +
}
- +
} else {
- 0
+throw new ExpressionError("Invalid operator " + op);
- +
}
- 7164
+this.op = op;
- 7164
+this.args = args;
- +
}
},
- -
/**
- +
* Converts the {@link patio.sql.NegativeBooleanConstant} to a string.
* Converts the ComplexExpression to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.NegativeBooleanConstant}.
* @return String the SQL version of the {@link patio.sql.ComplexExpression}.
*/
- -
toString:function (ds) {
- 2
-!Dataset && (Dataset = require("./dataset"));
- 2
-ds = ds || new Dataset();
- 2
+return ds.negativeBooleanConstantSql(this.__constant);
- 6761
+!Dataset && (Dataset = require("./dataset"));
- 6761
+ds = ds || new Dataset();
- 6761
return ds.complexExpressionSql(this.op, this.args);
- -
}
- -
}
- -
}).as(sql, "NegativeBooleanConstant");
- -
- -
/**
- -
* @namespace Holds default generic constants that can be referenced. These
- -
* are included in {@link patio}
- -
* @name Constants
- -
* @memberOf patio.sql
- -
*/
- 1
-sql.Constants = {
- -
/**@lends patio.sql.Constants*/
- -
- -
/**
- -
* Constant for CURRENT DATE
- -
* @type patio.sql.Constant
- -
*/
- -
CURRENT_DATE:new Constant("CURRENT_DATE"),
- -
- -
/**
- -
* Constant for CURRENT TIME
- -
* @type patio.sql.Constant
- -
*/
- -
CURRENT_TIME:new Constant("CURRENT_TIME"),
- -
- -
/**
- -
* Constant for CURRENT TIMESTAMP
- -
* @type patio.sql.Constant
- -
*/
- -
CURRENT_TIMESTAMP:new Constant("CURRENT_TIMESTAMP"),
- -
- -
/**
- -
* Constant for TRUE
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
SQLTRUE:new BooleanConstant(1),
- -
- -
/**
- -
* Constant for TRUE
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
TRUE:new BooleanConstant(1),
- -
- -
/**
- -
* Constant for FALSE.
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
SQLFALSE:new BooleanConstant(0),
- -
- -
/**
- -
* Constant for FALSE
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
FALSE:new BooleanConstant(0),
- -
/**
- -
* Constant for NULL
- -
* @type patio.sql.BooleanConstant
- -
*/
- -
NULL:new BooleanConstant(null),
- -
- -
/**
- -
* Constant for NOT NULL
- -
* @type patio.sql.NegativeBooleanConstant
- -
*/
- -
NOTNULL:new NegativeBooleanConstant(null)
- -
- +
};
},
- -
- 1
+var Constants = sql.Constants
- +
static:{
/**@lends patio.sql.ComplexExpression*/
- -
- 1
-var Identifier = define([GenericExpression, QualifyingMethods], {
- -
instance:{
- +
/**@lends patio.sql.Identifier.prototype*/
- +
/**
- +
* Hash of operator inversions
- +
* @type Object
- +
* @default {
- +
* AND:"OR",
- +
* OR:"AND",
- +
* GT:"lte",
- +
* GTE:"lt",
- +
* LT:"gte",
- +
* LTE:"gt",
- +
* EQ:"neq",
- +
* NEQ:"eq",
- +
* LIKE:'NOT LIKE',
- +
* "NOT LIKE":"LIKE",
- +
* '!~*':'~*',
- +
* '~*':'!~*',
- +
* "~":'!~',
- +
* "IN":'NOTIN',
- +
* "NOTIN":"IN",
- +
* "IS":'IS NOT',
- +
* "ISNOT":"IS",
- +
* NOT:"NOOP",
- +
* NOOP:"NOT",
- +
* ILIKE:'NOT ILIKE',
- +
* NOTILIKE:"ILIKE"
- +
* }
- +
*/
OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,
- -
/**
- -
* Represents an identifier (column or table). Can be used
- -
* to specify a String with multiple underscores that should not be
- +
* split, or for creating an implicit identifier without using a String.
* Default mathematical operators.
- -
*
- -
* @constructs
- -
* @augments patio.sql.GenericExpression
- +
* @augments patio.sql.QualifyingMethods
- +
* @type Object
- +
* @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}
- +
*/
- +
MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,
- +
- +
/**
* Default bitwise operators.
- -
*
- +
* @param {String}value the identifier.
- +
* @type Object
- +
* @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}
- +
*/
- +
BITWISE_OPERATORS:BITWISE_OPERATORS,
- +
/**
* Default inequality operators.
- -
*
- +
* @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
- +
* @type Object
* @default {GT:">",GTE:">=",LT:"<",LTE:"<="}
- -
*/
- -
constructor:function (value) {
- 16802
-this.__value = value;
- +
},
INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,
- -
/**
- -
* Converts the {@link patio.sql.Identifier} to a string.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
* Default boolean operators.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.Identifier}.
- +
* @type Object
* @default {AND:"AND",OR:"OR"}
- -
*/
- -
toString:function (ds) {
- 21906
-!Dataset && (Dataset = require("./dataset"));
- 21906
-ds = ds || new Dataset();
- 21906
-return ds.quoteIdentifier(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
value:function () {
- 25154
-return this.__value;
- -
}
- -
}
- -
}
- -
}).as(sql, "Identifier");
- -
- 1
-var JoinClause = define(Expression, {
- -
instance:{
- +
/**@lends patio.sql.JoinClause.prototype*/
BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,
- -
/**
- -
* Represents an SQL JOIN clause, used for joining tables.
- -
* Created by {@link patio.Dataset} join methods.
- -
* @constructs
- -
* @augments patio.sql.Expression
- -
*
- -
* @param {String} joinType the type of join this JoinClause should use
- -
* @param table the table to join with
- +
* @param tableAlias the alias to use for this join clause
* Default IN operators.
- -
*
- -
* @property {String} joinType <b>READ ONLY</b> the type of join this JoinClause should use
- -
* @property table <b>READ ONLY</b> the table to join with
- -
* @property joinType <b>READ ONLY</b> the alias to use for this join clause
- -
* */
- -
constructor:function (joinType, table, tableAlias) {
- 791
-this.__joinType = joinType;
- 791
-this.__table = table;
- 791
-this.__tableAlias = tableAlias || null;
- -
},
- +
- +
* @type Object
- +
* @default {IN:"IN",NOTIN:'NOT IN'}
- +
*/
IN_OPERATORS:IN_OPERATORS,
- -
/**
- +
* Converts the {@link patio.sql.JoinClause} to a string.
* Default IS operators.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* @type Object
- +
* @default {IS:"IS", ISNOT:'IS NOT'}
- +
*/
- +
IS_OPERATORS:IS_OPERATORS,
- +
/**
* Default two arity operators.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.JoinClause}.
- +
* @type Object
- +
* @default {
- +
* EQ:'=',
- +
* NEQ:'!=', LIKE:"LIKE",
- +
* "NOT LIKE":'NOT LIKE',
- +
* ILIKE:"ILIKE",
- +
* "NOT ILIKE":'NOT ILIKE',
- +
* "~":"~",
- +
* '!~':"!~",
- +
* '~*':"~*",
- +
* '!~*':"!~*",
- +
* GT:">",
- +
* GTE:">=",
- +
* LT:"<",
- +
* LTE:"<=",
- +
* bitWiseAnd:"&",
- +
* bitWiseOr:"|",
- +
* exclusiveOr:"^",
- +
* leftShift:"<<",
- +
* rightShift:">>",
- +
* IS:"IS",
- +
* ISNOT:'IS NOT',
- +
* IN:"IN",
- +
* NOTIN:'NOT IN'
* }
- -
*/
- -
toString:function (ds) {
- 17
-!Dataset && (Dataset = require("./dataset"));
- 17
-ds = ds || new Dataset();
- 17
-return ds.joinClauseSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
joinType:function () {
- 927
-return this.__joinType;
- -
},
- -
- -
table:function () {
- 928
-return this.__table;
- -
},
- -
- -
tableAlias:function () {
- 926
-return this.__tableAlias;
- -
}
- -
}
- -
}
- -
}).as(sql, "JoinClause");
- +
TWO_ARITY_OPERATORS:TWO_ARITY_OPERATORS,
- -
- 1
-var JoinOnClause = define(JoinClause, {
- -
instance:{
/**@lends patio.sql.JoinOnClause.prototype*/
- -
/**
- -
* Represents an SQL JOIN clause with ON conditions. Created by {@link patio.Dataset} join methods.
- -
* See {@link patio.sql.JoinClause} for other argument parameters.
- -
* @constructs
- +
* @augments patio.sql.JoinClause
* Default N(multi) arity operators.
- -
*
- -
* @param on the expression to filter with. See {@link patio.Dataset#filter}
- +
* @property on <b>READ ONLY</b> the filter to use with joining the datasets.
- +
* @type Object
- +
* @default {
- +
* "||":"||",
- +
* AND:"AND",
- +
* OR:"OR",
- +
* PLUS:"+",
- +
* MINUS:"-",
- +
* DIVIDE:"/", MULTIPLY:"*"
* }
- -
*/
- -
constructor:function (on, joinType, table, tableAlias) {
- 761
-this.__on = on;
- 761
-this._super(arguments, [joinType, table, tableAlias]);
- +
},
N_ARITY_OPERATORS:N_ARITY_OPERATORS,
- -
/**
- -
* Converts the {@link patio.sql.JoinOnClause} to a string.
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
* Default ONE operators.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.JoinOnClause}.
- +
* @type Object
- +
* @default {
- +
* "NOT":"NOT",
- +
* "NOOP":"NOOP"
* }
- -
*/
- -
toString:function (ds) {
- 813
-!Dataset && (Dataset = require("./dataset"));
- 813
-ds = ds || new Dataset();
- 813
-return ds.joinOnClauseSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
on:function () {
- 813
-return this.__on;
- -
}
- +
}
ONE_ARITY_OPERATORS:ONE_ARITY_OPERATORS
- -
}
- +
}).as(sql, "JoinOnClause");
}).as(sql, "ComplexExpression");
- -
- 1
-var JoinUsingClause = define(JoinClause, {
- -
instance:{
- +
/**@lends patio.sql.JoinUsingClause.prototype*/
- +
/**
- +
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- +
* in a boolean value in SQL.
- +
*
- +
* @augments patio.sql.ComplexExpression
- +
* @augments patio.sql.BooleanMethods
- +
* @name BooleanExpression
- +
* @memberOf patio.sql
- +
*/
- 1
+var BooleanExpression = define([ComplexExpression, BooleanMethods], {
- +
static:{
/**@lends patio.sql.BooleanExpression*/
- -
/**
- -
* Represents an SQL JOIN clause with USING conditions.
- -
* Created by {@link patio.Dataset} join methods.
- +
* See {@link patio.sql.JoinClause} for other argument parameters.
- +
* Invert the expression, if possible. If the expression cannot
- +
* be inverted, it throws an {@link patio.error.ExpressionError}. An inverted expression should match
- +
* everything that the uninverted expression did not match, and vice-versa, except for possible issues with
* SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
- -
*
- -
* @constructs
- +
* @augments patio.sql.JoinClause
- +
* @example
* BooleanExpression.invert(sql.a) //=> NOT "a"
- -
*
- -
* @param using the column/s to use when joining.
- +
* @property using <b>READ ONLY</b> the column/s to use when joining.
- +
* @param {patio.sql.BooleanExpression} expression
- +
* the expression to invert.
- +
*
* @return {patio.sql.BooleanExpression} the inverted expression.
- -
*/
- -
constructor:function (using, joinType, table, tableAlias) {
- 8
-this.__using = using.map(function (u) {
- 9
-return isString(u) ? new Identifier(u) : u;
- -
});
- 8
+this._super(arguments, [joinType, table, tableAlias]);
- +
invert:function (expression) {
- 95
+if (isInstanceOf(expression, BooleanExpression)) {
- 90
+var op = expression.op, newArgs;
- 90
+if (op == "AND" || op == "OR") {
- 3
+newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args.map(function (arg) {
- 6
+return BooleanExpression.invert(arg);
- +
}));
- 3
+return BooleanExpression.fromArgs(newArgs);
- +
} else {
- 87
+newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);
- 87
+return BooleanExpression.fromArgs(newArgs);
- +
}
- 5
+} else if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {
- 0
+throw new ExpressionError(format("Cannot invert %4j", [expression]));
- +
} else {
- 5
+return new BooleanExpression("NOT", expression);
}
- -
},
- -
/**
- +
* Converts the {@link patio.sql.JoinUsingClause} to a string.
- +
* Take pairs of values (e.g. a hash or array of two element arrays)
- +
* and converts it to a {@link patio.sql.BooleanExpression}. The operator and args
* used depends on the case of the right (2nd) argument:
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* <pre class='code'>
- +
* BooleanExpression.fromValuePairs({a : [1,2,3]}) //=> a IN (1,2,3)
- +
* BooleanExpression.fromValuePairs({a : true}); // a IS TRUE;
- +
* BooleanExpression.fromValuePairs({a : /^A/i}); // a *~ '^A'
* </pre>
- -
*
- -
* @return String the SQL version of the {@link patio.sql.JoinUsingClause}.
- -
*/
- -
toString:function (ds) {
- 95
-!Dataset && (Dataset = require("./dataset"));
- 95
-ds = ds || new Dataset();
- 95
-return ds.joinUsingClauseSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
using:function () {
- 95
+return this.__using;
- +
* If multiple arguments are given, they are joined with the op given (AND
- +
* by default, OR possible). If negate is set to true,
- +
* all subexpressions are inverted before used.
- +
* <pre class='code'>
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}) //=> a IN (1,2,3) AND b IS TRUE
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
- +
* </pre>
- +
* @param {Object} a object to convert to a {@link patio.sql.BooleanExpression}
- +
* @param {String} [op="AND"] Boolean operator to join each subexpression with.
- +
* <pre class="code">
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR") //=> a IN (1,2,3) OR b IS TRUE
- +
* </pre>
- +
* @param {Boolean} [negate=false] set to try to invert the {@link patio.sql.BooleanExpression}.
- +
* <pre class="code">
- +
* BooleanExpression.fromValuePairs({a : [1,2,3], b : true}, "OR", true) //=> a NOT IN (1,2,3) AND b IS NOT TRUE
- +
* </pre>
- +
* @return {patio.sql.BooleanExpression} expression composed of sub expressions built from the hash.
- +
*/
- +
fromValuePairs:function (a, op, negate) {
- 7610
+!Dataset && (Dataset = require("./dataset"));
- 7610
+op = op || "AND", negate = negate || false;
- 7610
+var pairArr = [];
- 7610
+var isArr = isArray(a) && Expression.isConditionSpecifier(a);
- 7610
+if (isHash(a)) {
- 3385
+pairArr.push(this.__filterObject(a));
- +
} else {
- 4225
+for (var k in a) {
- 5203
+var v = isArr ? a[k][1] : a[k], ret;
- 5203
+k = isArr ? a[k][0] : k;
- 5203
+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);
- 5186
+} else if (isInstanceOf(v, NegativeBooleanConstant)) {
- 0
+ret = new BooleanExpression("ISNOT", k, v.constant);
- 5186
+} else if (isInstanceOf(v, BooleanConstant)) {
- 0
+ret = new BooleanExpression("IS", k, v.constant);
- 5186
+} else if (isNull(v) || isBoolean(v)) {
- 240
+ret = new BooleanExpression("IS", k, v);
- 4946
+} else if (isHash(v)) {
- 0
+ret = BooleanExpression.__filterObject(v, k);
- 4946
+} else if (isRegExp(v)) {
- 69
+ret = StringExpression.like(sql.stringToIdentifier(k), v);
- +
} else {
- 4877
+ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);
- +
}
- 5203
+negate && (ret = BooleanExpression.invert(ret));
- 5203
+pairArr.push(ret);
}
- -
}
- -
}
- -
}
- -
}).as(sql, "JoinUsingClause");
- -
- -
- 1
-var PlaceHolderLiteralString = define(GenericExpression, {
- -
instance:{
- +
/**@lends patio.sql.PlaceHolderLiteralString.prototype*/
- +
//if We just have one then return the first otherwise create a new Boolean expression
- 7610
+return pairArr.length == 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));
},
- -
/**
- -
* Represents a literal string with placeholders and arguments.
- -
* This is necessary to ensure delayed literalization of the arguments
- -
* required for the prepared statement support and for database-specific
- +
* literalization.
* @private
- -
*
- -
* @constructs
- +
* @augments patio.sql.GenericExpression
* This builds an expression from a hash
- -
*
- -
* @param {String} str the string that contains placeholders.
- -
* @param {Array} args array of arguments that will be literalized using {@link patio.Dataset#literal}, and
- -
* replaced in the string.
- +
* @param {Boolean} [parens=false] set to true to wrap the string in parens.
* @example
- -
*
- -
* @property {String} str <b>READ ONLY</b> the string that contains placeholders.
- -
* @property {Array} args <b>READ ONLY</b> array of arguments that will be literalized using {@link patio.Dataset#literal}, and
- -
* replaced in the string.
- -
* @property {String} parens <b>READ ONLY</b> set to true to wrap the string in parens.
- -
*/
- -
constructor:function (str, args, parens) {
- 53
-parens = parens || false;
- 53
-var v;
- 53
-this.__str = str;
- 53
-this.__args = isArray(args) && args.length == 1 && isHash((v = args[0])) ? v : args;
- 53
-this.__parens = parens;
- -
},
- -
- -
/**
- +
* Converts the {@link patio.sql.PlaceHolderLiteralString} to a string.
- +
* Dataset._filterObject({a : 1}) //=> WHERE (a = 1)
- +
* Dataset._filterObject({x : {gt : 1}}) //=> WHERE (x > 1)
- +
* Dataset._filterObject({x : {gt : 1}, a : 1}) //=> WHERE ((x > 1) AND (a = 1))
- +
* Dataset._filterObject({x : {like : "name"}}) //=> WHERE (x LIKE 'name')
- +
* Dataset._filterObject({x : {iLike : "name"}}) //=> WHERE (x LIKE 'name')
- +
* Dataset._filterObject({x : {between : [1,10]}}) //=> WHERE ((x >= 1) AND (x <= 10))
- +
* Dataset._filterObject({x : {notBetween : [1,10]}}) //=> WHERE ((x < 1) OR (x > 10))
* Dataset._filterObject({x : {neq : 1}}) //=> WHERE (x != 1)
- -
*
- -
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
* @param {Object} expr the expression we need to create an expression out of
* @param {String} [key=null] the key that the hash corresponds to
- -
*
- +
* @return String the SQL version of the {@link patio.sql.PlaceHolderLiteralString}.
* @return {patio.sql.Expression} an expression to use in the filter
- -
*/
- -
toString:function (ds) {
- 52
-!Dataset && (Dataset = require("./dataset"));
- 52
-ds = ds || new Dataset();
- 52
-return ds.placeholderLiteralStringSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
str:function () {
- 55
-return this.__str;
- -
},
- -
args:function () {
- 55
-return this.__args;
- +
},
- +
__filterObject:function (expr, key) {
- 3502
+var pairs = [], opts, newKey;
- 3502
+var twoArityOperators = this.TWO_ARITY_OPERATORS;
- 3502
+for (var k in expr) {
- 3541
+var v = expr[k];
- 3541
+if (isHash(v)) { //its a hash too filter it too!
- 115
+pairs.push(this.__filterObject(v, k));
- 3426
+} else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {
- +
//its a two arrity operator (e.g. '=', '>')
- 118
+newKey = isString(key) ? key.split(",") : [key];
- 118
+if (newKey.length > 1) {
- +
//this represents a hash where the key represents two columns
- +
//(e.g. {"col1,col2" : 1}) => WHERE (col1 = 1 AND col2 = 1)
- 1
+pairs = pairs.concat(newKey.map(function (k) {
- +
//filter each column with the expression
- 2
+return this.__filterObject(expr, k);
- +
}, this));
- +
} else {
- 117
+newKey = [sql.stringToIdentifier(newKey[0])];
- 117
+if (k.match(/^like$/)) {
//its a like clause {col : {like : "hello"}}
- -
- -
parens:function () {
- 55
+return this.__parens;
- 3
+pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));
- 114
+} else if (k.match(/^iLike$/)) {
- +
//its a like clause {col : {iLike : "hello"}}
- 2
+pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]).concat({caseInsensitive:true}))));
- 112
+} else if (k.match(/between/i)) {
- +
//its a like clause {col : {between : [1,10]}}
- 6
+var between = sql.stringToIdentifier(newKey[0]).between(v);
- 6
+k == "notBetween" && (between = between.not());
- 6
+pairs.push(between);
- +
} else {
- +
//otherwise is just a boolean expressio
- +
//it its not a valid operator then we
- +
//BooleanExpression with throw an error
- 106
+pairs.push(new BooleanExpression(k, newKey[0], v));
- +
}
- +
}
- +
} else {
- +
//we're not a twoarity operator
- +
//so we create a boolean expression out of it
- 3308
+newKey = k.split(",");
- 3308
+if (newKey.length == 1) {
- 3302
+newKey = sql.stringToIdentifier(newKey[0]);
- +
}
- 3308
+opts = [
- +
[newKey, v]
- +
];
- 3308
+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
- 3502
return pairs.length == 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));
}
- -
}
- +
}).as(sql, "PlaceHolderLiteralString");
}).as(sql, "BooleanExpression");
- -
- 1
+var SQLFunction = define(GenericExpression, {
- 1
var Constant = define(GenericExpression, {
- -
instance:{
- -
/**@lends patio.sql.SQLFunction.prototype*/
- +
/**@lends patio.sql.Constant.prototype*/
- -
/**
- +
* Represents an SQL function call.
* Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.
*
* @constructs
- -
* @augments patio.sql.GenericExpression
- -
*
- -
* @param {...} f variable number of arguments where the first argument is the name
- -
* of the SQL function to invoke. The rest of the arguments will be literalized through
- -
* {@link patio.Dataset#literal} and placed into the SQL function call.
- -
*
- -
* @property {String} f <b>READ ONLY</b> the SQL function to call.
- -
* @property {Array} args <b>READ ONLY</b> args arguments will be literalized through
- -
* {@link patio.Dataset#literal} and placed into the SQL function call.
- -
* */
- -
constructor:function (f) {
- 1109
-var args = argsToArray(arguments).slice(1);
- 1109
-this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {
- 773
-return isString(a) ? sql.stringToIdentifier(a) : a;
- +
});
- +
* @property {String} constant <b>READ ONLY</b> the contant.
- +
*/
- +
constructor:function (constant) {
- 18
this.__constant = constant;
},
- -
/**
- +
* Converts the {@link patio.sql.SQLFunction} to a string.
* Converts the {@link patio.sql.Constant} to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.SQLFunction}.
* @return String the SQL version of the {@link patio.sql.Constant}.
*/
- -
toString:function (ds) {
- 565
-!Dataset && (Dataset = require("./dataset"));
- 565
-ds = ds || new Dataset();
- 565
+return ds.functionSql(this);
- 6
+!Dataset && (Dataset = require("./dataset"));
- 6
+ds = ds || new Dataset();
- 6
return ds.constantSql(this.__constant);
},
- -
/**@ignore*/
- -
getters:{
- -
f:function () {
- 567
-return this.__f;
- -
},
- -
- -
args:function () {
- 567
+return this.__args;
- +
constant:function () {
- 0
return this.__constant;
}
}
- -
}
- +
}).as(sql, "SQLFunction");
}).as(sql, "Constant");
- -
/**
- -
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- -
* in a numeric value in SQL.
- -
*
- +
* @name NumericExpression
- +
* @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
- +
* @auments patio.sql.Constant
* @name BooleanConstant
- -
* @memberOf patio.sql
- -
* @augments patio.sql.ComplexExpression
- -
* @augments patio.sql.BitWiseMethods
- -
* @augments patio.sql.NumericMethods
* @augments patio.sql.InequalityMethods
- -
*/
- 1
-var NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");
- -
- -
- 1
+var OrderedExpression = define(Expression, {
- 1
var BooleanConstant = define(Constant, {
- -
instance:{
- -
/**@lends patio.sql.OrderedExpression.prototype*/
- -
- -
/**
- -
* Represents a column/expression to order the result set by.
- -
* @constructs
- -
* @augments patio.sql.Expression
- -
*
- -
* @param expression the expression to order
- -
* @param {Boolean}[descending=true] set to false to order ASC
- -
* @param {String|Object} [opts=null] additional options
- -
* <ul>
- -
* <li>String: if value is "first" the null values will be first, if "last" then null values
- -
* will be last</li>
- -
* <li>Object: will pull the nulls property off of the object use use the same rules as if it
- -
* were a string</li>
- -
* </ul>
- -
* @property expression <b>READ ONLY</b> the expression to order.
- -
* @property {Boolean} [descending=true] <b>READ ONLY</b> true if decending, false otherwise.
- -
* @property {String} [nulls=null] if value is "first" the null values will be first, if "last" then null values
- -
* will be last
- -
*/
- -
constructor:function (expression, descending, opts) {
- 92
-descending = isBoolean(descending) ? descending : true;
- 92
-opts = opts || {};
- 92
-this.__expression = expression;
- 92
-this.__descending = descending;
- 92
-var nulls = isString(opts) ? opts : opts.nulls;
- 92
-this.__nulls = isString(nulls) ? nulls.toLowerCase() : null;
- -
},
- -
- -
/**
- -
* @return {patio.sql.OrderedExpression} a copy that is ordered ASC
- -
*/
- -
asc:function () {
- 0
-return new OrderedExpression(this.__expression, false, {nulls:this.__nulls});
- +
},
/**@lends patio.sql.BooleanConstant.prototype*/
- -
/**
- +
* @return {patio.sql.OrderedExpression} Return a copy that is ordered DESC
- +
* Converts the {@link patio.sql.BooleanConstant} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
* @return String the SQL version of the {@link patio.sql.BooleanConstant}.
- -
*/
- -
desc:function () {
- 0
-return new OrderedExpression(this.__expression, true, {nulls:this.__nulls});
- +
},
- +
toString:function (ds) {
- 10
+!Dataset && (Dataset = require("./dataset"));
- 10
+ds = ds || new Dataset();
- 10
+return ds.booleanConstantSql(this.__constant);
- +
}
- +
}
}).as(sql, "BooleanConstant");
- -
- -
/**
- -
* * @return {patio.sql.OrderedExpression} an inverted expression, changing ASC to DESC and NULLS FIRST to NULLS LAST.
- -
* */
- -
invert:function () {
- 17
-return new OrderedExpression(this.__expression, !this.__descending, {nulls:this._static.INVERT_NULLS[this.__nulls] || this.__nulls});
- +
},
- +
/**
- +
* Represents inverse boolean constants (currently only NOTNULL). A
- +
* special class to allow for special behavior.
- +
*
- +
* @augments patio.sql.BooleanConstant
- +
* @name NegativeBooleanConstant
- +
* @memberOf patio.sql
- +
*/
- 1
+var NegativeBooleanConstant = define(BooleanConstant, {
- +
instance:{
/**@lends patio.sql.NegativeBooleanConstant.prototype*/
- -
/**
- +
* Converts the {@link patio.sql.OrderedExpression} to a string.
* Converts the {@link patio.sql.NegativeBooleanConstant} to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.OrderedExpression}.
* @return String the SQL version of the {@link patio.sql.NegativeBooleanConstant}.
*/
- -
toString:function (ds) {
- 73
-!Dataset && (Dataset = require("./dataset"));
- 73
-ds = ds || new Dataset();
- 73
-return ds.orderedExpressionSql(this);
- -
},
- -
- -
/**@ignore*/
- -
getters:{
- -
expression:function () {
- 75
-return this.__expression;
- -
},
- -
descending:function () {
- 75
-return this.__descending;
- -
},
- -
nulls:function () {
- 82
-return this.__nulls;
- +
}
- 2
+!Dataset && (Dataset = require("./dataset"));
- 2
+ds = ds || new Dataset();
- 2
return ds.negativeBooleanConstantSql(this.__constant);
- -
}
- -
},
- -
static:{
- -
/**@lends patio.sql.OrderedExpression*/
- -
/**
- -
* Hash that contains the inversions for "first" and "last".
- -
* @type Object
- -
* @default {first:"last", last:"first"}
- -
*/
INVERT_NULLS:{first:"last", last:"first"}
- -
}
- +
}).as(sql, "OrderedExpression");
}).as(sql, "NegativeBooleanConstant");
- -
- 1
+var QualifiedIdentifier = define([GenericExpression, QualifyingMethods], {
- +
/**
- +
* @namespace Holds default generic constants that can be referenced. These
- +
* are included in {@link patio}
- +
* @name Constants
- +
* @memberOf patio.sql
- +
*/
- 1
+sql.Constants = {
- +
/**@lends patio.sql.Constants*/
- +
- +
/**
- +
* Constant for CURRENT DATE
- +
* @type patio.sql.Constant
- +
*/
- +
CURRENT_DATE:new Constant("CURRENT_DATE"),
- +
- +
/**
- +
* Constant for CURRENT TIME
- +
* @type patio.sql.Constant
- +
*/
- +
CURRENT_TIME:new Constant("CURRENT_TIME"),
- +
- +
/**
- +
* Constant for CURRENT TIMESTAMP
- +
* @type patio.sql.Constant
- +
*/
- +
CURRENT_TIMESTAMP:new Constant("CURRENT_TIMESTAMP"),
- +
- +
/**
- +
* Constant for TRUE
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
SQLTRUE:new BooleanConstant(1),
- +
- +
/**
- +
* Constant for TRUE
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
TRUE:new BooleanConstant(1),
- +
- +
/**
- +
* Constant for FALSE.
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
SQLFALSE:new BooleanConstant(0),
- +
- +
/**
- +
* Constant for FALSE
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
FALSE:new BooleanConstant(0),
- +
/**
- +
* Constant for NULL
- +
* @type patio.sql.BooleanConstant
- +
*/
- +
NULL:new BooleanConstant(null),
- +
- +
/**
- +
* Constant for NOT NULL
- +
* @type patio.sql.NegativeBooleanConstant
- +
*/
- +
NOTNULL:new NegativeBooleanConstant(null)
- +
- +
};
- +
- 1
+var Constants = sql.Constants
- +
- 1
var Identifier = define([GenericExpression, QualifyingMethods], {
- -
instance:{
- +
/**@lends patio.sql.QualifiedIdentifier.prototype*/
/**@lends patio.sql.Identifier.prototype*/
- -
/**
- +
* Represents a qualified identifier (column with table or table with schema).
- +
* Represents an identifier (column or table). Can be used
- +
* to specify a String with multiple underscores that should not be
* split, or for creating an implicit identifier without using a String.
*
* @constructs
* @augments patio.sql.GenericExpression
* @augments patio.sql.QualifyingMethods
- -
*
- -
* @param table the table or schema to qualify the column or table to.
- +
* @param column the column or table to qualify.
* @param {String}value the identifier.
- -
*
- -
* @property table <b>READ ONLY</b> the table or schema to qualify the column or table to.
- +
* @property column <b>READ ONLY</b> he column or table to qualify.
* @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
- -
*/
- -
constructor:function (table, column) {
- 4451
-this.__table = table;
- 4451
+this.__column = column;
- +
constructor:function (value) {
- 16414
this.__value = value;
},
- -
/**
- +
* Converts the {@link patio.sql.QualifiedIdentifier} to a string.
* Converts the {@link patio.sql.Identifier} to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.QualifiedIdentifier}.
* @return String the SQL version of the {@link patio.sql.Identifier}.
*/
- -
toString:function (ds) {
- 4532
-!Dataset && (Dataset = require("./dataset"));
- 4532
-ds = ds || new Dataset();
- 4532
+return ds.qualifiedIdentifierSql(this);
- 21906
+!Dataset && (Dataset = require("./dataset"));
- 21906
+ds = ds || new Dataset();
- 21906
return ds.quoteIdentifier(this);
},
/**@ignore*/
- -
getters:{
- -
table:function () {
- 4554
-return this.__table;
- -
},
- -
- -
column:function () {
- 4559
+return this.__column;
- +
value:function () {
- 25154
return this.__value;
}
}
- -
}
- -
}).as(sql, "QualifiedIdentifier");
- +
}).as(sql, "Identifier");
- -
- 1
-var likeElement = function (re) {
- 875
-var ret;
- 875
-if (isRegExp(re)) {
- 80
-ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase]
- -
} else {
- 795
-ret = [re, false, false];
- -
}
- 875
-return ret;
- -
};
- -
/**
- -
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- -
* in a text/string/varchar value in SQL.
- -
*
- -
* @augments patio.sql.ComplexExpression
- -
* @augments patio.sql.StringMethods
- -
* @augments patio.sql.StringConcatenationMethods
- -
* @augments patio.sql.InequalityMethods
- -
* @augments patio.sql.NoBooleanInputMethods
- -
* @name StringExpression
- -
* @memberOf patio.sql
- -
*/
- 1
-var StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {
- -
static:{
- +
/**@lends patio.sql.StringExpression*/
- 1
+var JoinClause = define(Expression, {
- +
instance:{
/**@lends patio.sql.JoinClause.prototype*/
- -
/**
- -
* <p>Creates a SQL pattern match expression. left (l) is the SQL string we
- -
* are matching against, and ces are the patterns we are matching.
- +
* The match succeeds if any of the patterns match (SQL OR).</p>
- +
* Represents an SQL JOIN clause, used for joining tables.
- +
* Created by {@link patio.Dataset} join methods.
- +
* @constructs
* @augments patio.sql.Expression
- -
*
- -
* <p>If a regular expression is used as a pattern, an SQL regular expression will be
- -
* used, which is currently only supported on MySQL and PostgreSQL. Be aware
- -
* that MySQL and PostgreSQL regular expression syntax is similar to javascript
- -
* regular expression syntax, but it not exactly the same, especially for
- -
* advanced regular expression features. Patio just uses the source of the
- +
* regular expression verbatim as the SQL regular expression string.</p>
- +
* @param {String} joinType the type of join this JoinClause should use
- +
* @param table the table to join with
* @param tableAlias the alias to use for this join clause
- -
*
- -
* <p>If any other object is used as a regular expression, the SQL LIKE operator will
- +
* be used, and should be supported by most databases.</p>
- +
* @property {String} joinType <b>READ ONLY</b> the type of join this JoinClause should use
- +
* @property table <b>READ ONLY</b> the table to join with
- +
* @property joinType <b>READ ONLY</b> the alias to use for this join clause
- +
* */
- +
constructor:function (joinType, table, tableAlias) {
- 791
+this.__joinType = joinType;
- 791
+this.__table = table;
- 791
+this.__tableAlias = tableAlias || null;
- +
},
- +
- +
/**
* Converts the {@link patio.sql.JoinClause} to a string.
- -
*
- -
* <p>The pattern match will be case insensitive if the last argument is a hash
- -
* with a key of caseInsensitive that is not false or null. Also,
- -
* if a case insensitive regular expression is used (//i), that particular
- +
* pattern which will always be case insensitive.</p>
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- -
* @example
- -
* StringExpression.like(sql.a, 'a%') //=> "a" LIKE 'a%'
- -
* StringExpression.like(sql.a, 'a%', {caseInsensitive : true}) //=> "a" ILIKE 'a%'
- +
* StringExpression.like(sql.a, 'a%', /^a/i) //=> "a" LIKE 'a%' OR "a" ~* '^a'
* @return String the SQL version of the {@link patio.sql.JoinClause}.
- -
*/
- -
like:function (l) {
- 437
-var args = argsToArray(arguments, 1);
- 437
-var params = likeElement(l);
- 437
-var likeMap = this.likeMap;
- 437
-var lh = params[0], lre = params[1], lci = params[2];
- 437
-var last = args[args.length - 1];
- 437
-lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;
- 437
-args = args.map(function (ce) {
- 438
-var r, rre, rci;
- 438
-var ceArr = likeElement(ce);
- 438
-r = ceArr[0], rre = ceArr[1], rci = ceArr[2];
- 438
-return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r)
- -
}, this);
- 437
+return args.length == 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));
- +
toString:function (ds) {
- 17
+!Dataset && (Dataset = require("./dataset"));
- 17
+ds = ds || new Dataset();
- 17
return ds.joinClauseSql(this);
},
- -
- -
/**
- -
* Like map used to by {@link patio.sql.StringExpression.like} to create the
- -
* LIKE expression.
- -
* @type Object
- -
*/
- +
likeMap:{"truetrue":'~*', "truefalse":"~", "falsetrue":"ILIKE", "falsefalse":"LIKE"}
- +
/**@ignore*/
- +
getters:{
- +
joinType:function () {
- 927
+return this.__joinType;
},
- +
- +
table:function () {
- 928
+return this.__table;
},
- +
- +
tableAlias:function () {
- 926
+return this.__tableAlias;
- +
}
}
- -
}
- +
}).as(sql, "StringExpression");
}).as(sql, "JoinClause");
- -
- 1
-var SubScript = define(GenericExpression, {
- -
instance:{
/**@lends patio.sql.SubScript.prototype*/
- +
- 1
+var JoinOnClause = define(JoinClause, {
- +
instance:{
/**@lends patio.sql.JoinOnClause.prototype*/
- -
/**
- +
* Represents an SQL array access, with multiple possible arguments.
- +
* Represents an SQL JOIN clause with ON conditions. Created by {@link patio.Dataset} join methods.
* See {@link patio.sql.JoinClause} for other argument parameters.
- -
* @constructs
- +
* @augments patio.sql.GenericExpression
* @augments patio.sql.JoinClause
- -
*
- -
* @param arrCol the SQL array column
- -
* @param sub The array of subscripts to use (should be an array of numbers)
- -
*/
- -
constructor:function (arrCol, sub) {
- -
//The SQL array column
- 109
-this.__arrCol = arrCol;
- -
//The array of subscripts to use (should be an array of numbers)
- 109
-this.__sub = sub;
- -
},
- -
- -
/**
- -
* Create a new {@link patio.sql.Subscript} appending the given subscript(s)
- +
* the the current array of subscripts.
- +
* @param on the expression to filter with. See {@link patio.Dataset#filter}
* @property on <b>READ ONLY</b> the filter to use with joining the datasets.
- -
*/
- -
addSub:function (sub) {
- 0
+return new SubScript(this.__arrCol, this.__sub.concat(sub));
- +
constructor:function (on, joinType, table, tableAlias) {
- 761
+this.__on = on;
- 761
this._super(arguments, [joinType, table, tableAlias]);
},
- -
/**
- +
* Converts the {@link patio.sql.SubScript} to a string.
* Converts the {@link patio.sql.JoinOnClause} to a string.
*
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return String the SQL version of the {@link patio.sql.SubScript}.
* @return String the SQL version of the {@link patio.sql.JoinOnClause}.
*/
- -
toString:function (ds) {
- 109
-!Dataset && (Dataset = require("./dataset"));
- 109
-ds = ds || new Dataset();
- 109
+return ds.subscriptSql(this);
- 813
+!Dataset && (Dataset = require("./dataset"));
- 813
+ds = ds || new Dataset();
- 813
return ds.joinOnClauseSql(this);
},
/**@ignore*/
- -
getters:{
- -
f:function () {
- 110
-return this.__arrCol;
- -
},
- -
- -
sub:function () {
- 110
+return this.__sub;
- +
on:function () {
- 813
return this.__on;
}
}
- -
}
- -
}).as(sql, "SubScript");
- -
- 1
-var STRING_METHODS = ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "quote",
- -
"replace", "search", "slice", "split", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase",
- -
"toSource", "toString", "toUpperCase", "trim", "trimLeft", "trimRight", "valueOf"];
- +
}).as(sql, "JoinOnClause");
- -
- 1
-var addStringMethod = function (op) {
- 24
-return function () {
- 4294
-return this.__str[op].apply(this.__str, arguments);
- -
}
};
- -
- 1
+var LiteralString = define([OrderedMethods, ComplexExpressionMethods, BooleanMethods, NumericMethods, StringMethods, InequalityMethods, AliasMethods], {
- 1
var JoinUsingClause = define(JoinClause, {
- -
instance:{
- +
/**@lends patio.sql.LiteralString*/
/**@lends patio.sql.JoinUsingClause.prototype*/
- -
/**
- -
* Represents a string that should be placed into a SQL query literally.
- +
* <b>This class has all methods that a normal javascript String has.</b>
- +
* Represents an SQL JOIN clause with USING conditions.
- +
* Created by {@link patio.Dataset} join methods.
- +
* See {@link patio.sql.JoinClause} for other argument parameters.
*
- -
* @constructs
- -
* @augments patio.sql.OrderedMethods
- -
* @augments patio.sql.ComplexExpressionMethods
- -
* @augments patio.sql.BooleanMethods
- -
* @augments patio.sql.NumericMethods
- -
* @augments patio.sql.StringMethods
- -
* @augments patio.sql.InequalityMethods
- +
* @augments patio.sql.AliasMethods
* @augments patio.sql.JoinClause
- -
*
- +
* @param {String} str the literal string.
- +
* @param using the column/s to use when joining.
* @property using <b>READ ONLY</b> the column/s to use when joining.
- -
*/
- -
constructor:function (str) {
- 3677
-this.__str = str;
- -
}
- -
}
- -
}).as(sql, "LiteralString");
- -
- 1
-STRING_METHODS.forEach(function (op) {
- 24
-LiteralString.prototype[op] = addStringMethod(op);
- -
}, this);
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- +
- +
constructor:function (using, joinType, table, tableAlias) {
- 8
+this.__using = using.map(function (u) {
- 9
+return isString(u) ? new Identifier(u) : u;
- +
});
- 8
+this._super(arguments, [joinType, table, tableAlias]);
},
- +
- +
/**
- +
* Converts the {@link patio.sql.JoinUsingClause} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL version of the {@link patio.sql.JoinUsingClause}.
- +
*/
- +
toString:function (ds) {
- 95
+!Dataset && (Dataset = require("./dataset"));
- 95
+ds = ds || new Dataset();
- 95
+return ds.joinUsingClauseSql(this);
},
- -
- migration.js
- |
-
-
- Coverage91.97
- SLOC607
- LOC274
- Missed22
-
- |
-
- 1
-var comb = require("comb"),
- -
hitch = comb.hitch,
- -
Promise = comb.Promise,
- -
errors = require("./errors"),
- -
MigrationError = errors.MigrationError,
- -
NotImplemented = errors.NotImplemented(),
- -
format = comb.string.format,
- -
define = comb.define,
- -
isFunction = comb.isFunction,
- -
serial = comb.serial,
- -
isNumber = comb.isNumber,
- -
when = comb.when,
- -
isUndefined = comb.isUndefined,
- -
fs = require("fs"),
- +
path = require("path");
- +
/**@ignore*/
- +
getters:{
- +
using:function () {
- 95
+return this.__using;
- +
}
- +
}
- +
}
}).as(sql, "JoinUsingClause");
- -
- 1
+var Migrator = define(null, {
- 1
var PlaceHolderLiteralString = define(GenericExpression, {
- -
instance:{
- -
/**@lends patio.migrations.Migrator.prototype*/
- -
column:null,
- -
db:null,
- -
directory:null,
- -
ds:null,
- -
files:null,
- -
table:null,
- +
target:null,
/**@lends patio.sql.PlaceHolderLiteralString.prototype*/
- -
/**
- +
* Abstract Migrator class. This class should be be instantiated directly.
- +
* Represents a literal string with placeholders and arguments.
- +
* This is necessary to ensure delayed literalization of the arguments
- +
* required for the prepared statement support and for database-specific
* literalization.
*
- -
* @constructs
- -
* @param {patio.Database} db the database to migrate
- -
* @param {String} directory directory that the migration files reside in
- -
* @param {Object} [opts={}] optional parameters.
- -
* @param {String} [opts.column] the column in the table that version information should be stored.
- -
* @param {String} [opts.table] the table that version information should be stored.
- -
* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
- +
* @param {String} [opts.current] the version that the database is currently at if the current version
- +
* @augments patio.sql.GenericExpression
- +
*
- +
* @param {String} str the string that contains placeholders.
- +
* @param {Array} args array of arguments that will be literalized using {@link patio.Dataset#literal}, and
- +
* replaced in the string.
- +
* @param {Boolean} [parens=false] set to true to wrap the string in parens.
- +
*
- +
* @property {String} str <b>READ ONLY</b> the string that contains placeholders.
- +
* @property {Array} args <b>READ ONLY</b> array of arguments that will be literalized using {@link patio.Dataset#literal}, and
- +
* replaced in the string.
* @property {String} parens <b>READ ONLY</b> set to true to wrap the string in parens.
- -
*/
- -
constructor:function (db, directory, opts) {
- 31
-this.db = db;
- 31
-this.directory = directory;
- 31
-opts = opts || {};
- 31
-this.table = opts.table || this._static.DEFAULT_SCHEMA_TABLE;
- 31
-this.column = opts.column || this._static.DEFAULT_SCHEMA_COLUMN;
- 31
+this._opts = opts;
- +
constructor:function (str, args, parens) {
- 53
+parens = parens || false;
- 53
+var v;
- 53
+this.__str = str;
- 53
+this.__args = isArray(args) && args.length == 1 && isHash((v = args[0])) ? v : args;
- 53
this.__parens = parens;
},
- -
/**
- +
* Runs the migration and returns a promise.
- +
* Converts the {@link patio.sql.PlaceHolderLiteralString} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
* @return String the SQL version of the {@link patio.sql.PlaceHolderLiteralString}.
- -
*/
- -
run:function () {
- 0
+throw new NotImplemented("patio.migrations.Migrator#run");
- +
toString:function (ds) {
- 52
+!Dataset && (Dataset = require("./dataset"));
- 52
+ds = ds || new Dataset();
- 52
return ds.placeholderLiteralStringSql(this);
},
- -
- -
getFileNames:function () {
- 50
-if (!this.__files) {
- 49
-return this._static.getFileNames(this.directory).addCallback(hitch(this, function (files) {
- 49
-this.__files = files;
- -
}));
- -
} else {
- 1
+return new Promise().callback(this.__files).promise();
- +
/**@ignore*/
- +
getters:{
- +
str:function () {
- 55
+return this.__str;
- +
},
- +
args:function () {
- 55
+return this.__args;
- +
},
- +
- +
parens:function () {
- 55
return this.__parens;
- -
}
},
- -
- -
getMigrationVersionFromFile:function (filename) {
- 476
return parseInt(path.basename(filename).split(this._static.MIGRATION_SPLITTER)[0], 10);
- -
}
- -
},
- -
- -
"static":{
- +
/**@lends patio.migrations.Migrator*/
- +
}
}).as(sql, "PlaceHolderLiteralString");
- -
- -
MIGRATION_FILE_PATTERN:/^\d+\..+\.js$/i,
- -
MIGRATION_SPLITTER:'.',
MINIMUM_TIMESTAMP:20000101,
- -
- -
getFileNames:function (directory) {
- 80
-var ret = new Promise();
- 80
-fs.readdir(directory, hitch(this, function (err, files) {
- 80
-if (err) {
- 0
-ret.errback(err);
- -
} else {
- 80
-files = files.filter(function (file) {
- 394
-return file.match(this.MIGRATION_FILE_PATTERN) !== null;
- -
}, this);
- 80
-ret.callback(files.map(function (file) {
- 394
-return path.resolve(directory, file);
- -
}));
- -
}
- -
}));
- 80
-return ret.promise();
- +
},
- 1
+var SQLFunction = define(GenericExpression, {
- +
instance:{
/**@lends patio.sql.SQLFunction.prototype*/
- -
/**
- -
* Migrates the database using migration files found in the supplied directory.
- +
* See {@link patio#migrate}
* Represents an SQL function call.
- -
*
- -
* @example
- -
* var DB = patio.connect("my://connection/string");
- -
* patio. migrate(DB, __dirname + "/timestamp_migration").then(function(){
- -
* console.log("done migrating!");
- +
* });
- +
* @constructs
* @augments patio.sql.GenericExpression
- -
*
- -
* patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).then(function(){
- -
* console.log("done migrating down!");
- +
* });
- +
* @param {...} f variable number of arguments where the first argument is the name
- +
* of the SQL function to invoke. The rest of the arguments will be literalized through
* {@link patio.Dataset#literal} and placed into the SQL function call.
- +
*
- +
* @property {String} f <b>READ ONLY</b> the SQL function to call.
- +
* @property {Array} args <b>READ ONLY</b> args arguments will be literalized through
- +
* {@link patio.Dataset#literal} and placed into the SQL function call.
- +
* */
- +
constructor:function (f) {
- 1109
+var args = argsToArray(arguments).slice(1);
- 1109
+this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {
- 773
+return isString(a) ? sql.stringToIdentifier(a) : a;
- +
});
- +
},
- +
- +
/**
* Converts the {@link patio.sql.SQLFunction} to a string.
- -
*
- -
* @param {patio.Database} db the database to migrate
- -
* @param {String} directory directory that the migration files reside in
- -
* @param {Object} [opts={}] optional parameters.
- -
* @param {String} [opts.column] the column in the table that version information should be stored.
- -
* @param {String} [opts.table] the table that version information should be stored.
- -
* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
- -
* @param {String} [opts.current] the version that the database is currently at if the current version
- +
* is not provided it is retrieved from the database.
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- -
*
- +
* @return {Promise} a promise that is resolved once the migration is complete.
* @return String the SQL version of the {@link patio.sql.SQLFunction}.
- -
*/
- -
run:function (db, directory, opts, cb) {
- 31
-if (isFunction(opts)) {
- 0
-cb = opts;
- 0
-opts = {};
- -
} else {
- 31
-opts = opts || {};
- -
}
- 31
-opts = opts || {};
- 31
-var ret = new Promise();
- 31
-this.__getMigrator(directory).then(function (migrator) {
- 31
-new migrator(db, directory, opts).run().then(ret);
- -
}, ret);
- 31
-ret.classic(cb);
- 31
+return ret.promise();
- +
toString:function (ds) {
- 565
+!Dataset && (Dataset = require("./dataset"));
- 565
+ds = ds || new Dataset();
- 565
return ds.functionSql(this);
},
- -
- -
// Choose the Migrator subclass to use. Uses the TimestampMigrator
- -
// // if the version number appears to be a unix time integer for a year
- -
// after 2005, otherwise uses the IntegerMigrator.
- -
__getMigrator:function (directory) {
- 31
-var ret = new Promise();
- 31
-var retClass = IntegerMigrator;
- 31
-this.getFileNames(directory).then(hitch(this, function (files) {
- 31
-var l = files.length;
- 31
-if (l) {
- 31
-for (var i = 0; i < l; i++) {
- 81
-var file = files[i];
- 81
-if (parseInt(path.basename(file).split(this.MIGRATION_SPLITTER)[0], 10) > this.MINIMUM_TIMESTAMP) {
- 18
-retClass = TimestampMigrator;
- 18
-break;
- -
}
- -
}
- -
}
- 31
+ret.callback(retClass);
- +
/**@ignore*/
- +
getters:{
- +
f:function () {
- 567
+return this.__f;
},
- -
- -
}), ret);
- 31
+return ret.promise();
- +
args:function () {
- 567
+return this.__args;
}
}
- -
}
- -
});
- +
}).as(sql, "SQLFunction");
- -
/**
- -
* @class Migrator that uses the file format {migrationName}.{version}.js, where version starts at 0.
- +
* <b>Missing migrations are not allowed</b>
- +
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
* in a numeric value in SQL.
- -
*
- -
* @augments patio.migrations.Migrator
- -
* @name IntegerMigrator
- +
* @memberOf patio.migrations
- +
* @name NumericExpression
- +
* @memberOf patio.sql
- +
* @augments patio.sql.ComplexExpression
- +
* @augments patio.sql.BitWiseMethods
- +
* @augments patio.sql.NumericMethods
* @augments patio.sql.InequalityMethods
- -
*/
- 1
-var IntegerMigrator = define(Migrator, {
- -
instance:{
- -
/**@lends patio.migrations.IntegerMigrator.prototype*/
- -
current:null,
- -
direction:null,
- +
migrations:null,
- 1
var NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");
- -
_migrationFiles:null,
- -
- -
run:function () {
- 13
-var ret = new Promise(), DB = this.db;
- 13
-serial([this._getLatestMigrationVersion.bind(this), this._getCurrentMigrationVersion.bind(this)]).then(hitch(this, function (res) {
- 11
-var target = res[0], current = res[1];
- 11
-if (current !== target) {
- 11
-var direction = this.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;
- 11
-this._getMigrations(current, target, direction).then(hitch(this, function (migrations) {
- 11
-var runMigration = hitch(this, function (index) {
- 60
-if (index >= migrations.length) {
- 11
-ret.callback(version);
- -
} else {
- 49
-var curr = migrations[index], migration = curr[0];
- 49
-version = curr[1];
- 49
-var now = new Date();
- 49
-var lv = isUp ? version : version - 1;
- 49
-DB.logInfo("Begin applying migration version %d, direction: %s", lv, direction);
- 49
-DB.transaction(hitch(this, function () {
- 49
-var ret = new Promise();
- 49
-if (!isFunction(migration[direction])) {
- 0
-this._setMigrationVersion(lv).then(ret);
- -
} else {
- 49
-when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
- 49
-this._setMigrationVersion(lv).then(ret);
- -
}), ret);
- -
}
- 49
-return ret.promise();
- -
})).then(function () {
- 49
-DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);
- 49
-runMigration(index + 1);
- -
}, ret);
- +
}
- 1
+var OrderedExpression = define(Expression, {
- +
instance:{
/**@lends patio.sql.OrderedExpression.prototype*/
- -
- -
});
- 11
-runMigration(0);
- -
}), ret);
- -
} else {
- 0
-ret.callback(target);
- +
}
- +
/**
- +
* Represents a column/expression to order the result set by.
- +
* @constructs
- +
* @augments patio.sql.Expression
- +
*
- +
* @param expression the expression to order
- +
* @param {Boolean}[descending=true] set to false to order ASC
- +
* @param {String|Object} [opts=null] additional options
- +
* <ul>
- +
* <li>String: if value is "first" the null values will be first, if "last" then null values
- +
* will be last</li>
- +
* <li>Object: will pull the nulls property off of the object use use the same rules as if it
- +
* were a string</li>
- +
* </ul>
- +
* @property expression <b>READ ONLY</b> the expression to order.
- +
* @property {Boolean} [descending=true] <b>READ ONLY</b> true if decending, false otherwise.
- +
* @property {String} [nulls=null] if value is "first" the null values will be first, if "last" then null values
- +
* will be last
- +
*/
- +
constructor:function (expression, descending, opts) {
- 92
+descending = isBoolean(descending) ? descending : true;
- 92
+opts = opts || {};
- 92
+this.__expression = expression;
- 92
+this.__descending = descending;
- 92
+var nulls = isString(opts) ? opts : opts.nulls;
- 92
+this.__nulls = isString(nulls) ? nulls.toLowerCase() : null;
},
- -
- -
}), ret);
- 13
+return ret.promise();
- +
/**
- +
* @return {patio.sql.OrderedExpression} a copy that is ordered ASC
- +
*/
- +
asc:function () {
- 0
return new OrderedExpression(this.__expression, false, {nulls:this.__nulls});
},
- -
- -
_getMigrations:function (current, target, direction) {
- 11
-var ret = new Promise(), isUp = direction === "up", migrations = [];
- 11
-when(this._getMigrationFiles()).then(function (files) {
- 11
-try {
- 11
-if ((isUp ? target : current - 1) < files.length) {
- 11
-if (isUp) {
- 8
-current++;
- -
}
- 11
-for (; isUp ? current <= target : current > target; isUp ? current++ : current--) {
- 49
-migrations.push([require(files[current]), current]);
- -
}
- -
} else {
- 0
-return ret.errback(new MigrationError("Invalid target " + target));
- -
}
- -
} catch (e) {
- 0
-return ret.errback(e);
- -
}
- 11
-ret.callback(migrations);
- -
}, ret);
- 11
+return ret.promise();
- +
/**
- +
* @return {patio.sql.OrderedExpression} Return a copy that is ordered DESC
- +
*/
- +
desc:function () {
- 0
+return new OrderedExpression(this.__expression, true, {nulls:this.__nulls});
- +
},
- +
- +
/**
- +
* * @return {patio.sql.OrderedExpression} an inverted expression, changing ASC to DESC and NULLS FIRST to NULLS LAST.
- +
* */
- +
invert:function () {
- 17
return new OrderedExpression(this.__expression, !this.__descending, {nulls:this._static.INVERT_NULLS[this.__nulls] || this.__nulls});
},
- +
- +
/**
- +
* Converts the {@link patio.sql.OrderedExpression} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL version of the {@link patio.sql.OrderedExpression}.
- +
*/
- +
toString:function (ds) {
- 73
+!Dataset && (Dataset = require("./dataset"));
- 73
+ds = ds || new Dataset();
- 73
+return ds.orderedExpressionSql(this);
},
- -
- -
_getMigrationFiles:function () {
- 19
-var ret = new Promise();
- 19
-if (!this._migrationFiles) {
- 13
-var retFiles = [];
- 13
-var directory = this.directory;
- 13
-this.getFileNames().then(hitch(this, function (files) {
- 13
-var l = files.length;
- 13
-if (l) {
- 13
-for (var i = 0; i < l; i++) {
- 59
-var file = files[i];
- 59
-var version = this.getMigrationVersionFromFile(file);
- 59
-if (isUndefined(retFiles[version])) {
- 58
-retFiles[version] = file;
- -
} else {
- 1
-return ret.errback(new MigrationError("Duplicate migration number " + version));
- -
}
- -
}
- 12
-if (isUndefined(retFiles[0])) {
- 0
-retFiles.shift();
- -
}
- 12
-for (var j = 0; j < l; j++) {
- 57
-if (isUndefined(retFiles[j])) {
- 1
-return ret.errback(new MigrationError("Missing migration for " + j));
- -
}
- -
}
- -
}
- 11
-this._migrationFiles = retFiles;
- 11
-ret.callback(retFiles);
- -
}), ret);
- -
} else {
- 6
+ret.callback(this._migrationFiles);
- +
/**@ignore*/
- +
getters:{
- +
expression:function () {
- 75
+return this.__expression;
- +
},
- +
descending:function () {
- 75
+return this.__descending;
- +
},
- +
nulls:function () {
- 82
return this.__nulls;
- -
}
- 19
+return ret.promise();
- +
}
- +
},
- +
static:{
- +
/**@lends patio.sql.OrderedExpression*/
- +
/**
- +
* Hash that contains the inversions for "first" and "last".
- +
* @type Object
- +
* @default {first:"last", last:"first"}
- +
*/
- +
INVERT_NULLS:{first:"last", last:"first"}
- +
}
- +
}).as(sql, "OrderedExpression");
- +
- 1
+var QualifiedIdentifier = define([GenericExpression, QualifyingMethods], {
- +
instance:{
- +
/**@lends patio.sql.QualifiedIdentifier.prototype*/
- +
- +
/**
- +
* Represents a qualified identifier (column with table or table with schema).
- +
*
- +
* @constructs
- +
* @augments patio.sql.GenericExpression
- +
* @augments patio.sql.QualifyingMethods
- +
*
- +
* @param table the table or schema to qualify the column or table to.
- +
* @param column the column or table to qualify.
- +
*
- +
* @property table <b>READ ONLY</b> the table or schema to qualify the column or table to.
- +
* @property column <b>READ ONLY</b> he column or table to qualify.
- +
*/
- +
constructor:function (table, column) {
- 4451
+this.__table = table;
- 4451
this.__column = column;
},
- -
- -
_getLatestMigrationVersion:function () {
- 13
-var ret = new Promise();
- 13
-if (!isUndefined(this._opts.target)) {
- 5
-ret.callback(this._opts.target);
- -
} else {
- 8
-this._getMigrationFiles().then(hitch(this, function (files) {
- 6
-var l = files[files.length - 1];
- 6
-ret.callback(l ? this.getMigrationVersionFromFile(path.basename(l)) : null);
- -
}), ret);
- -
}
- 13
+return ret.promise();
- +
/**
- +
* Converts the {@link patio.sql.QualifiedIdentifier} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL version of the {@link patio.sql.QualifiedIdentifier}.
- +
*/
- +
toString:function (ds) {
- 4532
+!Dataset && (Dataset = require("./dataset"));
- 4532
+ds = ds || new Dataset();
- 4532
return ds.qualifiedIdentifierSql(this);
},
- -
- -
_getCurrentMigrationVersion:function () {
- 11
-var ret = new Promise();
- 11
-if (!isUndefined(this._opts.current)) {
- 2
-ret.callback(this._opts.current);
- -
} else {
- 9
-when(this._getSchemaDataset()).then(hitch(this, function (ds) {
- 9
-ds.get(this.column).then(ret);
- +
}), ret);
- +
/**@ignore*/
- +
getters:{
- +
table:function () {
- 4554
+return this.__table;
- +
},
- +
- +
column:function () {
- 4559
return this.__column;
- -
}
- 11
-return ret.promise();
- +
},
- +
}
- +
}
}).as(sql, "QualifiedIdentifier");
- -
- -
_setMigrationVersion:function (version) {
- 49
-var ret = new Promise(), c = this.column;
- 49
-this._getSchemaDataset().then(function (ds) {
- 49
-var item = {};
- 49
-item[c] = version;
- 49
-ds.update(item).both(ret);
}, ret);
- -
- 49
-return ret.promise();
- +
},
- 1
+var likeElement = function (re) {
- 875
+var ret;
- 875
+if (isRegExp(re)) {
- 80
+ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase]
- +
} else {
- 795
+ret = [re, false, false];
- +
}
- 875
+return ret;
- +
};
- +
/**
- +
* @class Subclass of {@link patio.sql.ComplexExpression} where the expression results
- +
* in a text/string/varchar value in SQL.
- +
*
- +
* @augments patio.sql.ComplexExpression
- +
* @augments patio.sql.StringMethods
- +
* @augments patio.sql.StringConcatenationMethods
- +
* @augments patio.sql.InequalityMethods
- +
* @augments patio.sql.NoBooleanInputMethods
- +
* @name StringExpression
- +
* @memberOf patio.sql
- +
*/
- 1
+var StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {
- +
static:{
/**@lends patio.sql.StringExpression*/
- -
- -
_getSchemaDataset:function () {
- 58
-var c = this.column, table = this.table;
- 58
-var ret = new Promise();
- 58
-if (!this.__schemaDataset) {
- 11
-var ds = this.db.from(table);
- 11
-this.__createOrAlterMigrationTable().then(hitch(this, function () {
- 11
-ds.isEmpty().then(hitch(this, function (empty) {
- 11
-if (empty) {
- 0
-var item = {};
- 0
-item[c] = -1;
- 0
-this.__schemaDataset = ds;
- 0
-ds.insert(item).then(hitch(ret, "callback", ds), ret);
- -
} else {
- 11
-ds.count().then(hitch(this, function (count) {
- 11
-if (count > 1) {
- 0
-ret.errback(new Error("More than one row in migrator table"));
- -
} else {
- 11
-this.__schemaDataset = ds;
- 11
-ret.callback(ds);
- -
}
- -
}), ret);
- -
}
- -
}), ret);
- -
}), ret);
- -
} else {
- 47
-ret.callback(this.__schemaDataset);
- -
}
- 58
+return ret.promise();
- +
/**
- +
* <p>Creates a SQL pattern match expression. left (l) is the SQL string we
- +
* are matching against, and ces are the patterns we are matching.
- +
* The match succeeds if any of the patterns match (SQL OR).</p>
- +
*
- +
* <p>If a regular expression is used as a pattern, an SQL regular expression will be
- +
* used, which is currently only supported on MySQL and PostgreSQL. Be aware
- +
* that MySQL and PostgreSQL regular expression syntax is similar to javascript
- +
* regular expression syntax, but it not exactly the same, especially for
- +
* advanced regular expression features. Patio just uses the source of the
- +
* regular expression verbatim as the SQL regular expression string.</p>
- +
*
- +
* <p>If any other object is used as a regular expression, the SQL LIKE operator will
- +
* be used, and should be supported by most databases.</p>
- +
*
- +
* <p>The pattern match will be case insensitive if the last argument is a hash
- +
* with a key of caseInsensitive that is not false or null. Also,
- +
* if a case insensitive regular expression is used (//i), that particular
- +
* pattern which will always be case insensitive.</p>
- +
*
- +
* @example
- +
* StringExpression.like(sql.a, 'a%') //=> "a" LIKE 'a%'
- +
* StringExpression.like(sql.a, 'a%', {caseInsensitive : true}) //=> "a" ILIKE 'a%'
- +
* StringExpression.like(sql.a, 'a%', /^a/i) //=> "a" LIKE 'a%' OR "a" ~* '^a'
- +
*/
- +
like:function (l) {
- 437
+var args = argsToArray(arguments, 1);
- 437
+var params = likeElement(l);
- 437
+var likeMap = this.likeMap;
- 437
+var lh = params[0], lre = params[1], lci = params[2];
- 437
+var last = args[args.length - 1];
- 437
+lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;
- 437
+args = args.map(function (ce) {
- 438
+var r, rre, rci;
- 438
+var ceArr = likeElement(ce);
- 438
+r = ceArr[0], rre = ceArr[1], rci = ceArr[2];
- 438
+return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r)
- +
}, this);
- 437
return args.length == 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));
},
- -
- -
__createOrAlterMigrationTable:function () {
- 11
-var c = this.column, table = this.table, db = this.db;
- 11
-var ds = this.db.from(table);
- 11
-var ret = new Promise();
- 11
-db.tableExists(table).then(hitch(this, function (exists) {
- 11
-if (!exists) {
- 6
-db.createTable(table,
- -
function () {
- 6
-this.column(c, "integer", {"default":-1, allowNull:false});
- -
}).then(ret);
- -
} else {
- 5
-ds.columns.then(function (columns) {
- 5
-if (columns.indexOf(c) === -1) {
- 1
-db.addColumn(table, c, "integer", {"default":-1, allowNull:false})
- -
.then(ret);
- -
} else {
- 4
-ret.callback();
- -
}
- -
});
- -
}
- -
}), ret);
- 11
-return ret.promise();
- +
}
- +
/**
- +
* Like map used to by {@link patio.sql.StringExpression.like} to create the
- +
* LIKE expression.
- +
* @type Object
- +
*/
likeMap:{"truetrue":'~*', "truefalse":"~", "falsetrue":"ILIKE", "falsefalse":"LIKE"}
- -
},
- -
- -
static:{
- -
DEFAULT_SCHEMA_COLUMN:"version",
DEFAULT_SCHEMA_TABLE:"schema_info"
- -
}
- -
}).as(exports, "IntegerMigrator");
- +
}).as(sql, "StringExpression");
- -
- -
/**
- -
* @class Migrator that uses the file format {migrationName}.{timestamp}.js, where the timestamp
- -
* can be anything greater than 20000101.
- -
*
- -
* @name TimestampMigrator
- -
* @augments patio.migrations.Migrator
- -
* @memberOf patio.migrations
- -
*/
- 1
+var TimestampMigrator = define(Migrator, {
- 1
var SubScript = define(GenericExpression, {
- +
instance:{
/**@lends patio.sql.SubScript.prototype*/
- -
- -
constructor:function (db, directory, opts) {
- 18
-this._super(arguments);
- 18
-opts = opts || {};
- 18
-this.target = opts.target;
- -
},
- -
- -
run:function () {
- 18
-var ret = new Promise(), DB = this.db, column = this.column;
- 18
-serial([this.__getMirationFiles.bind(this), this._getSchemaDataset.bind(this)]).then(function (res) {
- 18
-var migrations = res[0], ds = res[1];
- 18
-var runMigration = hitch(this, function (index) {
- 86
-if (index >= migrations.length) {
- 16
-ret.callback();
- -
} else {
- 70
-var curr = migrations[index], file = curr[0], migration = curr[1], direction = curr[2];
- 70
-var now = new Date();
- 70
-DB.logInfo("Begin applying migration file %s, direction: %s", file, direction);
- 70
-DB.transaction(hitch(this, function () {
- 70
-var ret = new Promise();
- 70
-when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
- 68
-var fileLowerCase = file.toLowerCase();
- 68
-var query = {};
- 68
-query[column] = fileLowerCase;
- 68
-(direction === "up" ? ds.insert(query) : ds.filter(query).remove()).then(ret);
- -
}), ret);
- 68
-return ret.promise();
- -
})).then(function () {
- 68
-DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);
- 68
-runMigration(index + 1);
- -
}, ret);
- -
}
- -
- -
});
- 18
-runMigration(0);
- -
}, ret);
- 18
-return ret.promise();
- -
},
- -
- -
getFileNames:function () {
- 37
-var ret = new Promise();
- 37
-var sup = this._super(arguments);
- 37
-sup.then(hitch(this, function (files) {
- 37
-ret.callback(files.sort(hitch(this, function (f1, f2) {
- 178
-return this.getMigrationVersionFromFile(f1) - this.getMigrationVersionFromFile(f2);
- -
})));
- -
}), ret);
- 37
-return ret.promise();
- -
},
- -
- -
__getAppliedMigrations:function () {
- 18
-var ret = new Promise();
- 18
-if (!this.__appliedMigrations) {
- 18
-this._getSchemaDataset().then(hitch(this, function (ds) {
- 18
-when(
- -
ds.selectOrderMap(this.column),
- -
this.getFileNames()
- -
).then(hitch(this, function (res) {
- 18
-var appliedMigrations = res[0], files = res[1].map(function (f) {
- 92
-return path.basename(f).toLowerCase();
- -
});
- 18
-var l = appliedMigrations.length;
- 18
-if (l) {
- 9
-for (var i = 0; i < l; i++) {
- 39
-if (files.indexOf(appliedMigrations[i]) == -1) {
- 0
-return ret.errback("Applied migrations file not found in directory " + appliedMigrations[i]);
- -
}
- -
}
- 9
-this.__appliedMigrations = appliedMigrations;
- 9
-ret.callback(appliedMigrations);
- -
} else {
- 9
-this.__appliedMigrations = [];
- 9
-ret.callback([]);
- -
}
- -
}), ret);
- -
}), ret);
- -
} else {
- 0
-ret.callback(this.__appliedMigrations);
- -
}
- 18
+return ret.promise();
- +
/**
- +
* Represents an SQL array access, with multiple possible arguments.
- +
* @constructs
- +
* @augments patio.sql.GenericExpression
- +
*
- +
* @param arrCol the SQL array column
- +
* @param sub The array of subscripts to use (should be an array of numbers)
- +
*/
- +
constructor:function (arrCol, sub) {
- +
//The SQL array column
- 109
+this.__arrCol = arrCol;
- +
//The array of subscripts to use (should be an array of numbers)
- 109
this.__sub = sub;
},
- -
- -
__getMirationFiles:function () {
- 18
-var ret = new Promise();
- 18
-var upMigrations = [], downMigrations = [], target = this.target;
- 18
-if (!this.__migrationFiles) {
- 18
-when(
- -
this.getFileNames(),
- -
this.__getAppliedMigrations()
- -
).then(hitch(this, function (res) {
- 18
-var files = res[0], appliedMigrations = res[1];
- 18
-var l = files.length, inserts = [];
- 18
-if (l > 0) {
- 18
-try {
- 18
-for (var i = 0; i < l; i++) {
- 92
-var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
- 92
-if (!isUndefined(target)) {
- 48
-var version = this.getMigrationVersionFromFile(f);
- 48
-if (version > target || (version === 0 && target === version)) {
- 35
-if (appliedMigrations.indexOf(fLowerCase) != -1) {
- 26
-downMigrations.push([f, require(file), "down"]);
- -
}
- 13
-} else if (appliedMigrations.indexOf(fLowerCase) == -1) {
- 9
-upMigrations.push([f, require(file), "up"]);
- -
}
- 44
-} else if (appliedMigrations.indexOf(fLowerCase) == -1) {
- 35
-upMigrations.push([f, require(file), "up"]);
- -
}
- -
}
- -
} catch (e) {
- 0
-return ret.errback(e)
- -
}
- 18
-this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
- 18
-ret.callback(this.__migrationFiles);
- -
} else {
- 0
-return ret.callback();
- -
}
- -
}), ret);
- -
} else {
- 0
-ret.callback(this.__migrationFiles);
- -
}
- 18
+return ret.promise();
- +
/**
- +
* Create a new {@link patio.sql.Subscript} appending the given subscript(s)
- +
* the the current array of subscripts.
- +
*/
- +
addSub:function (sub) {
- 0
return new SubScript(this.__arrCol, this.__sub.concat(sub));
},
- -
- -
- -
// Returns the dataset for the schema_migrations table. If no such table
- -
// exists, it is automatically created.
- -
_getSchemaDataset:function () {
- 36
-var ret = new Promise();
- 36
-if (!this.__schemaDataset) {
- 18
-var ds = this.db.from(this.table);
- 18
-this.__createTable().then(hitch(this, function () {
- 18
-this.__schemaDataset = ds;
- 18
-ret.callback(ds);
- -
}), ret);
- -
} else {
- 18
-ret.callback(this.__schemaDataset);
- -
}
- 36
+return ret.promise();
- +
/**
- +
* Converts the {@link patio.sql.SubScript} to a string.
- +
*
- +
* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if
- +
* the dataset is ommited then the default {@link patio.Dataset} implementation is used.
- +
*
- +
* @return String the SQL version of the {@link patio.sql.SubScript}.
- +
*/
- +
toString:function (ds) {
- 109
+!Dataset && (Dataset = require("./dataset"));
- 109
+ds = ds || new Dataset();
- 109
return ds.subscriptSql(this);
},
- -
- -
__convertSchemaInfo:function () {
- 1
-var ret = new Promise(), c = this.column;
- 1
-var ds = this.db.from(this.table);
- 1
-this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
- 1
-this.getFileNames().then(hitch(this, function (files) {
- 1
-var l = files.length, inserts = [];
- 1
-if (l > 0) {
- 1
-for (var i = 0; i < l; i++) {
- 7
-var f = path.basename(files[i]);
- 7
-if (this.getMigrationVersionFromFile(f) <= version) {
- 5
-var insert = {};
- 5
-insert[c] = f;
- 5
-inserts.push(ds.insert(insert));
- -
}
- -
}
- -
}
- 1
-if (inserts.length) {
- 1
-when.apply(comb, inserts).then(ret);
- -
} else {
- 0
-ret.callback();
- -
}
- -
}), ret);
- -
}), ret);
- 1
-return ret.promise();
- -
- +
},
- +
/**@ignore*/
- +
getters:{
- +
f:function () {
- 110
+return this.__arrCol;
},
- -
- -
__createTable:function () {
- 18
-var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
- 18
-var ds = this.db.from(table);
- 18
-var ret = new Promise();
- 18
-when(
- -
db.tableExists(table),
- -
db.tableExists(intMigrationTable)
- -
).then(hitch(this, function (res) {
- 18
-var exists = res[0], intMigratorExists = res[1];
- 18
-if (!exists) {
- 10
-db.createTable(table,
- -
function () {
- 10
-this.column(c, String, {primaryKey:true});
- -
}).addErrback(ret);
- 10
-if (intMigratorExists) {
- 1
-db.from(intMigrationTable).all().then(hitch(this, function (versions) {
- 1
-var version;
- 1
-if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
- 1
-this.__convertSchemaInfo().then(ret);
- -
} else {
- 0
-ret.callback();
- -
}
- -
}));
- -
} else {
- 9
-ret.callback();
- -
}
- -
} else {
- 8
-ds.columns.then(hitch(this, function (columns) {
- 8
-if (columns.indexOf(c) === -1) {
- 0
-ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
- -
} else {
- 8
-ret.callback();
- -
}
- -
}), ret);
- -
}
- -
}), ret);
- 18
+return ret.promise();
- +
sub:function () {
- 110
+return this.__sub;
}
- -
}
- +
},
- +
}
}).as(sql, "SubScript");
- -
- -
static:{
- -
DEFAULT_SCHEMA_COLUMN:"filename",
- +
DEFAULT_SCHEMA_TABLE:"schema_migrations"
- 1
+var STRING_METHODS = ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "quote",
- +
"replace", "search", "slice", "split", "substr", "substring", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase",
- +
"toSource", "toString", "toUpperCase", "trim", "trimLeft", "trimRight", "valueOf"];
- +
- +
- 1
+var addStringMethod = function (op) {
- 24
+return function () {
- 4294
return this.__str[op].apply(this.__str, arguments);
- -
}
- +
}).as(exports, "TimestampMigrator");
};
- -
- 1
-exports.run = function () {
- 31
-return Migrator.run.apply(Migrator, arguments);
};
var LiteralString = define([OrderedMethods, ComplexExpressionMethods, BooleanMethods, NumericMethods, StringMethods, InequalityMethods, AliasMethods], {
instance:{
/**@lends patio.sql.LiteralString*/
/**
* Represents a string that should be placed into a SQL query literally.
* <b>This class has all methods that a normal javascript String has.</b>
* @constructs
* @augments patio.sql.OrderedMethods
* @augments patio.sql.ComplexExpressionMethods
* @augments patio.sql.BooleanMethods
* @augments patio.sql.NumericMethods
* @augments patio.sql.StringMethods
* @augments patio.sql.InequalityMethods
* @augments patio.sql.AliasMethods
*
* @param {String} str the literal string.
*/
constructor:function (str) {
this.__str = str;
}
}
}).as(sql, "LiteralString");
STRING_METHODS.forEach(function (op) {
LiteralString.prototype[op] = addStringMethod(op);
}, this);
- dataset/index.js
+ migration.js
|
- Coverage92.54
- SLOC439
- LOC67
- Missed5
+ Coverage91.97
+ SLOC607
+ LOC274
+ Missed22
|
- 1
+var comb = require("comb"),
+
- 1
var comb = require("comb"),
- -
hitch = comb.hitch,
- -
logging = comb.logging,
- -
Logger = logging.Logger,
- -
errors = require("../errors"),
- -
QueryError = errors.QueryError,
DatasetError = errors.DatasetError,
- -
Promise = comb.Promise,
- -
PromiseList = comb.PromiseList,
- -
isUndefined = comb.isUndefined,
- -
isUndefinedOrNull = comb.isUndefinedOrNull,
- -
isString = comb.isString,
- -
isInstanceOf = comb.isInstanceOf,
- -
isString = comb.isString,
- -
isFunction = comb.isFunction,
- -
isNull = comb.isNull,
- +
merge = comb.merge,
- +
errors = require("./errors"),
- +
MigrationError = errors.MigrationError,
- +
NotImplemented = errors.NotImplemented(),
format = comb.string.format,
- -
define = comb.define,
- -
graph = require("./graph"),
- -
actions = require("./actions"),
- -
features = require("./features"),
- -
query = require("./query"),
- -
sql = require("./sql"),
- -
SQL = require("../sql").sql,
- -
AliasedExpression = SQL.AliasedExpression,
- -
Identifier = SQL.Identifier,
- +
QualifiedIdentifier = SQL.QualifiedIdentifier;
- +
isFunction = comb.isFunction,
- +
serial = comb.serial,
- +
isNumber = comb.isNumber,
- +
when = comb.when,
- +
isUndefined = comb.isUndefined,
- +
fs = require("fs"),
path = require("path");
- -
- 1
-var LOGGER = comb.logger("patio.Dataset");
- 1
+define([actions, graph, features, query, sql], {
- 1
var Migrator = define(null, {
- -
instance:{
- -
- -
/**@lends patio.Dataset.prototype*/
- -
- -
/**
- -
* Class that is used for querying/retirving datasets from a database.
- -
*
- -
* <p> Dynamically genertated methods include
- -
* <ul>
- -
* <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
- -
* {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
- -
* to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
- -
* {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
- -
* <ul>
- -
* <li>Conditioned join types that accept conditions.
- -
* <ul>
- -
* <li>inner - INNER JOIN</li>
- -
* <li>fullOuter - FULL OUTER</li>
- -
* <li>rightOuter - RIGHT OUTER JOIN</li>
- -
* <li>leftOuter - LEFT OUTER JOIN</li>
- -
* <li>full - FULL JOIN</li>
- -
* <li>right - RIGHT JOIN</li>
- -
* <li>left - LEFT JOIN</li>
- -
* </ul>
- -
* </li>
- -
* <li>Unconditioned join types that do not accept join conditions
- -
* <ul>
- -
* <li>natural - NATURAL JOIN</li>
- -
* <li>naturalLeft - NATURAL LEFT JOIN</li>
- -
* <li>naturalRight - NATURAL RIGHT JOIN</li>
- -
* <li>naturalFull - NATURA FULLL JOIN</li>
- -
* <li>cross - CROSS JOIN</li>
- -
* </ul>
- -
* </li>
- -
* </ul>
- -
* </li>
- -
* </li>
- -
* </ul>
- -
*
- -
* <p>
- -
* <h4>Features:</h4>
- -
* <p>
- -
* Features that a particular {@link patio.Dataset} supports are shown in the example below.
- -
* If you wish to implement an adapter please override these values depending on the database that
- -
* you are developing the adapter for.
- -
* </p>
- -
* <pre class="code">
- -
* var ds = DB.from("test");
- -
*
- -
* //The default values returned
- -
*
- -
* //Whether this dataset quotes identifiers.
- -
* //Whether this dataset quotes identifiers.
- -
* ds.quoteIdentifiers //=>true
- -
*
- -
* //Whether this dataset will provide accurate number of rows matched for
- -
* //delete and update statements. Accurate in this case is the number of
- -
* //rows matched by the dataset's filter.
- -
* ds.providesAccurateRowsMatched; //=>true
- -
*
- -
* //Times Whether the dataset requires SQL standard datetimes (false by default,
- -
* // as most allow strings with ISO 8601 format).
- -
* ds.requiresSqlStandardDate; //=>false
- -
*
- -
* //Whether the dataset supports common table expressions (the WITH clause).
- -
* ds.supportsCte; //=>true
- -
*
- -
* //Whether the dataset supports the DISTINCT ON clause, false by default.
- -
* ds.supportsDistinctOn; //=>false
- -
*
- -
* //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
- -
* ds.supportsIntersectExcept; //=>true
- -
*
- -
* //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
- -
* ds.supportsIntersectExceptAll; //=>true
- -
*
- -
* //Whether the dataset supports the IS TRUE syntax.
- -
* ds.supportsIsTrue; //=>true
- -
*
- -
* //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
- -
* ds.supportsJoinUsing; //=>true
- -
*
- -
* //Whether modifying joined datasets is supported.
- -
* ds.supportsModifyingJoin; //=>false
- -
*
- -
* //Whether the IN/NOT IN operators support multiple columns when an
- -
* ds.supportsMultipleColumnIn; //=>true
- -
*
- -
* //Whether the dataset supports timezones in literal timestamps
- -
* ds.supportsTimestampTimezone; //=>false
- -
*
- -
* //Whether the dataset supports fractional seconds in literal timestamps
- -
* ds.supportsTimestampUsecs; //=>true
- -
*
- -
* //Whether the dataset supports window functions.
- -
* ds.supportsWindowFunctions; //=>false
- -
* </pre>
- -
* <p>
- -
* <p>
- -
* <h4>Actions</h4>
- -
* <p>
- -
* Each dataset does not actually send any query to the database until an action method has
- -
* been called upon it(with the exception of {@link patio.Dataset#graph} because columns
- -
* from the other table might need retrived in order to set up the graph). Each action
- -
* returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
- -
* that you account for errors otherwise it can be difficult to track down issues.
- -
* The list of action methods is:
- -
* <ul>
- -
* <li>{@link patio.Dataset#all}</li>
- -
* <li>{@link patio.Dataset#one}</li>
- -
* <li>{@link patio.Dataset#avg}</li>
- -
* <li>{@link patio.Dataset#count}</li>
- -
* <li>{@link patio.Dataset#columns}</li>
- -
* <li>{@link patio.Dataset#remove}</li>
- -
* <li>{@link patio.Dataset#forEach}</li>
- -
* <li>{@link patio.Dataset#empty}</li>
- -
* <li>{@link patio.Dataset#first}</li>
- -
* <li>{@link patio.Dataset#get}</li>
- -
* <li>{@link patio.Dataset#import}</li>
- -
* <li>{@link patio.Dataset#insert}</li>
- -
* <li>{@link patio.Dataset#save}</li>
- -
* <li>{@link patio.Dataset#insertMultiple}</li>
- -
* <li>{@link patio.Dataset#saveMultiple}</li>
- -
* <li>{@link patio.Dataset#interval}</li>
- -
* <li>{@link patio.Dataset#last}</li>
- -
* <li>{@link patio.Dataset#map}</li>
- -
* <li>{@link patio.Dataset#max}</li>
- -
* <li>{@link patio.Dataset#min}</li>
- -
* <li>{@link patio.Dataset#multiInsert}</li>
- -
* <li>{@link patio.Dataset#range}</li>
- -
* <li>{@link patio.Dataset#selectHash}</li>
- -
* <li>{@link patio.Dataset#selectMap}</li>
- -
* <li>{@link patio.Dataset#selectOrderMap}</li>
- -
* <li>{@link patio.Dataset#set}</li>
- -
* <li>{@link patio.Dataset#singleRecord}</li>
- -
* <li>{@link patio.Dataset#singleValue}</li>
- -
* <li>{@link patio.Dataset#sum}</li>
- -
* <li>{@link patio.Dataset#toCsv}</li>
- -
* <li>{@link patio.Dataset#toHash}</li>
- -
* <li>{@link patio.Dataset#truncate}</li>
- -
* <li>{@link patio.Dataset#update}</li>
- -
* </ul>
- -
*
- -
* </p>
- +
* </p>
- +
/**@lends patio.migrations.Migrator.prototype*/
- +
column:null,
- +
db:null,
- +
directory:null,
- +
ds:null,
- +
files:null,
- +
table:null,
- +
target:null,
- +
- +
/**
* Abstract Migrator class. This class should be be instantiated directly.
*
- +
* @constructs
- +
* @param {patio.Database} db the database to migrate
- +
* @param {String} directory directory that the migration files reside in
- +
* @param {Object} [opts={}] optional parameters.
- +
* @param {String} [opts.column] the column in the table that version information should be stored.
- +
* @param {String} [opts.table] the table that version information should be stored.
- +
* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
- +
* @param {String} [opts.current] the version that the database is currently at if the current version
- +
*/
- +
constructor:function (db, directory, opts) {
- 31
+this.db = db;
- 31
+this.directory = directory;
- 31
+opts = opts || {};
- 31
+this.table = opts.table || this._static.DEFAULT_SCHEMA_TABLE;
- 31
+this.column = opts.column || this._static.DEFAULT_SCHEMA_COLUMN;
- 31
+this._opts = opts;
- +
},
- +
- +
/**
- +
* Runs the migration and returns a promise.
- +
*/
- +
run:function () {
- 0
+throw new NotImplemented("patio.migrations.Migrator#run");
- +
},
- +
- +
getFileNames:function () {
- 50
+if (!this.__files) {
- 49
+return this._static.getFileNames(this.directory).addCallback(hitch(this, function (files) {
- 49
+this.__files = files;
- +
}));
- +
} else {
- 1
+return new Promise().callback(this.__files).promise();
- +
}
- +
},
- +
- +
getMigrationVersionFromFile:function (filename) {
- 476
+return parseInt(path.basename(filename).split(this._static.MIGRATION_SPLITTER)[0], 10);
- +
}
- +
},
- +
- +
"static":{
- +
/**@lends patio.migrations.Migrator*/
- +
- +
MIGRATION_FILE_PATTERN:/^\d+\..+\.js$/i,
- +
MIGRATION_SPLITTER:'.',
- +
MINIMUM_TIMESTAMP:20000101,
- +
- +
getFileNames:function (directory) {
- 80
+var ret = new Promise();
- 80
+fs.readdir(directory, hitch(this, function (err, files) {
- 80
+if (err) {
- 0
+ret.errback(err);
- +
} else {
- 80
+files = files.filter(function (file) {
- 394
+return file.match(this.MIGRATION_FILE_PATTERN) !== null;
- +
}, this);
- 80
+ret.callback(files.map(function (file) {
- 394
+return path.resolve(directory, file);
- +
}));
- +
}
- +
}));
- 80
+return ret.promise();
- +
},
- +
- +
/**
- +
* Migrates the database using migration files found in the supplied directory.
* See {@link patio#migrate}
- +
*
- +
* @example
- +
* var DB = patio.connect("my://connection/string");
- +
* patio. migrate(DB, __dirname + "/timestamp_migration").then(function(){
- +
* console.log("done migrating!");
* });
- -
*
- -
* @param {patio.Database} db the database this dataset should use when querying for data.
- -
* @param {Object} opts options to set on this dataset instance
- -
*
- -
* @property {Function} rowCb callback to be invoked for each row returned from the database.
- -
* the return value will be used as the result of query. The rowCb can also return a promise,
- -
* The resolved value of the promise will be used as result.
- -
*
- -
* @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
- -
* This value will be defaulted to whatever the identifierInputMethod
- -
* is on the database used in initialization.
- -
*
- -
* @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
- -
* This value will be defaulted to whatever the identifierOutputMethod
- -
* is on the database used in initialization.
- -
* @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
- -
* throws a {patio.DatasetError} tf the dataset doesn't have a table.
- -
* <pre class="code">
- -
* DB.from("table").firstSourceAlias;
- -
* //=> "table"
- -
*
- -
* DB.from("table___t").firstSourceAlias;
- -
* //=> "t"
- +
* </pre>
- +
* patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).then(function(){
- +
* console.log("done migrating down!");
* });
- -
*
- -
* @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
- -
* have a table, raises a {@link patio.erros.DatasetError}.
*<pre class="code">
- -
*
- -
* DB.from("table").firstSourceTable;
- +
* //=> "table"
- +
* @param {patio.Database} db the database to migrate
- +
* @param {String} directory directory that the migration files reside in
- +
* @param {Object} [opts={}] optional parameters.
- +
* @param {String} [opts.column] the column in the table that version information should be stored.
- +
* @param {String} [opts.table] the table that version information should be stored.
- +
* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).
- +
* @param {String} [opts.current] the version that the database is currently at if the current version
* is not provided it is retrieved from the database.
- -
*
- -
* DB.from("table___t").firstSourceTable;
- -
* //=> "t"
- -
* </pre>
- -
* @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
- -
* <pre class="code">
- -
* DB.from("items").isSimpleSelectAll; //=> true
- -
* DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
- -
* </pre>
- -
* @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
- -
* @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
- -
* delete and update statements. Accurate in this case is the number of
- -
* rows matched by the dataset's filter.
- -
* @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
- -
* as most allow strings with ISO 8601 format).
- -
* @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
- -
* @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
- -
* @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
- -
* @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
- -
* @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
- -
* @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
- -
* @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
- -
* @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
- -
* @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
- -
* @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
- +
* @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
* @return {Promise} a promise that is resolved once the migration is complete.
- -
*/
- -
constructor:function (db, opts) {
- 29216
-this._super(arguments);
- 29216
-this.db = db;
- 29216
-this.__opts = {};
- 29216
-this.__rowCb = null;
- 29216
-if (db) {
- 15039
-this.__quoteIdentifiers = db.quoteIdentifiers;
- 15039
-this.__identifierInputMethod = db.identifierInputMethod;
- 15039
+this.__identifierOutputMethod = db.identifierOutputMethod;
- +
run:function (db, directory, opts, cb) {
- 31
+if (isFunction(opts)) {
- 0
+cb = opts;
- 0
+opts = {};
- +
} else {
- 31
+opts = opts || {};
- +
}
- 31
+opts = opts || {};
- 31
+var ret = new Promise();
- 31
+this.__getMigrator(directory).then(function (migrator) {
- 31
+new migrator(db, directory, opts).run().then(ret);
- +
}, ret);
- 31
+ret.classic(cb);
- 31
+return ret.promise();
- +
},
- +
- +
// Choose the Migrator subclass to use. Uses the TimestampMigrator
- +
// // if the version number appears to be a unix time integer for a year
- +
// after 2005, otherwise uses the IntegerMigrator.
- +
__getMigrator:function (directory) {
- 31
+var ret = new Promise();
- 31
+var retClass = IntegerMigrator;
- 31
+this.getFileNames(directory).then(hitch(this, function (files) {
- 31
+var l = files.length;
- 31
+if (l) {
- 31
+for (var i = 0; i < l; i++) {
- 81
+var file = files[i];
- 81
+if (parseInt(path.basename(file).split(this.MIGRATION_SPLITTER)[0], 10) > this.MINIMUM_TIMESTAMP) {
- 18
+retClass = TimestampMigrator;
- 18
+break;
- +
}
- +
}
- +
}
- 31
+ret.callback(retClass);
- +
- +
}), ret);
- 31
+return ret.promise();
- +
}
- +
}
- +
});
- +
- +
- +
/**
- +
* @class Migrator that uses the file format {migrationName}.{version}.js, where version starts at 0.
- +
* <b>Missing migrations are not allowed</b>
- +
*
- +
* @augments patio.migrations.Migrator
- +
* @name IntegerMigrator
- +
* @memberOf patio.migrations
- +
*/
- 1
+var IntegerMigrator = define(Migrator, {
- +
instance:{
- +
/**@lends patio.migrations.IntegerMigrator.prototype*/
- +
current:null,
- +
direction:null,
- +
migrations:null,
- +
- +
_migrationFiles:null,
- +
- +
run:function () {
- 13
+var ret = new Promise(), DB = this.db;
- 13
+serial([this._getLatestMigrationVersion.bind(this), this._getCurrentMigrationVersion.bind(this)]).then(hitch(this, function (res) {
- 11
+var target = res[0], current = res[1];
- 11
+if (current !== target) {
- 11
+var direction = this.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;
- 11
+this._getMigrations(current, target, direction).then(hitch(this, function (migrations) {
- 11
+var runMigration = hitch(this, function (index) {
- 60
+if (index >= migrations.length) {
- 11
+ret.callback(version);
- +
} else {
- 49
+var curr = migrations[index], migration = curr[0];
- 49
+version = curr[1];
- 49
+var now = new Date();
- 49
+var lv = isUp ? version : version - 1;
- 49
+DB.logInfo("Begin applying migration version %d, direction: %s", lv, direction);
- 49
+DB.transaction(hitch(this, function () {
- 49
+var ret = new Promise();
- 49
+if (!isFunction(migration[direction])) {
- 0
+this._setMigrationVersion(lv).then(ret);
- +
} else {
- 49
+when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
- 49
+this._setMigrationVersion(lv).then(ret);
- +
}), ret);
- +
}
- 49
+return ret.promise();
- +
})).then(function () {
- 49
+DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);
- 49
+runMigration(index + 1);
- +
}, ret);
- +
}
- +
- +
});
- 11
+runMigration(0);
- +
}), ret);
- +
} else {
- 0
+ret.callback(target);
- +
}
- +
- +
}), ret);
- 13
+return ret.promise();
- +
},
- +
- +
_getMigrations:function (current, target, direction) {
- 11
+var ret = new Promise(), isUp = direction === "up", migrations = [];
- 11
+when(this._getMigrationFiles()).then(function (files) {
- 11
+try {
- 11
+if ((isUp ? target : current - 1) < files.length) {
- 11
+if (isUp) {
- 8
+current++;
- +
}
- 11
+for (; isUp ? current <= target : current > target; isUp ? current++ : current--) {
- 49
+migrations.push([require(files[current]), current]);
- +
}
- +
} else {
- 0
+return ret.errback(new MigrationError("Invalid target " + target));
- +
}
- +
} catch (e) {
- 0
+return ret.errback(e);
- +
}
- 11
+ret.callback(migrations);
- +
}, ret);
- 11
+return ret.promise();
- +
},
- +
- +
- +
_getMigrationFiles:function () {
- 19
+var ret = new Promise();
- 19
+if (!this._migrationFiles) {
- 13
+var retFiles = [];
- 13
+var directory = this.directory;
- 13
+this.getFileNames().then(hitch(this, function (files) {
- 13
+var l = files.length;
- 13
+if (l) {
- 13
+for (var i = 0; i < l; i++) {
- 59
+var file = files[i];
- 59
+var version = this.getMigrationVersionFromFile(file);
- 59
+if (isUndefined(retFiles[version])) {
- 58
+retFiles[version] = file;
- +
} else {
- 1
+return ret.errback(new MigrationError("Duplicate migration number " + version));
- +
}
- +
}
- 12
+if (isUndefined(retFiles[0])) {
- 0
+retFiles.shift();
- +
}
- 12
+for (var j = 0; j < l; j++) {
- 57
+if (isUndefined(retFiles[j])) {
- 1
+return ret.errback(new MigrationError("Missing migration for " + j));
- +
}
- +
}
- +
}
- 11
+this._migrationFiles = retFiles;
- 11
+ret.callback(retFiles);
- +
}), ret);
- +
} else {
- 6
ret.callback(this._migrationFiles);
- +
}
- 19
return ret.promise();
},
- +
- +
_getLatestMigrationVersion:function () {
- 13
+var ret = new Promise();
- 13
+if (!isUndefined(this._opts.target)) {
- 5
+ret.callback(this._opts.target);
- +
} else {
- 8
+this._getMigrationFiles().then(hitch(this, function (files) {
- 6
+var l = files[files.length - 1];
- 6
+ret.callback(l ? this.getMigrationVersionFromFile(path.basename(l)) : null);
- +
}), ret);
- +
}
- 13
+return ret.promise();
},
- -
- -
/**
- -
* Returns a new clone of the dataset with with the given options merged into the current datasets options.
- -
* If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
- -
* columns are deleted. This method should generally not be called
- -
* directly by user code.
- -
*
- -
* @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
- -
* @return [patio.Dataset] a cloned dataset with the merged options
- -
**/
- -
mergeOptions:function (opts) {
- 14948
-opts = isUndefined(opts) ? {} : opts;
- 14948
-var ds = new this._static(this.db, {});
- 14948
-ds.rowCb = this.rowCb;
- 14948
-this._static.FEATURES.forEach(function (f) {
- 209272
-ds[f] = this[f];
- -
}, this);
- 14948
-ds.__opts = merge({}, this.__opts, opts);
- 14948
-ds.identifierInputMethod = this.identifierInputMethod;
- 14948
-ds.identifierOutputMethod = this.identifierOutputMethod;
- 14948
-var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
- 14948
-if (Object.keys(opts).some(function (o) {
- 13536
-return columnChangeOpts.indexOf(o) != -1;
- -
})) {
- 2456
+ds.__opts.columns = null;
- +
_getCurrentMigrationVersion:function () {
- 11
+var ret = new Promise();
- 11
+if (!isUndefined(this._opts.current)) {
- 2
+ret.callback(this._opts.current);
- +
} else {
- 9
+when(this._getSchemaDataset()).then(hitch(this, function (ds) {
- 9
+ds.get(this.column).then(ret);
}), ret);
- -
}
- 14948
+return ds;
- 11
return ret.promise();
},
- +
- +
_setMigrationVersion:function (version) {
- 49
+var ret = new Promise(), c = this.column;
- 49
+this._getSchemaDataset().then(function (ds) {
- 49
+var item = {};
- 49
+item[c] = version;
- 49
+ds.update(item).both(ret);
}, ret);
- -
- -
/**
- -
* Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
- -
* or {@link patio.sql.AliasedExpression}, depending on the format:
- -
*
- -
* <ul>
- -
* <li>For columns : table__column___alias.</li>
- -
* <li>For tables : schema__table___alias.</li>
- -
* </ul>
- -
* each portion of the identifier is optional. See example below
- -
*
- -
* @example
- -
*
- -
* ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
- -
* ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
- -
* ds.stringToIdentifier("table__column___alias");
- -
* //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
- -
*
- -
* @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
- -
* or {@link patio.sql.AliasedExpression}.
- -
*
- -
* @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
- -
*/
- -
stringToIdentifier:function (name) {
- 15093
-if (isString(name)) {
- 10138
-var parts = this._splitString(name);
- 10138
-var schema = parts[0], table = parts[1], alias = parts[2];
- 10138
-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))));
- 49
+return ret.promise();
- +
},
- +
- +
_getSchemaDataset:function () {
- 58
+var c = this.column, table = this.table;
- 58
+var ret = new Promise();
- 58
+if (!this.__schemaDataset) {
- 11
+var ds = this.db.from(table);
- 11
+this.__createOrAlterMigrationTable().then(hitch(this, function () {
- 11
+ds.isEmpty().then(hitch(this, function (empty) {
- 11
+if (empty) {
- 0
+var item = {};
- 0
+item[c] = -1;
- 0
+this.__schemaDataset = ds;
- 0
+ds.insert(item).then(hitch(ret, "callback", ds), ret);
- +
} else {
- 11
+ds.count().then(hitch(this, function (count) {
- 11
+if (count > 1) {
- 0
+ret.errback(new Error("More than one row in migrator table"));
- +
} else {
- 11
+this.__schemaDataset = ds;
- 11
+ret.callback(ds);
- +
}
- +
}), ret);
- +
}
- +
}), ret);
- +
}), ret);
- +
} else {
- 47
+ret.callback(this.__schemaDataset);
- +
}
- 58
+return ret.promise();
- +
},
- +
- +
__createOrAlterMigrationTable:function () {
- 11
+var c = this.column, table = this.table, db = this.db;
- 11
+var ds = this.db.from(table);
- 11
+var ret = new Promise();
- 11
+db.tableExists(table).then(hitch(this, function (exists) {
- 11
+if (!exists) {
- 6
+db.createTable(table,
- +
function () {
- 6
+this.column(c, "integer", {"default":-1, allowNull:false});
- +
}).then(ret);
- +
} else {
- 5
+ds.columns.then(function (columns) {
- 5
+if (columns.indexOf(c) === -1) {
- 1
+db.addColumn(table, c, "integer", {"default":-1, allowNull:false})
- +
.then(ret);
- +
} else {
- 4
+ret.callback();
- +
}
- +
});
- +
}
- +
}), ret);
- 11
+return ret.promise();
- +
}
- +
- +
},
- +
- +
static:{
- +
DEFAULT_SCHEMA_COLUMN:"version",
- +
DEFAULT_SCHEMA_TABLE:"schema_info"
- +
}
- +
}).as(exports, "IntegerMigrator");
- +
- +
- +
/**
- +
* @class Migrator that uses the file format {migrationName}.{timestamp}.js, where the timestamp
- +
* can be anything greater than 20000101.
- +
*
- +
* @name TimestampMigrator
- +
* @augments patio.migrations.Migrator
- +
* @memberOf patio.migrations
- +
*/
- 1
+var TimestampMigrator = define(Migrator, {
- +
instance:{
- +
- +
constructor:function (db, directory, opts) {
- 18
+this._super(arguments);
- 18
+opts = opts || {};
- 18
+this.target = opts.target;
- +
},
- +
- +
run:function () {
- 18
+var ret = new Promise(), DB = this.db, column = this.column;
- 18
+serial([this.__getMirationFiles.bind(this), this._getSchemaDataset.bind(this)]).then(function (res) {
- 18
+var migrations = res[0], ds = res[1];
- 18
+var runMigration = hitch(this, function (index) {
- 86
+if (index >= migrations.length) {
- 16
+ret.callback();
- +
} else {
- 70
+var curr = migrations[index], file = curr[0], migration = curr[1], direction = curr[2];
- 70
+var now = new Date();
- 70
+DB.logInfo("Begin applying migration file %s, direction: %s", file, direction);
- 70
+DB.transaction(hitch(this, function () {
- 70
+var ret = new Promise();
- 70
+when(migration[direction].apply(DB, [DB])).then(hitch(this, function (args) {
- 68
+var fileLowerCase = file.toLowerCase();
- 68
+var query = {};
- 68
+query[column] = fileLowerCase;
- 68
+(direction === "up" ? ds.insert(query) : ds.filter(query).remove()).then(ret);
- +
}), ret);
- 68
+return ret.promise();
- +
})).then(function () {
- 68
+DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);
- 68
+runMigration(index + 1);
- +
}, ret);
- +
}
- +
- +
});
- 18
+runMigration(0);
- +
}, ret);
- 18
+return ret.promise();
- +
},
- +
- +
getFileNames:function () {
- 37
+var ret = new Promise();
- 37
+var sup = this._super(arguments);
- 37
+sup.then(hitch(this, function (files) {
- 37
+ret.callback(files.sort(hitch(this, function (f1, f2) {
- 178
+return this.getMigrationVersionFromFile(f1) - this.getMigrationVersionFromFile(f2);
- +
})));
- +
}), ret);
- 37
+return ret.promise();
- +
},
- +
- +
__getAppliedMigrations:function () {
- 18
+var ret = new Promise();
- 18
+if (!this.__appliedMigrations) {
- 18
+this._getSchemaDataset().then(hitch(this, function (ds) {
- 18
+when(
- +
ds.selectOrderMap(this.column),
- +
this.getFileNames()
- +
).then(hitch(this, function (res) {
- 18
+var appliedMigrations = res[0], files = res[1].map(function (f) {
- 92
+return path.basename(f).toLowerCase();
- +
});
- 18
+var l = appliedMigrations.length;
- 18
+if (l) {
- 9
+for (var i = 0; i < l; i++) {
- 39
+if (files.indexOf(appliedMigrations[i]) == -1) {
- 0
+return ret.errback("Applied migrations file not found in directory " + appliedMigrations[i]);
- +
}
- +
}
- 9
+this.__appliedMigrations = appliedMigrations;
- 9
+ret.callback(appliedMigrations);
- +
} else {
- 9
+this.__appliedMigrations = [];
- 9
+ret.callback([]);
- +
}
- +
}), ret);
}), ret);
- -
} else {
- 4955
+return name;
- 0
ret.callback(this.__appliedMigrations);
- +
}
- 18
return ret.promise();
},
- -
- -
/**
- -
* Can either be a string or null.
- -
*
- -
*
- -
* @example
- -
* //columns
- -
* table__column___alias //=> table.column as alias
- -
* table__column //=> table.column
- -
* //tables
- -
* schema__table___alias //=> schema.table as alias
- -
* schema__table //=> schema.table
- -
*
- -
* //name and alias
- -
* columnOrTable___alias //=> columnOrTable as alias
- -
*
- -
*
- -
*
- -
* @return {String[]} an array with the elements being:
- -
* <ul>
- -
* <li>For columns :[table, column, alias].</li>
- -
* <li>For tables : [schema, table, alias].</li>
- -
* </ul>
- -
*/
- -
_splitString:function (s) {
- 19662
-var ret, m;
- 19662
-if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
- 189
-ret = m.slice(1);
- -
}
- 19473
-else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
- 28
-ret = [null, m[1], m[2]];
- -
}
- 19445
-else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
- 2079
-ret = [m[1], m[2], null];
- -
}
- -
else {
- 17366
+ret = [null, s, null];
- +
__getMirationFiles:function () {
- 18
+var ret = new Promise();
- 18
+var upMigrations = [], downMigrations = [], target = this.target;
- 18
+if (!this.__migrationFiles) {
- 18
+when(
- +
this.getFileNames(),
- +
this.__getAppliedMigrations()
- +
).then(hitch(this, function (res) {
- 18
+var files = res[0], appliedMigrations = res[1];
- 18
+var l = files.length, inserts = [];
- 18
+if (l > 0) {
- 18
+try {
- 18
+for (var i = 0; i < l; i++) {
- 92
+var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
- 92
+if (!isUndefined(target)) {
- 48
+var version = this.getMigrationVersionFromFile(f);
- 48
+if (version > target || (version === 0 && target === version)) {
- 35
+if (appliedMigrations.indexOf(fLowerCase) != -1) {
- 26
+downMigrations.push([f, require(file), "down"]);
- +
}
- 13
+} else if (appliedMigrations.indexOf(fLowerCase) == -1) {
- 9
+upMigrations.push([f, require(file), "up"]);
- +
}
- 44
+} else if (appliedMigrations.indexOf(fLowerCase) == -1) {
- 35
+upMigrations.push([f, require(file), "up"]);
- +
}
- +
}
- +
} catch (e) {
- 0
+return ret.errback(e)
- +
}
- 18
+this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
- 18
+ret.callback(this.__migrationFiles);
- +
} else {
- 0
+return ret.callback();
- +
}
- +
}), ret);
- +
} else {
- 0
ret.callback(this.__migrationFiles);
- -
}
- 19662
+return ret;
- 18
return ret.promise();
},
- -
- -
/**
- -
* @ignore
- -
**/
- -
getters:{
- -
- -
rowCb:function () {
- 23052
-return this.__rowCb;
- -
},
- -
- -
identifierInputMethod:function () {
- 14948
-return this.__identifierInputMethod;
- -
},
- -
- -
identifierOutputMethod:function () {
- 14948
-return this.__identifierOutputMethod;
- -
},
- -
- -
firstSourceAlias:function () {
- 579
-var source = this.__opts.from;
- 579
-if (isUndefinedOrNull(source) || !source.length) {
- 2
-throw new DatasetError("No source specified for the query");
- -
}
- 577
-source = source[0];
- 577
-if (isInstanceOf(source, AliasedExpression)) {
- 20
-return source.alias;
- 557
-} else if (isString(source)) {
- 0
-var parts = this._splitString(source);
- 0
-var alias = parts[2];
- 0
-return alias ? alias : source;
- -
} else {
- 557
-return source;
- -
}
},
- -
- -
firstSourceTable:function () {
- 15
-var source = this.__opts.from;
- 15
-if (isUndefinedOrNull(source) || !source.length) {
- 1
-throw new QueryError("No source specified for the query");
- -
}
- 14
-var source = source[0];
- 14
-if (isInstanceOf(source, AliasedExpression)) {
- 3
-return source.expression;
- 11
-} else if (isString(source)) {
- 0
-var parts = this._splitString(source);
- 0
-return source;
- -
} else {
- 11
-return source;
- +
}
- +
// Returns the dataset for the schema_migrations table. If no such table
- +
// exists, it is automatically created.
- +
_getSchemaDataset:function () {
- 36
+var ret = new Promise();
- 36
+if (!this.__schemaDataset) {
- 18
+var ds = this.db.from(this.table);
- 18
+this.__createTable().then(hitch(this, function () {
- 18
+this.__schemaDataset = ds;
- 18
+ret.callback(ds);
- +
}), ret);
- +
} else {
- 18
ret.callback(this.__schemaDataset);
- +
}
- 36
return ret.promise();
},
- -
- -
/**
- -
* @ignore
- -
**/
- -
setters:{
- -
/**@lends patio.Dataset.prototype*/
- -
- -
identifierInputMethod:function (meth) {
- 15038
-this.__identifierInputMethod = meth;
- +
},
- +
__convertSchemaInfo:function () {
- 1
+var ret = new Promise(), c = this.column;
- 1
+var ds = this.db.from(this.table);
- 1
+this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
- 1
+this.getFileNames().then(hitch(this, function (files) {
- 1
+var l = files.length, inserts = [];
- 1
+if (l > 0) {
- 1
+for (var i = 0; i < l; i++) {
- 7
+var f = path.basename(files[i]);
- 7
+if (this.getMigrationVersionFromFile(f) <= version) {
- 5
+var insert = {};
- 5
+insert[c] = f;
- 5
+inserts.push(ds.insert(insert));
- +
}
- +
}
- +
}
- 1
+if (inserts.length) {
- 1
+when.apply(comb, inserts).then(ret);
- +
} else {
- 0
+ret.callback();
- +
}
- +
}), ret);
- +
}), ret);
- 1
return ret.promise();
- -
- -
identifierOutputMethod:function (meth) {
- 15038
-this.__identifierOutputMethod = meth;
- +
},
},
- -
- -
rowCb:function (cb) {
- 18564
-if (isFunction(cb) || isNull(cb)) {
- 18559
+this.__rowCb = cb;
- +
__createTable:function () {
- 18
+var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
- 18
+var ds = this.db.from(table);
- 18
+var ret = new Promise();
- 18
+when(
- +
db.tableExists(table),
- +
db.tableExists(intMigrationTable)
- +
).then(hitch(this, function (res) {
- 18
+var exists = res[0], intMigratorExists = res[1];
- 18
+if (!exists) {
- 10
+db.createTable(table,
- +
function () {
- 10
+this.column(c, String, {primaryKey:true});
- +
}).addErrback(ret);
- 10
+if (intMigratorExists) {
- 1
+db.from(intMigrationTable).all().then(hitch(this, function (versions) {
- 1
+var version;
- 1
+if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
- 1
+this.__convertSchemaInfo().then(ret);
- +
} else {
- 0
+ret.callback();
- +
}
- +
}));
- +
} else {
- 9
+ret.callback();
}
- -
} else {
- 5
+throw new DatasetError("rowCb mus be a function");
- 8
+ds.columns.then(hitch(this, function (columns) {
- 8
+if (columns.indexOf(c) === -1) {
- 0
+ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
- +
} else {
- 8
+ret.callback();
- +
}
}), ret);
- -
}
- +
}
- +
}), ret);
- 18
return ret.promise();
}
},
- -
static:{
- -
COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
- -
COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
- +
COLUMN_REF_RE3:/^(\w+)__(\w+)$/
- +
DEFAULT_SCHEMA_COLUMN:"filename",
DEFAULT_SCHEMA_TABLE:"schema_migrations"
- -
}
- +
}).as(module);
}).as(exports, "TimestampMigrator");
- -
- 1
+exports.run = function () {
- 31
+return Migrator.run.apply(Migrator, arguments);
};
- 1
-var comb = require("comb"),
- -
define = comb.define,
- -
array = comb.array,
- -
intersect = array.intersect,
- -
compact = array.compact,
- -
string = comb.string,
- -
format = string.format,
- -
argsToArray = comb.argsToArray,
- -
isInstanceOf = comb.isInstanceOf,
- -
isArray = comb.isArray,
- -
isNumber = comb.isNumber,
- -
isDate = comb.isDate,
- -
isNull = comb.isNull,
- -
isBoolean = comb.isBoolean,
- -
isFunction = comb.isFunction,
- -
isUndefined = comb.isUndefined,
- -
isObject = comb.isObject,
- -
isHash = comb.isHash,
- -
merge = comb.merge,
- -
isUndefinedOrNull = comb.isUndefinedOrNull,
- -
isString = comb.isString,
- -
sql = require("../sql").sql,
- -
Expression = sql.Expression,
- -
ComplexExpression = sql.ComplexExpression,
- -
AliasedExpression = sql.AliasedExpression,
- -
Identifier = sql.Identifier,
- -
QualifiedIdentifier = sql.QualifiedIdentifier,
- -
OrderedExpression = sql.OrderedExpression,
- -
CaseExpression = sql.CaseExpression,
- -
SubScript = sql.SubScript,
- -
NumericExpression = sql.NumericExpression,
- -
ColumnAll = sql.ColumnAll,
- -
Cast = sql.Cast,
- -
StringExpression = sql.StringExpression,
- -
BooleanExpression = sql.BooleanExpression,
- -
SQLFunction = sql.SQLFunction,
- -
LiteralString = sql.LiteralString,
- -
PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
- +
QueryError = require("../errors").QueryError, patio;
- +
define = comb.define,
- +
array = comb.array,
- +
intersect = array.intersect,
- +
compact = array.compact,
- +
string = comb.string,
- +
format = string.format,
- +
argsToArray = comb.argsToArray,
- +
isInstanceOf = comb.isInstanceOf,
- +
isArray = comb.isArray,
- +
isNumber = comb.isNumber,
- +
isDate = comb.isDate,
- +
isNull = comb.isNull,
- +
isBoolean = comb.isBoolean,
- +
isFunction = comb.isFunction,
- +
isUndefined = comb.isUndefined,
- +
isObject = comb.isObject,
- +
isHash = comb.isHash,
- +
isEmpty = comb.isEmpty,
- +
merge = comb.merge,
- +
isUndefinedOrNull = comb.isUndefinedOrNull,
- +
isString = comb.isString,
- +
sql = require("../sql").sql,
- +
Expression = sql.Expression,
- +
ComplexExpression = sql.ComplexExpression,
- +
AliasedExpression = sql.AliasedExpression,
- +
Identifier = sql.Identifier,
- +
QualifiedIdentifier = sql.QualifiedIdentifier,
- +
OrderedExpression = sql.OrderedExpression,
- +
CaseExpression = sql.CaseExpression,
- +
SubScript = sql.SubScript,
- +
NumericExpression = sql.NumericExpression,
- +
ColumnAll = sql.ColumnAll,
- +
Cast = sql.Cast,
- +
StringExpression = sql.StringExpression,
- +
BooleanExpression = sql.BooleanExpression,
- +
SQLFunction = sql.SQLFunction,
- +
LiteralString = sql.LiteralString,
- +
PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
QueryError = require("../errors").QueryError, patio;
- 1
var Dataset;
- @@ -14323,7 +14349,7 @@
- 59
return new QualifiedIdentifier(table, e);
- 217
} else if (isInstanceOf(e, OrderedExpression)) {
- 2
-return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending,
- +
{nulls:e.nulls});
{nulls:e.nulls});
- 215
} else if (isInstanceOf(e, AliasedExpression)) {
- 72
return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias);
- 143
@@ -14491,9 +14517,9 @@} else if (isInstanceOf(e, CaseExpression)) {
- 1440
var columns = this.__opts.columns, ret = "";
- 1440
if (columns && columns.length) {
- 1387
-ret = " (" + columns.map(
- -
function (c) {
- 6691
-return c.toString(this);
- +
}, this).join(this._static.COMMA_SEPARATOR) + ")";
- +
function (c) {
- 6691
+return c.toString(this);
}, this).join(this._static.COMMA_SEPARATOR) + ")";
}
- 1440
return ret;
- @@ -14605,8 +14631,8 @@
},
- 0
return this.complexExpressionSql("EQ", args);
} else {
- 0
-return this.complexExpressionSql("OR",
- -
[BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
- +
null)]);
- +
[BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
null)]);
}
- 6454
@@ -14637,8 +14663,8 @@} else if (["IN", "NOTIN"].indexOf(op) != -1) {
//literal so that if values is an array of two element arrays, it
//will be treated as a value list instead of a condition specifier.
- 4
-return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
- -
ComplexExpression.IN_OPERATORS[op],
- +
valArray ? this._arraySql(vals) : this.literal(vals));
- +
ComplexExpression.IN_OPERATORS[op],
valArray ? this._arraySql(vals) : this.literal(vals));
}
}
- @@ -14652,13 +14678,13 @@
else {
}
} else {
- 10
-return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
- +
ComplexExpression.IN_OPERATORS[op], this.literal(vals));
ComplexExpression.IN_OPERATORS[op], this.literal(vals));
}
}
- 6437
} else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
- 5427
var l = args[0];
- 5427
-return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
- +
this.literal(args[1]));
this.literal(args[1]));
- 1010
} else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
- 976
return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
- 34
@@ -14701,7 +14727,7 @@} else if (op == "NOT") {
}
- 925
var tref = this.__tableRef(table);
- 925
-return string.format(" %s %s", this._joinTypeSql(jc.joinType),
- +
tableAlias ? this.__asSql(tref, tableAlias) : tref);
tableAlias ? this.__asSql(tref, tableAlias) : tref);
},
- @@ -14774,11 +14800,11 @@
/**
*/
qualifiedIdentifierSql:function (qcr) {
- 4532
-return [qcr.table, qcr.column].map(
- -
function (x) {
- 9064
-return [QualifiedIdentifier, Identifier, String].some(function (c) {
- 25671
-return x instanceof c
- -
}) ? this.literal(x) : this.quoteIdentifier(x)
- +
}, this).join('.');
- +
function (x) {
- 9064
+return [QualifiedIdentifier, Identifier, String].some(function (c) {
- 25671
+return x instanceof c
- +
}) ? this.literal(x) : this.quoteIdentifier(x)
}, this).join('.');
},
- @@ -14813,12 +14839,12 @@
/**
- 33693
var i = this.__identifierInputMethod;
- 33693
v = v.toString(this);
- 33693
-return !isUndefinedOrNull(i) ?
- -
isFunction(v[i]) ?
- -
v[i]() :
- -
isFunction(comb[i]) ?
- -
comb[i](v)
- -
: v
- +
: v;
- +
isFunction(v[i]) ?
- +
v[i]() :
- +
isFunction(comb[i]) ?
- +
comb[i](v)
- +
: v
: v;
},
- @@ -14831,12 +14857,12 @@
/**
- 19885
(v == '' && (v = 'untitled'));
- 19885
var i = this.__identifierOutputMethod;
- 19885
-return !isUndefinedOrNull(i) ?
- -
isFunction(v[i]) ?
- -
v[i]() :
- -
isFunction(comb[i]) ?
- -
comb[i](v)
- -
: v
- +
: v;
- +
isFunction(v[i]) ?
- +
v[i]() :
- +
isFunction(comb[i]) ?
- +
comb[i](v)
- +
: v
: v;
},
- @@ -14932,9 +14958,9 @@
/**
*/
_joinTypeSql:function (joinType) {
- 922
-return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g,
- -
function (m) {
- 1127
-return m.toUpperCase() + " ";
- +
}
- +
function (m) {
- 1127
+return m.toUpperCase() + " ";
}
).trimRight() + " JOIN";
},
- @@ -15060,10 +15086,10 @@
- 6059
var table = parts[0], column = parts[1], alias = parts[2];
- 6059
if (!alias) {
- 6059
-return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'"
- +
+ v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
+ v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
} else {
- 0
-return this.literal(new AliasedExpression(column
- +
&& table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
&& table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
}
},
- @@ -15356,9 +15382,9 @@
- 0
throw new QueryError("No source specified for the query");
}
- 6657
-return " " + source.map(
- -
function (s) {
- 6971
-return this.__tableRef(s);
- +
}, this).join(this._static.COMMA_SEPARATOR);
- +
function (s) {
- 6971
+return this.__tableRef(s);
}, this).join(this._static.COMMA_SEPARATOR);
},
- @@ -15904,7 +15930,7 @@
/**
- 2734
var a = [];
- 2734
var ret = new Promise().classic(cb);
- 2734
-this.forEach(hitch(this, function (r) {
- 2936
+a.push(r);
- 2947
a.push(r);
})).then(hitch(this, function () {
- 2731
this.postLoad(a);
- 2731
@@ -17150,10 +17176,10 @@if (block) {
*
*/
- -
constructor:function () {
- 3218
-if (comb.isUndefinedOrNull(this.__associations)) {
- 3055
+this.__associations = {};
- 3107
+if (comb.isUndefinedOrNull(this.__associations)) {
- 3000
this.__associations = {};
- -
}
- 3218
+this._super(arguments);
- 3107
this._super(arguments);
},
- @@ -18080,240 +18106,29 @@
reload:function () {
* });
* }
- -
*
- -
*
- -
* @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
- -
*
- -
* @throws {patio.ModelError} if name is not a function or string.
- -
* @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
- -
*/
- -
validate:function (name) {
- 41
-this.__initValidation();
- 41
-var ret;
- 41
-if (isFunction(name)) {
- 1
-name.apply(this, [this.__getValidator.bind(this)]);
- 1
-ret = this;
- 40
-} else if (isString(name)) {
- 40
-ret = this.__getValidator(name);
- -
} else {
- 0
-throw new ModelError("name is must be a string or function when validating");
- -
}
- 41
-return ret;
- -
}
- -
}
- -
- -
}).as(module);
- database/logging.js
- |
-
-
- Coverage97.96
- SLOC193
- LOC49
- Missed1
-
- |
-
- 1
-var comb = require("comb"),
- -
define = comb.define,
- -
Promise = comb.Promise,
- -
isFunction = comb.isFunction,
- -
logging = comb.logging,
- -
Logger = logging.Logger,
- -
hitch = comb.hitch,
- -
format = comb.string.format,
- -
QueryError = require("../errors").QueryError;
- -
- -
- 1
-var LOGGER = Logger.getLogger("patio.Database");
- -
- 1
-define(null, {
- -
instance:{
- -
/**@lends patio.Database.prototype*/
- -
- -
/**
- -
* Logs an INFO level message to the "patio.Database" logger.
- -
*/
- -
logInfo:function () {
- 8337
-if (LOGGER.isInfo) {
- 8337
-LOGGER.info.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a DEBUG level message to the "patio.Database" logger.
- -
*/
- -
logDebug:function () {
- 8027
-if (LOGGER.isDebug) {
- 8027
-LOGGER.debug.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs an ERROR level message to the "patio.Database" logger.
- -
*/
- -
logError:function (error) {
- 75
-if (LOGGER.isError) {
- 75
-LOGGER.error.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a WARN level message to the "patio.Database" logger.
- -
*/
- -
logWarn:function () {
- 1
-if (LOGGER.isWarn) {
- 1
-LOGGER.warn.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a TRACE level message to the "patio.Database" logger.
- -
*/
- -
logTrace:function () {
- 1
-if (LOGGER.isTrace) {
- 1
-LOGGER.trace.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a FATAL level message to the "patio.Database" logger.
- -
*/
- -
logFatal:function () {
- 1
-if (LOGGER.isFatal) {
- 1
-LOGGER.fatal.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/* Yield to the block, logging any errors at error level to all loggers,
- -
* and all other queries with the duration at warn or info level.
- -
* */
- -
__logAndExecute:function (sql, args, cb) {
- 8101
-if (isFunction(args)) {
- 8100
-cb = args;
- 8100
-args = null;
- -
}
- -
- 8101
-if (args) {
- 0
-sql = format("%s; %j", sql, args);
- -
}
- 8101
-sql = sql.trim();
- 8101
-var start = new Date();
- 8101
-var ret = new Promise();
- 8101
-if (isFunction(cb)) {
- 8100
-this.logInfo("Executing; %s", sql);
- 8100
-cb().then(hitch(this, function () {
- 8026
-this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
- 8026
-ret.callback.apply(ret, arguments);
- -
}), hitch(this, function (err) {
- 74
-err = new QueryError(format("%s: %s", err.message, sql));
- 74
-this.logError(err);
- 74
-ret.errback.apply(ret, [err]);
- -
}));
- -
} else {
- 1
-throw new QueryError("CB is required");
- -
}
- 8100
-return ret.promise();
- -
},
- -
- -
/*Log the given SQL and then execute it on the connection, used by
- -
*the transaction code.
- -
* */
- -
__logConnectionExecute:function (conn, sql) {
- 2079
-return this.__logAndExecute(sql, hitch(this, function () {
- 2079
-return conn[this.connectionExecuteMethod](sql);
- -
}));
- -
},
- -
- -
- -
getters:{
- -
/**@lends patio.Database.prototype*/
- -
/**
- -
* The "patio.Database" logger.
- -
* @field
- -
*/
- -
logger:function () {
- 2
-return LOGGER;
- -
}
- -
}
- -
},
- -
- -
"static":{
- -
/**@lends patio.Database*/
- -
/**
- -
* Logs an INFO level message to the "patio.Database" logger.
- -
*/
- -
logInfo:function () {
- 1
-if (LOGGER.isInfo) {
- 1
-LOGGER.info.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a DEBUG level message to the "patio.Database" logger.
- -
*/
- -
logDebug:function () {
- 1
-if (LOGGER.isDebug) {
- 1
-LOGGER.debug.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a ERROR level message to the "patio.Database" logger.
- -
*/
- -
logError:function () {
- 1
-if (LOGGER.isError) {
- 1
-LOGGER.error.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a WARN level message to the "patio.Database" logger.
- -
*/
- -
logWarn:function () {
- 1
-if (LOGGER.isWarn) {
- 1
-LOGGER.warn.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- -
* Logs a TRACE level message to the "patio.Database" logger.
- -
*/
- -
logTrace:function () {
- 1
-if (LOGGER.isTrace) {
- 1
-LOGGER.trace.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
/**
- +
* Logs a FATAL level message to the "patio.Database" logger.
- +
*
- +
* @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
- +
*
- +
* @throws {patio.ModelError} if name is not a function or string.
* @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
- -
*/
- -
logFatal:function () {
- 1
-if (LOGGER.isFatal) {
- 1
-LOGGER.fatal.apply(LOGGER, arguments);
- -
}
- -
},
- -
- -
getters:{
- -
/**@lends patio.Database*/
- -
/**
- -
* The "patio.Database" logger.
- -
* @field
- -
*/
- -
logger:function () {
- 1
+return LOGGER;
- +
validate:function (name) {
- 41
+this.__initValidation();
- 41
+var ret;
- 41
+if (isFunction(name)) {
- 1
+name.apply(this, [this.__getValidator.bind(this)]);
- 1
+ret = this;
- 40
+} else if (isString(name)) {
- 40
+ret = this.__getValidator(name);
- +
} else {
- 0
throw new ModelError("name is must be a string or function when validating");
- +
}
- 41
return ret;
}
}
- -
}).as(module);
}).as(module);
},
/**
* Selects the columns if only if there is not already select sources.
*
* @example
*
* var ds = DB.from("items"); //SELECT * FROM items
*
* ds.select("a"); //SELECT a FROM items;
* ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
* ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
*
* @param cols columns to select if there is not already select sources.
* @return {patio.Dataset} a cloned dataset with the appropriate select sources.
*/
selectIfNoSource:function (cols) {
var ret;
if (!this.hasSelectSource) {
ret = this.select.apply(this, arguments);
} else {
ret = this.mergeOptions();
}
return ret;
},
/**
* Returns a copy of the dataset with the given columns added
* to the existing selected columns. If no columns are currently selected,
* it will select the columns given in addition to *.
as(module);
+ database/logging.js
+ |
+
+
+ Coverage97.96
+ SLOC193
+ LOC49
+ Missed1
+
+ |
+
- 1
+var comb = require("comb"),
- +
define = comb.define,
- +
Promise = comb.Promise,
- +
isFunction = comb.isFunction,
- +
logging = comb.logging,
- +
Logger = logging.Logger,
- +
hitch = comb.hitch,
- +
format = comb.string.format,
- +
QueryError = require("../errors").QueryError;
- +
- +
- 1
+var LOGGER = Logger.getLogger("patio.Database");
- +
- 1
+define(null, {
- +
instance:{
- +
/**@lends patio.Database.prototype*/
- +
- +
/**
- +
* Logs an INFO level message to the "patio.Database" logger.
- +
*/
- +
logInfo:function () {
- 8337
+if (LOGGER.isInfo) {
- 8337
+LOGGER.info.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a DEBUG level message to the "patio.Database" logger.
- +
*/
- +
logDebug:function () {
- 8027
+if (LOGGER.isDebug) {
- 8027
+LOGGER.debug.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs an ERROR level message to the "patio.Database" logger.
- +
*/
- +
logError:function (error) {
- 75
+if (LOGGER.isError) {
- 75
+LOGGER.error.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a WARN level message to the "patio.Database" logger.
- +
*/
- +
logWarn:function () {
- 1
+if (LOGGER.isWarn) {
- 1
+LOGGER.warn.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a TRACE level message to the "patio.Database" logger.
- +
*/
- +
logTrace:function () {
- 1
+if (LOGGER.isTrace) {
- 1
+LOGGER.trace.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a FATAL level message to the "patio.Database" logger.
- +
*/
- +
logFatal:function () {
- 1
+if (LOGGER.isFatal) {
- 1
+LOGGER.fatal.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/* Yield to the block, logging any errors at error level to all loggers,
- +
* and all other queries with the duration at warn or info level.
- +
* */
- +
__logAndExecute:function (sql, args, cb) {
- 8101
+if (isFunction(args)) {
- 8100
+cb = args;
- 8100
+args = null;
- +
}
- +
- 8101
+if (args) {
- 0
+sql = format("%s; %j", sql, args);
- +
}
- 8101
+sql = sql.trim();
- 8101
+var start = new Date();
- 8101
+var ret = new Promise();
- 8101
+if (isFunction(cb)) {
- 8100
+this.logInfo("Executing; %s", sql);
- 8100
+cb().then(hitch(this, function () {
- 8026
+this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
- 8026
+ret.callback.apply(ret, arguments);
- +
}), hitch(this, function (err) {
- 74
+err = new QueryError(format("%s: %s", err.message, sql));
- 74
+this.logError(err);
- 74
+ret.errback.apply(ret, [err]);
- +
}));
- +
} else {
- 1
+throw new QueryError("CB is required");
- +
}
- 8100
+return ret.promise();
- +
},
- +
- +
/*Log the given SQL and then execute it on the connection, used by
- +
*the transaction code.
- +
* */
- +
__logConnectionExecute:function (conn, sql) {
- 2079
+return this.__logAndExecute(sql, hitch(this, function () {
- 2079
+return conn[this.connectionExecuteMethod](sql);
- +
}));
- +
},
- +
- +
- +
getters:{
- +
/**@lends patio.Database.prototype*/
- +
/**
- +
* The "patio.Database" logger.
- +
* @field
- +
*/
- +
logger:function () {
- 2
+return LOGGER;
- +
}
- +
}
- +
},
- +
- +
"static":{
- +
/**@lends patio.Database*/
- +
/**
- +
* Logs an INFO level message to the "patio.Database" logger.
- +
*/
- +
logInfo:function () {
- 1
+if (LOGGER.isInfo) {
- 1
+LOGGER.info.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a DEBUG level message to the "patio.Database" logger.
- +
*/
- +
logDebug:function () {
- 1
+if (LOGGER.isDebug) {
- 1
+LOGGER.debug.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a ERROR level message to the "patio.Database" logger.
- +
*/
- +
logError:function () {
- 1
+if (LOGGER.isError) {
- 1
+LOGGER.error.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a WARN level message to the "patio.Database" logger.
- +
*/
- +
logWarn:function () {
- 1
+if (LOGGER.isWarn) {
- 1
+LOGGER.warn.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a TRACE level message to the "patio.Database" logger.
- +
*/
- +
logTrace:function () {
- 1
+if (LOGGER.isTrace) {
- 1
+LOGGER.trace.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
/**
- +
* Logs a FATAL level message to the "patio.Database" logger.
- +
*/
- +
logFatal:function () {
- 1
+if (LOGGER.isFatal) {
- 1
+LOGGER.fatal.apply(LOGGER, arguments);
- +
}
- +
},
- +
- +
getters:{
- +
/**@lends patio.Database*/
- +
/**
- +
* The "patio.Database" logger.
- +
* @field
- +
*/
- +
logger:function () {
- 1
+return LOGGER;
- +
}
- +
}
- +
}
- +
}).as(module);
firstSourceAlias | String | The first source (primary table) for this dataset. If the table is aliased, returns the aliased name. throws a {patio.DatasetError} tf the dataset doesn't have a table. DB.from("table").firstSourceAlias; //=> "table" DB.from("table___t").firstSourceAlias; //=> "t" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
firstSourceTable | String | The first source (primary table) for this dataset. If the dataset doesn't have a table, raises a patio.erros.DatasetError. DB.from("table").firstSourceTable; //=> "table" DB.from("table___t").firstSourceTable; //=> "t"+ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
hasSelectSource | Boolean | true if this dataset already has a select sources. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
identifierInputMethod | String | this is the method that will be called on each identifier returned from the database. This value will be defaulted to whatever the identifierInputMethod is on the database used in initialization. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
identifierOutputMethod | String | this is the method that will be called on each identifier sent to the database. This value will be defaulted to whatever the identifierOutputMethod is on the database used in initialization. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
isSimpleSelectAll | Boolean | Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false. DB.from("items").isSimpleSelectAll; //=> true DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false+ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
joinSourceList | patio.sql.Identifier[] | [] | a list of join sources | |||||||||||||||||||||||||||||||||||||||||||||||||||||
providesAccurateRowsMatched | boolean | true | Whether this dataset will provide accurate number of rows matched for delete and update statements. Accurate in this case is the number of rows matched by the dataset's filter. @@ -3308,6 +3324,7 @@Actions | |||||||||||||||||||||||||||||||||||||||||||||||||||||
requiresSqlStandardDate | boolean | false | Whether the dataset requires SQL standard datetimes (false by default, as most allow strings with ISO 8601 format). | |||||||||||||||||||||||||||||||||||||||||||||||||||||
rowCb | Function | callback to be invoked for each row returned from the database. the return value will be used as the result of query. The rowCb can also return a promise, The resolved value of the promise will be used as result. + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
sourceList | patio.sql.Identifier[] | [] | a list of sources for this dataset. | |||||||||||||||||||||||||||||||||||||||||||||||||||||
supportsCte | boolean | true | Whether the dataset supports common table expressions (the WITH clause). | |||||||||||||||||||||||||||||||||||||||||||||||||||||
supportsDistinctOn | boolean | false | Whether the dataset supports the DISTINCT ON clause, false by default. | |||||||||||||||||||||||||||||||||||||||||||||||||||||
supportsIntersectExcept | boolean | true | Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default. @@ -4297,9 +4314,9 @@
var table = parts[0], column = parts[1], alias = parts[2];
if (!alias) {
return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'"
- + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
+ + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
} else {
return this.literal(new AliasedExpression(column
- && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
+ && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
}
}
@@ -5151,7 +5168,7 @@ throw new QueryError("No source specified for the query"); } return " " + source.map( - function (s) { - return this.__tableRef(s); - }, this).join(this._static.COMMA_SEPARATOR); + function (s) { + return this.__tableRef(s); + }, this).join(this._static.COMMA_SEPARATOR); } |
function (qcr){ return [qcr.table, qcr.column].map( - function (x) { - return [QualifiedIdentifier, Identifier, String].some(function (c) { - return x instanceof c - }) ? this.literal(x) : this.quoteIdentifier(x) - }, this).join('.'); + function (x) { + return [QualifiedIdentifier, Identifier, String].some(function (c) { + return x instanceof c + }) ? this.literal(x) : this.quoteIdentifier(x) + }, this).join('.'); }@@ -14036,6 +14053,75 @@