|
Overview
|
Coverage88.73
SLOC22584
LOC5597
Missed631
|
|
ConnectionPool.js
|
Coverage74.60
SLOC192
LOC63
Missed16
|
- 1
var comb = require("comb"),Promise = comb.Promise,PromiseList = comb.PromiseList,isFunction = comb.isFunction,Queue = comb.collections.Queue,merge = comb.merge,define = comb.define,Pool = comb.collections.Pool;- 1
define(Pool, {instance: {/**@lends patio.ConnectionPool.prototype*//*** ConnectionPool object used internall by the {@link patio.Database} class;* @constructs;* @param options*/constructor: function (options) {- 120
options = options || {};- 120
if (!options.createConnection || !isFunction(options.createConnection)) {- 0
throw new Error("patio.adapters.clients.ConnectionPool : create connection CB required.");}- 120
if (!options.closeConnection || !isFunction(options.closeConnection)) {- 0
throw new Error("patio.adapters.clients.ConnectionPool : close connection CB required.");}- 120
options.minObjects = parseInt(options.minConnections || 0, 10);- 120
options.maxObjects = parseInt(options.maxConnections || 10, 10);- 120
this.__deferredQueue = new Queue();- 120
this._options = options;- 120
this.__createConnectionCB = options.createConnection;- 120
this.__closeConnectionCB = options.closeConnection;- 120
this.__validateConnectionCB = options.validateConnection;- 120
this._super(arguments);},/*** Checks all deferred connection requests.*/__checkQueries: function () {- 2377
var fc = this.freeCount, def, defQueue = this.__deferredQueue;- 2377
while (fc-- >= 0 && defQueue.count) {- 0
def = defQueue.dequeue();- 0
var conn = this.getObject();- 0
if (conn) {- 0
def.callback(conn);} else {- 0
throw new Error("UNEXPECTED ERROR");}- 0
fc--;}},/*** Performs a query on one of the connection in this Pool.** @return {comb.Promise} A promise to called back with a connection.*/getConnection: function () {- 2384
var ret = new Promise(), conn;- 2384
if (this.count > this.__maxObjects) {- 0
this.__deferredQueue.enqueue(ret);} else {//todo override getObject to make async so creating a connetion can execute setup sql- 2384
conn = this.getObject();- 2384
if (!conn) {//we need to deffer it- 0
this.__deferredQueue.enqueue(ret);} else {- 2384
ret.callback(conn);}}- 2384
if (this.count > this.__maxObjects && !conn) {- 0
ret.errback(new Error("Unexpected ConnectionPool error"));}- 2384
return ret.promise();},/*** Override comb.collections.Pool to allow async validation to allow* pools to do any calls to reset a connection if it needs to be done.** @param {*} connection the connection to return.**/returnObject: function (obj) {- 2379
var self = this;- 2379
this.validate(obj).chain(function (valid) {- 2379
var index;- 2379
if (self.count <= self.__maxObjects && valid && (index = self.__inUseObjects.indexOf(obj)) > -1) {- 2377
self.__inUseObjects.splice(index, 1);- 2377
self.__freeObjects.enqueue(obj);- 2377
self.__checkQueries();} else {- 2
self.removeObject(obj);}});},/*** Removes a connection from the pool.* @param conn*/removeConnection: function (conn) {- 0
this.closeConnection(conn);- 0
return this.removeObject(conn);},/*** Return a connection to the pool.** @param {*} connection the connection to return.** @return {*} an adapter specific connection.*/returnConnection: function (connection) {- 2379
this.returnObject(connection);},createObject: function () {- 80
return this.createConnection();},/*** Override to implement the closing of all connections.** @return {comb.Promise} called when all connections are closed.*/endAll: function () {- 38
this.__ending = true;- 38
var conn, fQueue = this.__freeObjects, count = this.count, ps = [];- 38
while ((conn = this.__freeObjects.dequeue()) !== undefined) {- 65
ps.push(this.closeConnection(conn));}- 38
var inUse = this.__inUseObjects;- 38
for (var i = inUse.length - 1; i >= 0; i--) {- 5
ps.push(this.closeConnection(inUse[i]));}- 38
this.__inUseObjects.length = 0;- 38
return new PromiseList(ps).promise();},/*** Override to provide any additional validation. By default the promise is called back with true.** @param {*} connection the conneciton to validate.** @return {comb.Promise} called back with a valid or invalid state.*/validate: function (conn) {- 2379
if (!this.__validateConnectionCB) {- 0
var ret = new Promise();- 0
ret.callback(true);- 0
return ret;} else {- 2379
return this.__validateConnectionCB(conn);}},/*** Override to create connections to insert into this ConnectionPool.*/createConnection: function () {- 80
return this.__createConnectionCB(this._options);},/*** Override to implement close connection functionality;* @param {*} conn the connection to close;** @return {comb.Promise} called back when the connection is closed.*/closeConnection: function (conn) {- 70
return this.__closeConnectionCB(conn);}},"static": {/**@lends patio.ConnectionPool*/getPool: function (opts, createConnection, closeConnection, validateConnection) {- 120
var Self = this;- 120
return new Self(merge(opts, {createConnection: createConnection,closeConnection: closeConnection,validateConnection: validateConnection}));}}}).as(module);
|
associations/manyToMany.js
|
Coverage75.19
SLOC307
LOC133
Missed33
|
- 1
var comb = require("comb-proxy"),define = comb.define,isUndefined = comb.isUndefined,isUndefinedOrNull = comb.isUndefinedOrNull,isFunction = comb.isFunction,isInstanceOf = comb.isInstanceOf,sql = require("../sql").sql,array = comb.array,isBoolean = comb.isBoolean,when = comb.when,zip = array.zip,Promise = comb.Promise,PromiseList = comb.PromiseList,OneToMany = require("./oneToMany"),pluralize = comb.pluralize,AssociationError = require("../errors").AssociationError;- 1
var LOGGER = comb.logger("comb.associations.ManyToMany");/*** @class Class to define a manyToMany association.** </br>* <b>NOT to be instantiated directly</b>* Its just documented for reference.** @name ManyToMany* @augments patio.associations.OneToMany* @memberOf patio.associations** @param {String} options.joinTable the joinTable of the association.*** @property {String} joinTable the join table used in the relation.* */- 1
module.exports = define(OneToMany, {instance: {/**@lends patio.associations.ManyToMany.prototype*/type: "manyToMany",_fetchMethod: "all",supportsStringKey: false,supportsCompositeKey: false,_filter: function (parent) {- 205
var keys = this._getAssociationKey(parent), options = this.__opts || {}, ds, self = this;- 205
if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {- 0
ds = ds.apply(parent, [parent]);}- 205
if (!ds) {- 205
ds = this.model.dataset.select(sql.identifier(this.model.__tableName).all()).naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {- 205
return sql.stringToIdentifier(k);})).concat(zip(keys[0], this.parentPrimaryKey.map(function (k) {- 205
return parent[k];}))));- 205
var recip = this.model._findAssociation(this);- 205
if (recip) {- 205
recip = recip[1];}- 205
ds.rowCb = function (item) {- 200
var model = self._toModel(item, true);- 200
if (recip) {- 200
recip.__setValue(model, parent);}//call hook to finish other model associations- 200
return model._hook("post", "load").chain(function () {- 200
return model;});};- 0
} else if (!ds.rowCb && this.model) {- 0
ds.rowCb = function (item) {- 0
var model = self._toModel(item, true);//call hook to finish other model associations- 0
return model._hook("post", "load").chain(function () {- 0
return model;});};}- 205
return this._setDatasetOptions(ds);},_setAssociationKeys: function (parent, model, val) {- 349
var keys = this._getAssociationKey(parent),leftKey = keys[0],parentPk = this.parentPrimaryKey;- 349
if (!(leftKey && leftKey.length === parentPk.length)) {- 0
throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);}- 349
for (var i = 0; i < leftKey.length; i++) {- 349
model[leftKey[i]] = !isUndefined(val) ? val : parent[parentPk[i]];}},__createJoinTableInsertRemoveQuery: function (model, item) {- 82
var q = {};- 82
var keys = this._getAssociationKey(model),leftKey = keys[0],rightKey = keys[1],parentPk = this.parentPrimaryKey,modelPk = this.modelPrimaryKey;- 82
if (!(leftKey && leftKey.length === parentPk.length)) {- 0
throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);}- 82
if (!(rightKey && rightKey.length === modelPk.length)) {- 0
throw new AssociationError("Invalid rightKey for " + this.name + " : " + rightKey);}- 82
for (var i = 0; i < leftKey.length; i++) {- 82
q[leftKey[i]] = model[parentPk[i]];}- 82
for (i = 0; i < rightKey.length; i++) {- 82
q[rightKey[i]] = item[modelPk[i]];}- 82
return q;},_preRemove: function (next, model) {- 145
if (this.isOwner && !this.isCascading) {- 145
var q = {};- 145
this._setAssociationKeys(model, q);- 145
this.joinTable.where(q).remove().classic(next);} else {- 0
next();}},addAssociation: function (item, model, reload) {- 114
reload = isBoolean(reload) ? reload : false;- 114
var ret = new Promise().callback(model);- 114
if (!isUndefinedOrNull(item)) {- 114
if (!model.isNew) {- 70
item = this._toModel(item);- 70
var loaded = this.associationLoaded(model);- 70
var recip = this.model._findAssociation(this), save = item.isNew, self = this;- 70
ret = model._checkTransaction(function () {- 70
var joinTable = self.joinTable;- 70
return when(save ? item.save() : null).chain(function () {- 70
return joinTable.insert(self.__createJoinTableInsertRemoveQuery(model, item));}).chain(function () {- 70
if (recip) {- 70
recip[1].__setValue(item, [model]);}}).chain(function () {- 70
if (loaded && reload) {- 2
return self.parent._reloadAssociationsForType(self.type, self.model, model);} else {- 68
return model;}}).chain(function () {- 70
return model;});});} else {- 44
item = this._toModel(item);- 44
var items = this.getAssociation(model);- 44
if (isUndefinedOrNull(items)) {- 15
this.__setValue(model, [item]);} else {- 29
items.push(item);}}}- 114
return ret.promise();},removeItem: function (item, model, remove, reload) {- 24
reload = isBoolean(reload) ? reload : false;- 24
remove = isBoolean(remove) ? remove : false;- 24
if (!isUndefinedOrNull(item)) {- 24
if (!model.isNew) {- 24
if (isInstanceOf(item, this.model) && !item.isNew) {- 24
var loaded = this.associationLoaded(model), self = this;- 24
remove = remove && !item.isNew;- 24
return model._checkTransaction(function () {- 24
return remove ? item.remove() : self.joinTable.where(self.__createJoinTableInsertRemoveQuery(model, item)).remove();}).chain(function () {- 24
if (loaded && reload) {- 10
return self.parent._reloadAssociationsForType(self.type, self.model, model);}}).chain(function () {- 24
return model;});}} else {- 0
item = this._toModel(item);- 0
var items = this.getAssociation(model), index;- 0
if (!isUndefinedOrNull(items) && (index = items.indexOf(item)) !== -1) {- 0
items.splice(index, 1);}- 0
return when(model);}}},removeAllItems: function (model, remove) {- 0
remove = isBoolean(remove) ? remove : false;- 0
var ret;- 0
if (!model.isNew) {- 0
var q = {}, removeQ = {};- 0
this._setAssociationKeys(model, q);- 0
this._setAssociationKeys(model, removeQ, null);- 0
var loaded = this.associationLoaded(model), self = this;- 0
ret = model._checkTransaction(function () {- 0
return when(remove ? self._filter(model).forEach(function (m) {- 0
return m.remove();}) : self.joinTable.filter(q).update(removeQ)).chain(function () {- 0
if (loaded) {- 0
return self.parent._reloadAssociationsForType(self.type, self.model, model).chain(function () {- 0
return model;});} else {- 0
return model;}});});} else {//todo we may want to check if any of the items were previously saved items;- 0
this._clearAssociations(model);- 0
ret = new Promise().callback(model);}- 0
return ret;},getters: {select: function () {- 205
return this.__select;},defaultLeftKey: function () {- 1021
return this.__opts.leftKey || this.parent.tableName + "Id";},defaultRightKey: function () {- 1021
return this.__opts.rightKey || this.model.tableName + "Id";},parentPrimaryKey: function () {- 636
return this.__opts.leftPrimaryKey || this.parent.primaryKey;},modelPrimaryKey: function () {- 287
return this.__opts.rightPrimaryKey || this.model.primaryKey;},joinTableName: function () {- 217
if (!this._joinTable) {- 12
var options = this.__opts;- 12
var joinTable = options.joinTable;- 12
if (isUndefined(joinTable)) {- 10
var defaultJoinTable = this.defaultJoinTable;- 10
if (isUndefined(defaultJoinTable)) {- 0
throw new AssociationError("Unable to determine jointable for " + this.name);} else {- 10
this._joinTable = defaultJoinTable;}} else {- 2
this._joinTable = joinTable;}}- 217
return this._joinTable;},//returns our join table modeljoinTable: function () {- 227
if (!this.__joinTableDataset) {- 12
var ds = this.__joinTableDataset = this.model.dataset.db.from(this.joinTableName), model = this.model, options = this.__opts;- 12
var identifierInputMethod = isUndefined(options.identifierInputMethod) ? model.identifierInputMethod : options.identifierInputMethod,identifierOutputMethod = isUndefined(options.identifierOutputMethod) ? model.identifierOutputMethod : options.identifierOutputMethod;- 12
if (identifierInputMethod) {- 6
ds.identifierInputMethod = identifierInputMethod;}- 12
if (identifierOutputMethod) {- 6
ds.identifierOutputMethod = identifierOutputMethod;}}- 227
return this.__joinTableDataset;},defaultJoinTable: function () {- 10
var ret;- 10
var recip = this.model._findAssociation(this);- 10
if (recip && recip.length) {- 10
var names = [pluralize(this._model), pluralize(recip[1]._model)].sort();- 10
names[1] = names[1].charAt(0).toUpperCase() + names[1].substr(1);- 10
ret = names.join("");}- 10
return ret;}}}});
|
adapters/postgres.js
|
Coverage76.14
SLOC1046
LOC440
Missed105
|
- 1
var pg = require("pg"),PgTypes = require("pg-types"),QueryStream = require('pg-query-stream'),comb = require("comb"),asyncArray = comb.async.array,string = comb.string,isHash = comb.isHash,argsToArray = comb.argsToArray,pad = string.pad,format = string.format,when = comb.when,array = comb.array,toArray = array.toArray,zip = array.zip,flatten = array.flatten,Promise = comb.Promise,isUndefinedOrNull = comb.isUndefinedOrNull,isString = comb.isString,isArray = comb.isArray,isEmpty = comb.isEmpty,isBoolean = comb.isBoolean,isObject = comb.isObject,isFunction = comb.isFunction,define = comb.define,merge = comb.merge,isDefined = comb.isDefined,isInstanceOf = comb.isInstanceOf,QueryError = require("../errors").QueryError,Dataset = require("../dataset"),Database = require("../database"),sql = require("../sql").sql,stringToIdentifier = sql.stringToIdentifier,DateTime = sql.DateTime,Time = sql.Time,Year = sql.Year,literal = sql.literal,StringExpression = sql.StringExpression,identifier = sql.identifier,BooleanExpression = sql.BooleanExpression,LiteralString = sql.LiteralString,Subscript = sql.Subscript,patio, DS,stream = require("stream"),PassThroughStream = stream.PassThrough,pipeAll = require("../utils").pipeAll,hashPick = comb.hash.pick;- 1
var getPatio = function () {- 898
return patio || (patio = require("../index.js"));};- 1
var isBlank = function (obj) {- 768
var ret = false;- 768
if (isUndefinedOrNull(obj)) {- 679
ret = true;- 89
} else if (isString(obj) || isArray(obj)) {- 89
ret = obj.length === 0;- 0
} else if (isBoolean(obj) && !obj) {- 0
ret = true;- 0
} else if (isObject(obj) && isEmpty(obj)) {- 0
ret = true;}- 768
return ret;};- 1
var byteaParser = function (val) {- 12
if (val.toString().indexOf("\\x") === 0) {- 12
val = val.toString().replace(/^\\x/, "");- 12
return new Buffer(val, "hex");} else {- 0
val = val.toString().replace(/\\([0-7]{3})/g, function (fullMatch, code) {- 0
return String.fromCharCode(parseInt(code, 8));}).replace(/\\\\/g, "\\");- 0
return new Buffer(val, "binary");}};- 1
PgTypes.setTypeParser(17, "text", byteaParser);- 1
var timestampOrig = PgTypes.getTypeParser(1114, "text");//PgTypes.setTypeParser(25, "text", byteaParser);- 1
PgTypes.setTypeParser(1114, "text", function (val) {- 38
val = String(val);- 38
if (!val.match(/\.(\d{0,3})/)) {- 0
val += ".000";} else {- 38
val = val.replace(/\.(\d{0,3})$/, function (m, m1) {- 38
return "." + pad(m1, 3, "0", true);});}- 38
return getPatio().stringToTimeStamp(val.toString(), DS.TIMESTAMP_FORMAT).date;});- 1
PgTypes.setTypeParser(1184, "text", function (val) {- 0
return getPatio().stringToDate(val.toString());});- 1
PgTypes.setTypeParser(1082, "text", function (val) {- 4
return getPatio().stringToDate(val.toString());});- 1
PgTypes.setTypeParser(1083, "text", function (val) {- 2
val = String(val);- 2
if (!val.match(/\.(\d{0,3})/)) {- 0
val += ".000";} else {- 2
val = val.replace(/\.(\d{0,3})$/, function (m, m1) {- 2
return "." + pad(m1, 3, "0", true);});}- 2
return getPatio().stringToTime(val.toString(), DS.TIME_FORMAT);});- 1
PgTypes.setTypeParser(1700, "text", parseFloat);- 1
PgTypes.setTypeParser(114, "text", function (data) {- 854
return getPatio().sql.json(JSON.parse(data));});- 1
var Connection = define(null, {instance: {connection: null,errored: false,closed: false,constructor: function (conn) {- 64
this.connection = conn;},closeConnection: function () {- 63
this.closed = true;- 63
this.connection.end();- 63
return new Promise().callback().promise();},stream: function (query, opts) {- 3
var ret;- 3
if (!this.closed) {- 3
try {- 3
opts = hashPick(opts || {}, ["batchSize", "highWaterMark"]);- 3
this.connection.setMaxListeners(0);- 3
var fields = [];- 3
query = new QueryStream(query, null, opts);- 3
ret = this.connection.query(query);- 3
var orig = ret.handleRowDescription;- 3
ret.handleRowDescription = function (msg) {- 2
ret.emit("fields", msg.fields);- 2
ret.handleRowDescription = orig;- 2
return orig.apply(ret, arguments);};} catch (e) {- 0
ret = new PassThroughStream();- 0
setImmediate(function () {- 0
ret.emit("error", e);});}} else {- 0
ret = new PassThroughStream();- 0
setImmediate(function () {- 0
ret.emit("error", new Error("Connection already closed"));});}- 3
return ret;},query: function (query) {- 6493
var ret = new Promise();- 6493
if (!this.closed) {- 6493
try {- 6493
this.connection.setMaxListeners(0);- 6493
var fields = [], rows = [];- 6493
var q = this.connection.query(query).on("error", ret.errback).on("row", function(row){- 4777
rows.push(row);}).on("end", function(){- 6425
ret.callback(rows, fields);});- 6493
var orig = q.handleRowDescription;- 6493
q.handleRowDescription = function (msg) {- 3800
fields = msg.fields;- 3800
q.handleRowDescription = orig;- 3800
return orig.apply(q, arguments);};} catch (e) {- 0
ret.errback(e);}} else {- 0
ret.errback(new Error("Connection already closed"));}- 6493
return ret.promise();}}});- 1
function colCallback(o) {- 29633
return o;}- 1
DS = define(Dataset, {instance: {complexExpressionSql: function (op, args) {- 5608
var ret = "";- 5608
if (op === "^") {- 0
var j = this._static.XOR_OP, c = false;- 0
args.forEach(function (a) {- 0
if (c) {- 0
ret += j;}- 0
ret += this.literal(a);- 0
c = true;}, true);} else {- 5608
return this._super(arguments);}- 0
return ret;},forShare: function () {- 0
return this.lockStyle("share");},fullTextSearch: function (cols, terms, opts) {- 3
opts = opts || {};- 3
var lang = opts.language || 'simple';- 3
if (Array.isArray(terms)) {- 1
terms = terms.join(' | ');}- 3
return this.filter("to_tsvector(?, ?) @@ to_tsquery(?, ?)", lang, this.__fullTextStringJoin(toArray(cols).map(function (c) {- 4
return stringToIdentifier(c);})), lang, terms);},/*** Lock all tables in the datasets from clause (but not in JOINs), in the specified mode. If* a function is passed in as the last argument* @para {String} mode the lock mode (e.g. 'EXCLUSIVE').* @param {Object} [opts] see {@link patio.Database#transaction} for options.* @param {Function} [cb] of provided then a new {@link patio.Database} transaction is started.**/lock: function (mode, opts, cb) {- 3
if (isFunction(opts)) {- 1
cb = opts;- 1
opts = null;} else {- 2
opts = opts || {};}- 3
if (isFunction(cb)) {- 1
var self = this;- 1
return this.db.transaction(opts, function () {- 1
return self.lock(mode, opts).chain(function () {- 1
return cb.call(self);});});} else {- 2
return this.db.execute(format(this._static.LOCK, [this._sourceList(this.__opts.from), mode]), opts);}},multiInsertSql: function (columns, values) {- 1
var ret = literal('VALUES ');- 1
ret += this.__expressionList(values.map(function (r) {- 2
return toArray(r);}));- 1
return [this.insertSql(columns.map(function (c) {- 2
return stringToIdentifier(c);}), literal(ret))];},_literalString: function (v) {- 5369
return "'" + v.replace(/'/g, "''") + "'";},_literalJson: function (v) {- 504
return "'" + JSON.stringify(v).replace(/'/g, "''") + "'";},_deleteFromSql: function () {- 730
var self = this._static, space = self.SPACE;- 730
return [space, self.FROM, space, this._sourceList(this.__opts.from[0])].join("");},_deleteUsingSql: function () {- 730
return this._joinFromSql("USING");},_joinFromSql: function (type) {- 932
var from = this.__opts.from.slice(1), join = this.__opts.join, ret = "";- 932
if (!from.length) {- 932
if (!isEmpty(join)) {- 0
throw new QueryError("Need multiple FROM tables if updating/deleteing a dataset with joins");}} else {- 0
var space = this._static.SPACE;- 0
ret = [space, type.toString(), space, this._sourceList(from), this._selectJoinSql()].join("");}- 932
return ret;},_selectLockSql: function () {- 2712
if (this.__opts.lock === "share") {- 0
return this._static.FOR_SHARE;} else {- 2712
return this._super(arguments);}},_selectWithSql: function () {- 4764
var optsWith = this.__opts["with"];- 4764
if (!isEmpty(optsWith) && optsWith.some(function (w) {- 0
return w.recursive;})) {- 0
return this._static.SQL_WITH_RECURSIVE;} else {- 4764
return this._super(arguments);}},_updateFromSql: function () {- 202
return this._joinFromSql("FROM");},_updateTableSql: function () {- 202
return [this._static.SPACE, this._sourceList(this.__opts.from.slice(0, 1))].join("");},_quotedIdentifier: function (c) {- 25184
return format('"%s"', c);},__fullTextStringJoin: function (cols) {- 5
var EMPTY_STRING = this._static.EMPTY_STRING;- 5
cols = toArray(cols).map(function (x) {- 7
return sql.COALESCE(x, EMPTY_STRING);});- 5
cols = flatten(zip(cols, array.multiply([this._static.SPACE], cols.length)));- 5
cols.pop();- 5
return StringExpression.fromArgs(['||'].concat(cols));},insert: function () {- 2226
var args = arguments;- 2226
if (this.__opts.returning) {- 1113
return this._super(arguments);} else {- 1113
var self = this;- 1113
return this.primaryKey(this.__opts.from).chain(function (res) {- 1113
var pks = res.map(function (r) {- 1024
return r.name;});- 1113
var ds = self.returning.apply(self, pks);- 1113
var dsPromise = ds.insert.apply(ds, args), l = res.length;- 1113
if (l) {- 1024
return dsPromise.chain(function (insertRes) {- 1024
if (l === 1) {- 1024
return insertRes.map(function (i) {- 1024
return i[pks[0]];}).pop();} else {- 0
return insertRes.pop();}});} else {- 89
return dsPromise;}});}},primaryKey: function () {- 1113
return this.db.primaryKey(this.__opts.from[0]);},__processFields: function (fields) {- 2689
var col, colOutputIdentifier, i = -1, l, cols = [],outputIdentifier = this.outputIdentifier,selfCols = ( this.__columns = []);- 2689
if (fields && fields.length) {- 2689
l = fields.length;- 2689
while (++i < l) {- 21240
colOutputIdentifier = outputIdentifier(col = fields[i].name);- 21240
selfCols[i] = colOutputIdentifier;- 21240
cols[i] = [colOutputIdentifier, colCallback, col];}}- 2689
return cols;},_literalTimestamp: function (v) {- 28
return this.literal(literal("TIMESTAMP " + this._super(arguments) + ""));},_literalBuffer: function (b) {- 8
return this.literal(literal("decode('" + b.toString("hex") + "', 'hex')"));},getters: {columns: function () {- 6
var ret;- 6
if (this.__columns) {- 0
ret = when(this.__columns);} else {- 6
var self = this;- 6
ret = this.db.schema(this.firstSourceTable).chain(function (schema) {- 6
var columns = (schema ? Object.keys(schema) : []);- 6
self.__columns = columns;- 6
return columns;});}- 6
return ret.promise();},supportsCteInSubqueries: function () {- 0
return true;},supportsDistinctOn: function () {- 11576
return true;},supportsModifyingJoins: function () {- 13652
return true;},supportsTimestampTimezones: function () {- 11574
return true;}}},"static": {ACCESS_SHARE: 'ACCESS SHARE',ACCESS_EXCLUSIVE: 'ACCESS EXCLUSIVE',BOOL_FALSE: 'false',BOOL_TRUE: 'true',COMMA_SEPARATOR: ', ',DELETE_CLAUSE_METHODS: Dataset.clauseMethods("delete", 'qualify with from using where returning'),EXCLUSIVE: 'EXCLUSIVE',EXPLAIN: 'EXPLAIN ',EXPLAIN_ANALYZE: 'EXPLAIN ANALYZE ',FOR_SHARE: ' FOR SHARE',INSERT_CLAUSE_METHODS: Dataset.clauseMethods("insert", 'with into columns values returning'),LOCK: 'LOCK TABLE %s IN %s MODE',NULL: literal('NULL'),QUERY_PLAN: 'QUERY PLAN',ROW_EXCLUSIVE: 'ROW EXCLUSIVE',ROW_SHARE: 'ROW SHARE',SELECT_CLAUSE_METHODS: Dataset.clauseMethods("select", '' +'qualify with distinct columns from join where group having compounds order limit lock'),SHARE: 'SHARE',SHARE_ROW_EXCLUSIVE: 'SHARE ROW EXCLUSIVE',SHARE_UPDATE_EXCLUSIVE: 'SHARE UPDATE EXCLUSIVE',SQL_WITH_RECURSIVE: "WITH RECURSIVE ",TIMESTAMP_FORMAT: "yyyy-MM-dd HH:mm:ss.SSS",TIME_FORMAT: "HH:mm:ss.SSS",UPDATE_CLAUSE_METHODS: Dataset.clauseMethods("update", 'with table set from where returning'),XOR_OP: ' # ',CRLF: "\r\n",BLOB_RE: /[\000-\037\047\134\177-\377]/,WINDOW: " WINDOW ",EMPTY_STRING: literal("''")}}).as(exports, "PostgresDataset");- 1
var DB = define(Database, {instance: {EXCLUDE_SCHEMAS: /pg_*|information_schema/i,PREPARED_ARG_PLACEHOLDER: new LiteralString('$'),RE_CURRVAL_ERROR: /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/,SYSTEM_TABLE_REGEXP: /^pg|sql/,type: "postgres",constructor: function () {- 31
this._super(arguments);- 31
this.__primaryKeys = {};- 31
this.__listeners = {};},createConnection: function (opts) {- 64
delete opts.query;- 64
var self = this, ret;- 64
var conn = new pg.Client(merge({}, opts, {typeCast: false}));- 64
conn.on("error", function (err) {- 0
self.logWarn("Connection from " + self.uri + " errored removing from pool and reconnecting");- 0
self.logWarn(err.stack);- 0
ret.errored = true;- 0
self.pool.removeConnection(ret);});- 64
conn.on("end", function () {- 63
if (!ret.closed) {- 0
self.logWarn("Connection from " + self.uri + " unexpectedly ended");- 0
self.pool.removeConnection(ret);- 0
ret.closed = true;}});- 64
conn.connect();- 64
ret = new Connection(conn);- 64
return ret;},closeConnection: function (conn) {- 63
return conn.closeConnection();},validate: function (conn) {- 1945
return new Promise().callback(!(conn.errored)).promise();},listen: function (channel, cb, opts) {- 3
opts = opts || {};- 3
channel = this.__quoteSchemaTable(channel);- 3
var listeningChannel = channel.toLowerCase();- 3
var timeout = opts.timeout || 30000,ret,connected = true, errored = false;- 3
var self = this;- 3
if (this.quoteIdentifiers) {- 0
listeningChannel = listeningChannel.replace(/^"|"$/g, "");}- 3
var connectionTimeout = setTimeout(function () {- 0
if (!connected) {- 0
errored = true;- 0
ret.errback(new Error("Listen: Unable to connect to " + channel));}}, timeout);- 3
ret = this._getConnection().chain(function (conn) {- 3
function __listener(message) {- 8
if (message.channel === listeningChannel) {- 8
cb(JSON.parse(message.payload));}}- 3
if (!errored) {- 3
connected = true;- 3
clearTimeout(connectionTimeout);- 3
conn.connection.on('notification', __listener);- 3
var listeners = conn.__listeners;- 3
if (!listeners) {- 2
listeners = conn.__listeners = {};}- 3
listeners[channel] = __listener;- 3
var sql = self.__listenSql(channel);- 3
return self.__logAndExecute(sql, function () {- 3
return conn.query(sql);}).chain(function () {- 3
self.__listeners[channel] = conn;});}});- 3
return ret;},listenOnce: function (channel, cb, opts) {- 2
var self = this;- 2
var ret = new Promise(), called = false;- 2
this.listen(channel, function (payload) {//ensure we are not called twice- 7
if (!called) {- 2
called = true;- 2
self.unListen(channel).chain(function () {- 2
ret.callback(payload);- 2
self = ret = null;}).addErrback(ret);}}, opts).addErrback(ret);- 2
return ret.promise();},unListen: function (channel) {- 4
var ret = new Promise().callback(), conn;- 4
channel = this.__quoteSchemaTable(channel);- 4
if (channel in this.__listeners && (conn = this.__listeners[channel])) {- 2
var sql = this.__unListenSql(channel), self = this;- 2
return this.__logAndExecute(sql, sql, function () {- 2
return conn.query(sql);}).chain(function () {- 2
delete self.__listeners[channel];- 2
conn.connection.removeListener('notification', conn.__listeners[channel]);- 2
return self._returnConnection(conn);});}- 2
return ret.promise();},notify: function (channel, payload) {- 7
return this.executeDdl(this.__notifySql(this.__quoteSchemaTable(channel), payload));},// Use the pg_* system tables to determine indexes on a tableindexes: function (table, opts) {- 0
opts = opts || {};- 0
var m = this.outputIdentifierFunc;- 0
var im = this.inputIdentifierFunc;- 0
var parts = this.__schemaAndTable(table), schema = parts[0];- 0
table = parts[1];- 0
return this.serverVersion().chain(function (version) {- 0
var attNums;- 0
if (version >= 80100) {- 0
attNums = sql.ANY("ind__indkey");} else {- 0
attNums = [];- 0
for (var i = 0; i < 32; i++) {- 0
attNums.push(new Subscript("ind__indkey", [i]));}}- 0
var orderRange = [];- 0
for (var j = 0; j < 32; j++) {- 0
orderRange.push(new Subscript("ind__indkey", [j]));}- 0
orderRange = sql["case"](orderRange, 32, "att__attnum");- 0
var ds = this.metadataDataset.from("pg_class___tab").join("pg_index___ind", [[identifier("indrelid"), identifier("oid")],[im(table), "relname"]]).join("pg_class___indc", [[identifier("oid"), identifier("indexrelid")]]).join("pg_attribute___att", [[identifier("attrelid"), identifier("tab__oid")],[identifier("attnum"), attNums]]).filter({"indc__relkind": 'i', "ind__indisprimary": false, indexprs: null, indpred: null}).order("indc__relname", orderRange).select("indc__relname___name", "ind__indisunique___unique", "att__attname___column");- 0
if (schema) {- 0
ds = ds.join("pg_namespace___nsp", {oid: identifier("tab__relnamespace"), nspname: schema.toString()});}- 0
if (version >= 80200) {- 0
ds = ds.filter({indisvalid: true});}- 0
if (version >= 80300) {- 0
ds = ds.filter({indisready: true, indcheckxmin: false});}- 0
var indexes = {};- 0
return ds.forEach(function (r) {- 0
var ident = m(r.name), i = indexes[ident];- 0
if (!i) {- 0
i = indexes[ident] = {columns: [], unique: r.unique};}- 0
i.columns.push(r.column);}).chain(function () {- 0
return indexes;});});},locks: function () {- 2
return this.dataset.from("pg_class").join("pg_locks", {relation: identifier("relfilenode")}).select("pg_class__relname", identifier("pg_locks").all());},// Get version of postgres server, used for determined capabilities.serverVersion: function () {- 95
if (!this.__serverVersion) {- 30
var self = this;- 30
this.__serverVersion = this.get(identifier("version").sqlFunction).chain(function (version) {- 30
var m = version.match(/PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/);- 30
version = (parseInt(m[1], 10) * 10000) + (parseInt(m[2], 10) * 100) + parseInt(m[3], 10);- 30
self._serverVersion = version;- 30
return version;});}- 95
return this.__serverVersion.promise();},/*** Return an array of table names in the current database.* The dataset used is passed to the block if one is provided,* otherwise, an a promise resolved with an array of table names.** Options:* @param {Object} [opts = {}] options* @param {String|patio.sql.Identifier} [opts.schema] The schema to search (default_schema by default)* @param {Function} [cb = null] an optional callback that is invoked with the dataset to retrieve tables.* @return {Promise} a promise resolved with the table names or the result of the cb if one is provided.*/tables: function (opts, cb) {- 0
return this.__pgClassRelname('r', opts, cb);},/*** Return an array of view names in the current database.** Options:* @param {Object} [opts = {}] options* @param {String|patio.sql.Identifier} [opts.schema] The schema to search (default_schema by default)* @return {Promise} a promise resolved with the view names.*/views: function (opts) {- 0
return this.__pgClassRelname('v', opts);},primaryKey: function (table, opts) {- 1113
var ret, quotedTable = this.__quoteSchemaTable(table).toString(), pks = this.__primaryKeys;- 1113
if (pks.hasOwnProperty(quotedTable.toString())) {- 1052
ret = pks[quotedTable];} else {- 61
ret = (pks[quotedTable] = this.__primarykey(table));}- 1113
return ret.promise();},createMaterializedView: function (name, query, opts) {- 1
opts = opts || {};- 1
opts.materialized = true;- 1
return this.createView(name, query, opts);},dropMaterializedView: function (names, opts) {- 1
var args = argsToArray(arguments);- 1
if (isHash(args[args.length - 1])) {- 0
opts = args.pop();} else {- 1
opts = {};}- 1
opts.materialized = true;- 1
return this.dropView(args, opts);},refreshMaterializedView: function (names, opts) {- 4
if (isArray(names)) {- 2
var self = this, withNoData = opts.noData;- 2
return asyncArray(names).forEach(function (name) {- 2
var sql = "REFRESH MATERIALIZED VIEW %s";- 2
withNoData && (sql += " WITH NO DATA");- 2
return self.executeDdl(format(sql, self.__quoteSchemaTable(name)));}, null, 1);} else {- 2
var args = argsToArray(arguments);- 2
opts = isHash(args[args.length - 1]) ? args.pop() : {};- 2
return this.refreshMaterializedView(args, opts);}},__primarykey: function (table) {- 61
var parts = this.__schemaAndTable(table);- 61
var m2 = this.inputIdentifierFunc;- 61
var schema = parts[0];- 61
table = parts[1];- 61
var ds = this.from(table).select("pg_attribute__attname___name").from("pg_index", "pg_class", "pg_attribute", "pg_namespace").where([[identifier("pg_class__oid"), identifier("pg_attribute__attrelid")],[identifier("pg_class__relnamespace"), identifier("pg_namespace__oid")],[identifier("pg_class__oid"), identifier("pg_index__indrelid")],[identifier("pg_index__indkey").sqlSubscript(0), identifier("pg_attribute__attnum")],[identifier("indisprimary"), true],[identifier("pg_class__relname"), m2(table.toString())]]);- 61
if (schema) {- 0
ds.filter({"pg_namespace__nspname": m2(schema)});}- 61
return ds.all();},_indKeySql: function (key, version) {- 93
var ret = sql.identifier(key);- 93
if (version < 90000) {- 0
ret = sql.literal("string_to_array(textin(int2vectorout(?)), ' ')", ret);}- 93
return ret;},schemaParseTable: function (tableName, opts) {- 93
var self = this,m = this.outputIdentifierFunc,m2 = this.inputIdentifierFunc;- 93
return this.serverVersion().chain(function (serverVersion) {- 93
var ds = self.metadataDataset.select("pg_attribute__attname___name",sql["format_type"]("pg_type__oid", "pg_attribute__atttypmod").as("dbtype"),sql["pg_get_expr"]("pg_attrdef__adbin", "pg_class__oid").as(literal('"default"')),sql.NOT("pg_attribute__attnotnull").as("allownull"),sql.COALESCE(BooleanExpression.fromValuePairs({"pg_attribute__attnum": sql.ANY(self._indKeySql("pg_index__indkey", serverVersion))}), false).as("primarykey"),"pg_namespace__nspname").from("pg_class").join("pg_attribute", {attrelid: identifier("oid")}).join("pg_type", {oid: identifier("atttypid")}).join("pg_namespace", {oid: identifier("pg_class__relnamespace")}).leftOuterJoin("pg_attrdef", {adrelid: identifier("pg_class__oid"), adnum: identifier("pg_attribute__attnum")}).leftOuterJoin("pg_index", {indrelid: identifier("pg_class__oid"), indisprimary: true}).filter({"pg_attribute__attisdropped": false}).filter({"pg_attribute__attnum": {gt: 0}}).filter({"pg_class__relname": m2(tableName)}).order("pg_attribute__attnum");- 93
ds = self.__filterSchema(ds, opts);- 93
var currentSchema = null;- 93
return ds.map(function (row) {- 768
row.allowNull = row.allownull;- 768
delete row.allownull;- 768
row.primaryKey = row.primarykey;- 768
delete row.primarykey;- 768
row.dbType = row.dbtype;- 768
delete row.dbtype;- 768
var sch = row.nspname;- 768
delete row.nspname;- 768
if (currentSchema) {- 675
if (sch !== currentSchema) {- 0
var error = new Error("columns from two tables were returned please specify a schema");- 0
self.logError(error);}} else {- 93
currentSchema = sch;}- 768
if (isBlank(row["default"])) {- 679
row["default"] = null;}- 768
row.type = self.schemaColumnType(row.dbType);- 768
var fieldName = m(row.name);- 768
delete row.name;- 768
return [fieldName, row];});});},__commitTransaction: function (conn, opts) {- 744
opts = opts || {};- 744
var s = opts.prepare;- 744
if (s && this.__transactionDepth <= 1) {- 0
return this.__logConnectionExecute(conn, ["PREPARE TRANSACTION ", this.literal(s)].join(""));} else {- 744
return this._super(arguments);}},//Backbone of the tables and views support.__pgClassRelname: function (type, opts, cb) {- 0
var ret;- 0
var ds = this.metadataDataset.from("pg_class").filter({relkind: type}).select("relname").exclude({relname: {like: this.SYSTEM_TABLE_REGEXP}}).join("pg_namespace", {oid: identifier("relnamespace")});- 0
ds = this.__filterSchema(ds, opts);- 0
var m = this.outputIdentifierFunc;- 0
if (cb) {- 0
ret = when(cb(ds));} else {- 0
ret = ds.map(function (r) {- 0
return m(r.relname);});}- 0
return ret.promise();},//If opts includes a :schema option, or a default schema is used, restrict the dataset to// that schema. Otherwise, just exclude the default PostgreSQL schemas except for public.__filterSchema: function (ds, opts) {- 93
opts = opts || {};- 93
var schema = opts.schema, ret = ds;- 93
if (schema) {- 0
ds = ds.filter({"pg_namespace__nspname": schema});} else {- 93
ds = ds.exclude({"pg_namespace__nspname": this.EXCLUDE_SCHEMAS});}- 93
return ds;},__notifySql: function (channel, payload) {- 7
return format("NOTIFY %s %s", channel, payload ? ", " + this.literal(JSON.stringify(payload)) : "");},__listenSql: function (channel) {- 3
return format("LISTEN %s", channel);},__unListenSql: function (channel) {- 2
return format("UNLISTEN %s", channel);},__dropViewSql: function (name, opts) {- 1
var sql = "DROP";- 1
if (opts.materialized) {- 1
sql += " MATERIALIZED";}- 1
sql += " VIEW";- 1
if (opts.ifExists) {- 0
sql += " IF EXISTS";}- 1
sql += " %s";- 1
if (opts.cascade) {- 0
sql += " CASCADE";}- 1
return format(sql, this.__quoteSchemaTable(name));},__createViewSql: function (name, source, opts) {- 1
var sql = "CREATE";- 1
opts = opts || {};- 1
if (opts.replace) {- 0
sql += " OR REPLACE";}- 1
if (opts.materialized) {- 1
sql += " MATERIALIZED";- 0
} else if (opts.recursize) {- 0
sql += " RECURSIVE";- 0
} else if (opts.temporary || opts.temp) {- 0
sql += " TEMPORARY";}- 1
sql += " VIEW %s AS %s";- 1
return format(sql, this.__quoteSchemaTable(name), source);},__indexDefinitionSql: function (tableName, index) {- 9
tableName = stringToIdentifier(tableName);- 9
var cols = index.columns.map(function (col) {- 10
return stringToIdentifier(col);}),indexName = index.name || this.__defaultIndexName(tableName, cols),o = index.opclass,indexType = index.type,unique = index.unique ? "UNIQUE" : "",filter = index.where || index.filter,expr;- 9
filter = filter ? ["WHERE ", this.__filterExpr(filter)].join("") : "";- 9
if (isDefined(o)) {- 1
expr = ["(", cols.map(function (c) {- 1
return [this.literal(c), o].join(" ");}, this).join(", "), ")"].join("");} else {- 8
expr = this.literal(toArray(cols));}- 9
switch (indexType) {case "fullText":- 2
expr = ["(to_tsvector(", this.literal(index.language || "simple"), ", ", this.literal(this.dataset.__fullTextStringJoin(cols)), "))"].join("");- 2
indexType = "gin";- 2
break;case "spatial" :- 1
indexType = "gist";- 1
break;}- 9
return ["CREATE", unique, "INDEX", this.__quoteIdentifier(indexName), "ON", this.__quoteSchemaTable(tableName), indexType ? "USING " + indexType : "", expr, filter].join(" ");},/*todo might need this?__insertResult:function (conn, table, values) {},*/__renameTableSql: function (name, newName) {- 1
return ["ALTER TABLE ", this.__quoteSchemaTable(name), " RENAME TO ", this.__quoteIdentifier(this.__schemaAndTable(newName).pop())].join("");},__schemaAutoincrementingPrimaryKey: function (schema) {- 0
return this._super(arguments) && schema.dbType.match(/^(?:integer|bigint)$/i) && schema["default"].match(/^nextval/i);},__typeLiteralGenericNumeric: function (column) {- 2
return column.size ? format("numeric(%s)", array.toArray(column.size).join(', ')) : column.isInt ? "integer" : column.isDouble ? "double precision" : "numeric";},__typeLiteralGenericDateTime: function (column) {- 6
return "timestamp";},//handle bigserial__typeLiteralGenericBigint: function (column) {- 1
return column.serial ? "bigserial" : this.__typeLiteralSpecific(column);},__typeLiteralGenericBlob: function (column) {- 8
return "bytea";},//handle serial type__typeLiteralGenericInteger: function (column) {- 124
return column.serial ? "serial" : this.__typeLiteralSpecific(column);},// PostgreSQL prefers the text datatype. If a fixed size is requested,// the char type is used. If the text type is specifically// disallowed or there is a size specified, use the varchar type.// Otherwise use the type type.__typeLiteralGenericString: function (column) {- 143
if (column.fixed) {- 0
return ["char(", column.size || 255, ")"].join("");- 143
} else if (column.text === false || column.size) {- 119
return ["varchar(", column.size || 255, ")"].join("");} else {- 24
return 'text';}},getters: {connectionExecuteMethod: function () {- 1488
return "query";},dataset: function () {- 804
return new DS(this);},serialPrimaryKeyOptions: function () {- 62
return {primaryKey: true, serial: true, type: "integer"};},supportsSavepoints: function () {- 8497
return true;},supportsTransactionIsolationLevels: function () {- 744
return true;},identifierInputMethodDefault: function () {- 0
return null;},identifierOutputMethodDefault: function () {- 0
return null;}}},"static": {init: function () {- 1
this.setAdapterType("pg");}}}).as(exports, "PostgresDatabase");
|
adapters/mysql.js
|
Coverage77.21
SLOC891
LOC351
Missed80
|
- 1
var mysql = require("mysql"),comb = require("comb"),hitch = comb.hitch,asyncArray = comb.async.array,define = comb.define,merge = comb.merge,string = comb.string,argsToArray = comb.argsToArray,format = string.format,Promise = comb.Promise,isString = comb.isString,array = comb.array,toArray = array.toArray,isArray = comb.isArray,isHash = comb.isHash,when = comb.when,isInstanceOf = comb.isInstanceOf,isFunction = comb.isFunction,isUndefinedOrNull = comb.isUndefinedOrNull,isUndefined = comb.isUndefined,isEmpty = comb.isEmpty,QueryError = require("../errors").QueryError,Dataset = require("../dataset"),Database = require("../database"),sql = require("../sql").sql,DateTime = sql.DateTime,Time = sql.Time,Year = sql.Year,Double = sql.Double,stream = require("stream"),PassThroughStream = stream.PassThrough,pipeAll = require("../utils").pipeAll,patio, DB;- 1
var convertDate = function (v, m, convertDateTime) {- 15
v = ("" + v);- 15
try {- 15
return patio[m](v);} catch (e) {- 15
if (convertDateTime === null) {- 3
return null;- 12
} else if (convertDateTime === String || (isString(convertDateTime) && convertDateTime.match(/string/i))) {- 9
return v;} else {- 3
throw e;}}};- 1
var Connection = define(null, {instance: {connection: null,errored: false,closed: false,constructor: function (conn) {- 4
this.connection = conn;},closeConnection: function () {- 3
var ret = new Promise();- 3
this.closed = true;- 3
this.connection.end(hitch(ret, ret.resolve));- 3
return ret.promise();},stream: function (query) {- 2
var ret;- 2
if (!this.closed) {- 2
try {- 2
ret = this.connection.query(query).stream();} catch (e) {- 0
patio.logError(e);}} else {- 0
ret = new PassThroughStream();- 0
setImmediate(function () {- 0
ret.emit("error", new Error("Connection already closed"));});}- 2
return ret;},query: function (query) {- 295
var ret = new Promise();- 295
if (!this.closed) {- 295
try {- 295
this.connection.setMaxListeners(0);- 295
this.connection.query(query, hitch(ret, ret.resolve));} catch (e) {- 0
patio.logError(e);- 0
ret.errback(e);}} else {- 0
ret.errback(new Error("Connection already closed"));}- 295
return ret.promise();}}});- 1
var DS = define(Dataset, {instance: {__providesAccurateRowsMatched: false,__supportsDistinctOn: true,__supportsIntersectExcept: false,__supportsModifyingJoins: true,__supportsTimestampUsecs: false,// MySQL specific syntax for LIKE/REGEXP searches, as well as// string concatenation.complexExpressionSql: function (op, args) {- 24
var likeOps = ["~", "~*", "LIKE", "ILIKE"];- 24
var notLikeOps = ["!~", "!~*", "NOT LIKE", "NOT ILIKE"];- 24
var regExpOps = ["~", "!~", "~*", "!~*"];- 24
var binaryOps = ["~", "!~", "LIKE", "NOT LIKE"];- 24
if (likeOps.indexOf(op) !== -1 || notLikeOps.indexOf(op) !== -1) {- 10
return format("(%s%s %s%s %s)", this.literal(args[0]), notLikeOps.indexOf(op) !== -1 ? " NOT" : "",regExpOps.indexOf(op) !== -1 ? "REGEXP" : "LIKE", binaryOps.indexOf(op) !== -1 ? " BINARY" : "",this.literal(args[1]));- 14
} else if (op === "||") {- 5
if (args.length > 1) {- 3
return format("CONCAT(%s)", args.map(this.literal, this).join(", "));} else {- 2
return this.literal(args[0]);}- 9
} else if (op === "B~") {- 0
return format("CAST(~%s AS SIGNED INTEGER)", this.literal(args[0]));} else {- 9
return this._super(arguments);}},// Use GROUP BY instead of DISTINCT ON if arguments are provided.distinct: function (args) {- 2
args = argsToArray(arguments);- 2
return !args.length ? this._super(arguments) : this.group.apply(this, args);},//Return a cloned dataset which will use LOCK IN SHARE MODE to lock returned rows.forShare: function () {- 1
return this.lockStyle("share");},//Adds full text filterfullTextSearch: function (cols, terms, opts) {- 3
opts = opts || {};- 3
cols = toArray(cols).map(this.stringToIdentifier, this);- 3
return this.filter(sql.literal(this.fullTextSql(cols, terms, opts)));},//MySQL specific full text search syntax.fullTextSql: function (cols, term, opts) {- 3
opts = opts || {};- 3
return format("MATCH %s AGAINST (%s%s)", this.literal(toArray(cols)),this.literal(toArray(term).join(" ")), opts.boolean ? " IN BOOLEAN MODE" : "");},//MySQL allows HAVING clause on ungrouped datasets.having: function (cond, cb) {- 3
var args = argsToArray(arguments);- 3
return this._filter.apply(this, ["having"].concat(args));},// Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.//Raises an error on use of :full_outer type, since MySQL doesn't support it.joinTable: function (type, table, expr, tableAlias) {- 12
tableAlias = tableAlias || {};- 12
if (type === "cross" && !isUndefinedOrNull(expr)) {- 1
type = "inner";}- 12
if (type === "fullOuter") {- 1
throw new QueryError("MySQL does not support FULL OUTER JOIN");}- 11
return this._super(arguments, [type, table, expr, tableAlias]);},// Transforms :natural_inner to NATURAL LEFT JOIN and straight to//STRAIGHT_JOIN._joinTypeSql: function (joinType) {- 11
if (joinType === "straight") {- 2
return "STRAIGHT_JOIN";- 9
} else if (joinType === "naturalInner") {- 1
return "NATURAL LEFT JOIN";} else {- 8
return this._super(arguments);}},insertIgnore: function () {- 2
return this.mergeOptions({insertIgnore: true});},onDuplicateKeyUpdate: function (args) {- 3
args = argsToArray(arguments).map(function (c) {- 5
return isString(c) ? this.stringToIdentifier(c) : c;}, this);- 3
return this.mergeOptions({onDuplicateKeyUpdate: args});},// MySQL specific syntax for inserting multiple values at once.multiInsertSql: function (columns, values) {- 8
return [this.insertSql(columns, sql.literal('VALUES ' + values.map(function (r) {- 16
return this.literal(toArray(r));}, this).join(this._static.COMMA_SEPARATOR)))];},//MySQL uses the nonstandard ` (backtick) for quoting identifiers._quotedIdentifier: function (c) {- 51
return format("`%s`", c);},// MySQL specific syntax for REPLACE (aka UPSERT, or update if exists,//insert if it doesn't).replaceSql: function (values) {- 9
var ds = this.mergeOptions({replace: true});- 9
return ds.insertSql.apply(ds, argsToArray(arguments));},//If this is an replace instead of an insert, use replace instead_insertSql: function () {- 55
return this.__opts.replace ? this._clauseSql("replace") : this._super(arguments);},//Consider the first table in the joined dataset is the table to delete//from, but include the others for the purposes of selecting rows._deleteFromSql: function (sql) {- 12
if (this._joinedDataset) {- 0
return format(" %s FROM %s%s", this._sourceList(this.__opts.from[0]), this._sourceList(this.__opts.from), this._selectJoinSql());} else {- 12
return this._super(arguments);}},//alias replace_clause_methods insert_clause_methods//MySQL doesn't use the standard DEFAULT VALUES for empty values._insertColumnsSql: function (sql) {- 55
var values = this.__opts.values;- 55
if (isArray(values) && !values.length) {- 9
return " ()";} else {- 46
return this._super(arguments);}},//MySQL supports INSERT IGNORE INTO_insertIgnoreSql: function (sql) {- 55
return this.__opts.insertIgnore ? " IGNORE" : "";},//MySQL supports INSERT ... ON DUPLICATE KEY UPDATE_insertOnDuplicateKeyUpdateSql: function (sql) {- 55
return this.__opts.onDuplicateKeyUpdate ? this.onDuplicateKeyUpdateSql() : "";},//MySQL doesn't use the standard DEFAULT VALUES for empty values._insertValuesSql: function (sql) {- 55
var values = this.__opts.values;- 55
if (isArray(values) && !values.length) {- 9
return " VALUES ()";} else {- 46
return this._super(arguments);}},//MySQL allows a LIMIT in DELETE and UPDATE statements.limitSql: function (sql) {- 14
return this.__opts.limit ? format(" LIMIT %s", this.__opts.limit) : "";},_deleteLimitSql: function () {- 12
return this.limitSql.apply(this, arguments);},_updateLimitSql: function () {- 2
return this.limitSql.apply(this, arguments);},//MySQL specific syntax for ON DUPLICATE KEY UPDATEonDuplicateKeyUpdateSql: function () {- 3
var ret = "";- 3
var updateCols = this.__opts.onDuplicateKeyUpdate;- 3
if (updateCols) {- 3
var updateVals = null, l, last;- 3
if ((l = updateCols.length) > 0 && isHash((last = updateCols[l - 1]))) {- 2
updateVals = last;- 2
updateCols = l === 2 ? [updateCols[0]] : updateCols.slice(0, l - 2);}- 3
var updating = updateCols.map(function (c) {- 3
var quoted = this.quoteIdentifier(c);- 3
return format("%s=VALUES(%s)", quoted, quoted);}, this);- 3
for (var i in updateVals) {- 2
if (i in updateVals) {- 2
updating.push(format("%s=%s", this.quoteIdentifier(i), this.literal(updateVals[i])));}}- 3
if (updating || updateVals) {- 3
ret =format(" ON DUPLICATE KEY UPDATE %s", updating.join(this._static.COMMA_SEPARATOR));}}- 3
return ret;},//Support FOR SHARE locking when using the :share lock style._selectLockSql: function (sql) {- 88
return this.__opts.lock === "share" ? this._static.FOR_SHARE : this._super(arguments);},// Delete rows matching this datasetremove: function () {- 12
return this.executeDui(this.deleteSql).chain(function (c, info) {- 12
return c.affectedRows;});},__processFields: function (fields) {- 88
var cols = [], i = -1, l = fields.length, col, fieldName, type, length, colIdentifier,outputIdentifier = this.outputIdentifier,selfCols = ( this.__columns = []);- 88
while (++i < l) {- 308
col = fields[i];- 308
fieldName = col.name;- 308
type = col.type;- 308
length = col.fieldLength;- 308
colIdentifier = outputIdentifier(fieldName);- 308
selfCols[i] = colIdentifier;- 308
cols[i] = [colIdentifier, DB.convertMysqlType(type === 1 && length !== 1 ? 2 : type), fieldName];}- 88
return cols;},//Don't allow graphing a dataset that splits multiple statementsgraph: function () {- 0
if (this.__opts.splitMultipleResultSels) {- 0
throw new QueryError("Can't graph a dataset that splits multiple result sets");}- 0
this._super(arguments);},//Insert a new value into this datasetinsert: function () {- 36
return this.executeDui(this.insertSql.apply(this, arguments)).chain(function (c, info) {- 36
return c.insertId;});},// Replace (update or insert) the matching row.replace: function () {- 9
return this.executeDui(this.replaceSql.apply(this, arguments)).chain(function (c, info) {- 9
return c.insertId;});},splitMultipleResultSets: function () {- 0
if (this.__opts.graph) {- 0
throw new QueryError("Can't split multiple statements on a graphed dataset");}- 0
var ds = this.mergeOptions({splitMultipleResultSets: true});- 0
var rowCb = this.rowCb;- 0
if (rowCb) {- 0
ds.rowCb = function (x) {- 0
return x.map(rowCb, this);};}- 0
return ds;},//Update the matching rows.update: function () {- 0
return this.executeDui(this.updateSql.apply(this, arguments)).chain(function (c, info) {- 0
return c.affectedRows;});},//Set the :type option to select if it hasn't been set.execute: function (sql, opts) {- 89
opts = opts || {};- 89
return this._super([sql, merge({type: "select"}, opts)]);},//Set the :type option to :select if it hasn't been set.executeDui: function (sql, opts) {- 65
opts = opts || {};- 65
return this._super([sql, merge({type: "dui"}, opts)]);},_literalString: function (v) {- 64
return "'" + v.replace(/[\0\n\r\t\\\'\"\x1a]/g, function (s) {- 1
switch (s) {case "0":- 0
return "\\0";case "\n":- 0
return "\\n";case "\r":- 0
return "\\r";case "\b":- 0
return "\\b";case "\t":- 0
return "\\t";case "\x1a":- 0
return "\\Z";default:- 1
return "\\" + s;}}) + "'";}},"static": {BOOL_TRUE: '1',BOOL_FALSE: '0',COMMA_SEPARATOR: ', ',FOR_SHARE: ' LOCK IN SHARE MODE',DELETE_CLAUSE_METHODS: Dataset.clauseMethods("delete", "qualify from where order limit"),INSERT_CLAUSE_METHODS: Dataset.clauseMethods("insert", "ignore into columns values onDuplicateKeyUpdate"),REPLACE_CLAUSE_METHODS: Dataset.clauseMethods("insert", "ignore into columns values onDuplicateKeyUpdate"),SELECT_CLAUSE_METHODS: Dataset.clauseMethods("select", "qualify distinct columns from join where group having compounds order limit lock"),UPDATE_CLAUSE_METHODS: Dataset.clauseMethods("update", "table set where order limit")}});- 1
DB = define(Database, {instance: {PRIMARY: 'PRIMARY',type: "mysql",__supportsSavePoints: true,__supportsTransactionIsolationLevels: true,createConnection: function (opts) {- 4
delete opts.query;- 4
var self = this, ret;- 4
var conn = mysql.createConnection(merge({}, opts, {typeCast: false}));- 4
conn.on("error", function (err) {- 0
self.logWarn("Connection from " + self.uri + " errored removing from pool and reconnecting");- 0
self.logWarn(err.stack);- 0
ret.errored = true;- 0
self.pool.removeConnection(ret);});- 4
conn.connect();- 4
ret = new Connection(conn);- 4
return ret;},closeConnection: function (conn) {- 3
return conn.closeConnection();},validate: function (conn) {- 279
return new Promise().callback(!(conn.errored)).promise();},// MySQL's cast rules are restrictive in that you can't just cast to any possible// database type.castTypeLiteral: function (type) {- 0
var ret = null, meth;- 0
if (isString(type)) {- 0
ret = this._static.CAST_TYPES[type] || this._super(arguments);- 0
} else if (type === String) {- 0
meth += "CHAR";- 0
} else if (type === Number) {- 0
meth += "DECIMAL";- 0
} else if (type === DateTime) {- 0
meth += "DATETIME";- 0
} else if (type === Year) {- 0
meth += "Year";- 0
} else if (type === Time) {- 0
meth += "DATETIME";- 0
} else if (type === Double) {- 0
meth += "DECIMAL";} else {- 0
ret = this._super(arguments);}- 0
return ret;},// Use SHOW INDEX FROM to get the index information for the table.indexes: function (table, opts) {- 3
var indexes = {};- 3
var removeIndexes = [];- 3
var m = this.outputIdentifierFunc;- 3
var im = this.inputIdentifierFunc;- 3
return this.metadataDataset.withSql("SHOW INDEX FROM ?", isInstanceOf(table, sql.Identifier) ? table : sql.identifier(im(table))).forEach(function (r) {- 2
var name = r[m("Key_name")];- 2
if (name !== "PRIMARY") {- 2
name = m(name);- 2
if (r[m("Sub_part")]) {- 1
removeIndexes.push(name);}- 2
var i = indexes[name] || (indexes[name] = {columns: [], unique: r[m("Non_unique")] !== 1});- 2
i.columns.push(m(r[m("Column_name")]));}}).chain(function () {- 3
var r = {};- 3
for (var i in indexes) {- 2
if (removeIndexes.indexOf(i) === -1) {- 1
r[i] = indexes[i];}}- 3
return r;});},// Get version of MySQL server, used for determined capabilities.serverVersion: function () {- 1
var ret;- 1
if (!this.__serverVersion) {- 1
var self = this;- 1
ret = this.get(sql.version().sqlFunction).chain(function (version) {- 1
var m = version.match(/(\d+)\.(\d+)\.(\d+)/);- 1
return (self._serverVersion = (parseInt(m[1], 10) * 10000) + (parseInt(m[2], 10) * 100) + parseInt(m[3], 10));});} else {- 0
ret = new Promise().callback(this._serverVersion);}- 1
return ret.promise();},//Return an array of strings specifying table names in the current database.tables: function (opts) {- 0
var m = this.outputIdentifierFunc;- 0
return this.metadataDataset.withSql('SHOW TABLES').map(function (r) {- 0
return m(r[Object.keys(r)[0]]);});},use: function (dbName) {- 0
var self = this;- 0
return this.disconnect().chain(function () {- 0
return self.run("USE " + dbName).chain(function () {- 0
self.opts.database = dbName;- 0
self.schemas = {};- 0
return self;});});},//Use MySQL specific syntax for rename column, set column type, and// drop index cases.__alterTableSql: function (table, op) {- 21
var ret = new Promise(), self = this;- 21
if (op.op === "addColumn") {- 7
var related = op.table;- 7
if (related) {- 2
delete op.table;- 2
ret = this._super(arguments).chain(function (sql) {- 2
op.table = related;- 2
return [sql, format("ALTER TABLE %s ADD FOREIGN KEY (%s)%s",self.__quoteSchemaTable(table), self.__quoteIdentifier(op.name),self.__columnReferencesSql(op))];});} else {- 5
ret = this._super(arguments);}- 14
} else if (['renameColumn', "setColumnType", "setColumnNull", "setColumnDefault"].indexOf(op.op) !== -1) {- 11
ret = this.schema(table).chain(function (schema) {- 11
var name = op.name;- 11
var opts = schema[Object.keys(schema).filter(function (i) {- 28
return i === name;})[0]];- 11
opts = merge({}, opts || {});- 11
opts.name = op.newName || name;- 11
opts.type = op.type || opts.dbType;- 11
opts.allowNull = isUndefined(op["null"]) ? opts.allowNull : op["null"];- 11
opts["default"] = op["default"] || opts.jsDefault;- 11
if (isUndefinedOrNull(opts["default"])) {- 5
delete opts["default"];}- 11
return format("ALTER TABLE %s CHANGE COLUMN %s %s", self.__quoteSchemaTable(table),self.__quoteIdentifier(op.name), self.__columnDefinitionSql(merge(op, opts)));});- 3
} else if (op.op === "dropIndex") {- 0
ret = when(format("%s ON %s", this.__dropIndexSql(table, op), this.__quoteSchemaTable(table)));} else {- 3
ret = this._super(arguments);}- 21
return ret.promise();},//MySQL needs to set transaction isolation before beginning a transaction__beginNewTransaction: function (conn, opts) {- 9
var self = this;- 9
return this.__setTransactionIsolation(conn, opts).chain(function () {- 9
return self.__logConnectionExecute(conn, self.beginTransactionSql);});},// Use XA START to start a new prepared transaction if the :prepare//option is given.__beginTransaction: function (conn, opts) {- 9
opts = opts || {};- 9
var s;- 9
if ((s = opts.prepare)) {- 0
return this.__logConnectionExecute(conn, comb("XA START %s").format(this.literal(s)));} else {- 9
return this._super(arguments);}},// MySQL doesn't allow default values on text columns, so ignore if it the// generic text type is used__columnDefinitionSql: function (column) {- 132
if (isString(column.type) && column.type.match(/string/i) && column.text) {- 1
delete column["default"];}- 132
return this._super(arguments, [column]);},// Prepare the XA transaction for a two-phase commit if the// prepare option is given.__commitTransaction: function (conn, opts) {- 9
opts = opts || {};- 9
var s = opts.prepare, self = this;- 9
if (s) {- 0
return this.__logConnectionExecute(conn, comb("XA END %s").format(this.literal(s))).chain(function () {- 0
return self.__logConnectionExecute(comb("XA PREPARE %s").format(self.literal(s)));});} else {- 9
return this._super(arguments);}},//Use MySQL specific syntax for engine type and character encoding__createTableSql: function (name, generator, options) {- 45
options = options || {};- 45
var engine = options.engine, charset = options.charset, collate = options.collate;- 45
if (isUndefined(engine)) {- 37
engine = this._static.defaultEngine;}- 45
if (isUndefined(charset)) {- 40
charset = this._static.defaultCharset;}- 45
if (isUndefined(collate)) {- 43
collate = this._static.defaultCollate;}- 45
generator.columns.forEach(function (c) {- 114
var t = c.table;- 114
if (t) {- 2
delete c.table;- 2
generator.foreignKey([c.name], t, merge({}, c, {name: null, type: "foreignKey"}));}});- 45
return format(" %s%s%s%s", this._super(arguments), engine ? " ENGINE=" + engine : "",charset ? " DEFAULT CHARSET=" + charset : "", collate ? " DEFAULT COLLATE=" + collate : "");},//Handle MySQL specific index SQL syntax__indexDefinitionSql: function (tableName, index) {- 7
var indexName = this.__quoteIdentifier(index.name || this.__defaultIndexName(tableName,index.columns)), t = index.type, using = "";- 7
var indexType = "";- 7
if (t === "fullText") {- 2
indexType = "FULLTEXT ";- 5
} else if (t === "spatial") {- 1
indexType = "SPATIAL ";} else {- 4
indexType = index.unique ? "UNIQUE " : "";- 4
using = t ? " USING " + t : "";}- 7
return format("CREATE %sINDEX %s%s ON %s %s", indexType, indexName, using,this.__quoteSchemaTable(tableName), this.literal(index.columns.map(function (c) {- 8
return isString(c) ? sql.identifier(c) : c;})));},// Rollback the currently open XA transaction__rollbackTransaction: function (conn, opts) {- 0
opts = opts || {};- 0
var s = opts.prepare;- 0
var logConnectionExecute = comb("__logConnectionExecute");- 0
if (s) {- 0
s = this.literal(s);- 0
var self = this;- 0
return this.__logConnectionExecute(conn, "XA END " + s).chain(function () {- 0
return self.__logConnectionExecute(conn, "XA PREPARE " + s);}).chain(function () {- 0
return self.__logConnectionExecute(conn, "XA ROLLBACK " + s);});} else {- 0
return this._super(arguments);}},// MySQL treats integer primary keys as autoincrementing._schemaAutoincrementingPrimaryKey: function (schema) {- 0
return this._super(arguments) && schema.dbType.match(/int/i);},//Use the MySQL specific DESCRIBE syntax to get a table description.schemaParseTable: function (tableName, opts) {- 15
var m = this.outputIdentifierFunc, im = this.inputIdentifierFunc, self = this;- 15
return this.metadataDataset.withSql("DESCRIBE ?", sql.identifier(im(tableName))).map(function (row) {- 39
var ret = {};- 39
var e = row[m("Extra")];- 39
var allowNull = row[m("Null")];- 39
var key = row[m("Key")];- 39
ret.autoIncrement = e.match(/auto_increment/i) !== null;- 39
ret.allowNull = allowNull.match(/Yes/i) !== null;- 39
ret.primaryKey = key.match(/PRI/i) !== null;- 39
var defaultValue = row[m("Default")];- 39
ret["default"] = Buffer.isBuffer(defaultValue) ? defaultValue.toString() : defaultValue;- 39
if (isEmpty(row["default"])) {- 39
row["default"] = null;}- 39
ret.dbType = row[m("Type")];- 39
if (Buffer.isBuffer(ret.dbType)) {//handle case for field type being returned at 252 (i.e. BLOB)- 39
ret.dbType = ret.dbType.toString();}- 39
ret.type = self.schemaColumnType(ret.dbType.toString("utf8"));- 39
var fieldName = m(row[m("Field")]);- 39
return [fieldName, ret];});},//Convert tinyint(1) type to boolean if convert_tinyint_to_bool is trueschemaColumnType: function (dbType) {- 39
return this._static.convertTinyintToBool && dbType === 'tinyint(1)' ? "boolean" : this._super(arguments);},//MySQL doesn't have a true boolean class, so it uses tinyint(1)__typeLiteralGenericBoolean: function (column) {- 2
return 'tinyint(1)';},getters: {identifierInputMethodDefault: function () {- 1
return null;},identifierOutputMethodDefault: function () {- 1
return null;},connectionExecuteMethod: function () {- 18
return "query";},dataset: function () {- 141
return new DS(this);}}},"static": {__convertTinyintToBool: true,__convertInvalidDateTime: false,CAST_TYPES: {string: "CHAR", integer: "SIGNED", time: "DATETIME", datetime: "DATETIME", numeric: "DECIMAL"},AUTOINCREMENT: 'AUTO_INCREMENT',init: function () {- 1
this.setAdapterType("mysql");},FIELD_TYPES: {FIELD_TYPE_DECIMAL: 0x00,FIELD_TYPE_TINY: 0x01,FIELD_TYPE_SHORT: 0x02,FIELD_TYPE_LONG: 0x03,FIELD_TYPE_FLOAT: 0x04,FIELD_TYPE_DOUBLE: 0x05,FIELD_TYPE_NULL: 0x06,FIELD_TYPE_TIMESTAMP: 0x07,FIELD_TYPE_LONGLONG: 0x08,FIELD_TYPE_INT24: 0x09,FIELD_TYPE_DATE: 0x0a,FIELD_TYPE_TIME: 0x0b,FIELD_TYPE_DATETIME: 0x0c,FIELD_TYPE_YEAR: 0x0d,FIELD_TYPE_NEWDATE: 0x0e,FIELD_TYPE_VARCHAR: 0x0f,FIELD_TYPE_BIT: 0x10,FIELD_TYPE_NEWDECIMAL: 0xf6,FIELD_TYPE_ENUM: 0xf7,FIELD_TYPE_SET: 0xf8,FIELD_TYPE_TINY_BLOB: 0xf9,FIELD_TYPE_MEDIUM_BLOB: 0xfa,FIELD_TYPE_LONG_BLOB: 0xfb,FIELD_TYPE_BLOB: 0xfc,FIELD_TYPE_VAR_STRING: 0xfd,FIELD_TYPE_STRING: 0xfe,FIELD_TYPE_GEOMETRY: 0xff},convertMysqlType: function (type) {- 308
var convert = this.convertTinyintToBool, convertDateTime = this.__convertInvalidDateTime, types = this.FIELD_TYPES;- 308
if (!patio) {- 1
patio = require("../index");}- 308
return function (o) {- 476
var ret = o;- 476
if (o !== null) {- 353
switch (type) {case types.FIELD_TYPE_TIMESTAMP:case types.FIELD_TYPE_DATETIME:- 5
ret = convertDate(o, "stringToDateTime", convertDateTime);- 4
break;case types.FIELD_TYPE_DATE:case types.FIELD_TYPE_NEWDATE:- 5
ret = convertDate(o, "stringToDate", convertDateTime);- 4
break;case types.FIELD_TYPE_TIME:- 5
ret = convertDate(o, "stringToTime", convertDateTime);- 4
break;case types.FIELD_TYPE_TINY:- 0
ret = convert ? parseInt(o, 10) === 1 : parseInt(o, 10);- 0
break;case types.FIELD_TYPE_YEAR:- 0
ret = convertDate(o, "stringToYear", convertDateTime);- 0
break;case types.FIELD_TYPE_SHORT:case types.FIELD_TYPE_LONG:case types.FIELD_TYPE_LONGLONG:case types.FIELD_TYPE_INT24:- 71
ret = parseInt(o, 10);- 71
break;case types.FIELD_TYPE_FLOAT:case types.FIELD_TYPE_DOUBLE:case types.FIELD_TYPE_DECIMAL:// decimal types cannot be parsed as floats because// V8 Numbers have less precision than some MySQL Decimals- 1
ret = parseFloat(o);- 1
break;case types.FIELD_TYPE_TINY_BLOB:case types.FIELD_TYPE_MEDIUM_BLOB:case types.FIELD_TYPE_LONG_BLOB:case types.FIELD_TYPE_BLOB:- 68
ret = new Buffer(o);- 68
break;}}- 473
return ret;};},getters: {convertTinyintToBool: function () {- 347
return this.__convertTinyintToBool;},convertInvalidDateTime: function () {- 0
return this.__convertInvalidDateTime;}},setters: {convertTinyintToBool: function (convert) {- 0
this.__convertTinyintToBool = convert;},convertInvalidDateTime: function (convert) {- 8
this.__convertInvalidDateTime = convert;}}}}).as(exports, "MySQLDatabase");
|
index.js
|
Coverage77.38
SLOC887
LOC84
Missed19
|
/*** @projectName patio* @github https://github.com/C2FO/patio* @includeDoc [Connecting] ../docs-md/connecting.md* @includeDoc [Models] ../docs-md/models.md* @includeDoc [Associations] ../docs-md/associations.md* @includeDoc [Model Inheritance] ../docs-md/model-inheritance.md* @includeDoc [Model Validation] ../docs-md/validation.md* @includeDoc [Model Plugins] ../docs-md/plugins.md* @includeDoc [Querying] ../docs-md/querying.md* @includeDoc [DDL] ../docs-md/DDL.md* @includeDoc [Migrations] ../docs-md/migrations.md* @includeDoc [Logging] ../docs-md/logging.md* @includeDoc [Change Log] ../History.md* @includeDoc [Test Coverage] [../docs-md/coverage.html]** @header [../README.md]*/- 1
var Dataset = require("./dataset"),Database = require("./database"),adapters = require("./adapters"),EventEmitter = require("events").EventEmitter,PatioError = require("./errors").PatioError,migrate = require("./migration"),model = require("./model"),Model = model.Model,plugins = require("./plugins"),comb = require("comb-proxy"),Time = require("./time"),date = comb.date,SQL = require("./sql").sql,Promise = comb.Promise,PromiseList = comb.PromiseList,singleton = comb.singleton,isFunction = comb.isFunction,executeInOrder = comb.executeInOrder,argsToArray = comb.argsToArray,isString = comb.isString,patio = exports;- 1
var LOGGER = comb.logger("patio");- 1
var Patio = singleton([EventEmitter, Time], {instance: {/*** @lends patio.prototype*/__camelize: false,__underscore: false,__inImportOfModels: false,/*** A singleton class that acts as the entry point for all actions performed in patio.** @example** var patio = require("patio");** patio.createConnection(....);** patio.camelize = true;* patio.quoteIdentifiers=false;** patio.createModel("my_table");*** //CHANGING IDENTIFIER INPUT METHOD*** //use whatever is passed in* patio.identifierInputMethod = null;* //convert to uppercase* patio.identifierInputMethod = "toUpperCase";* //convert to camelCase* patio.identifierInputMethod = "camelize";* //convert to underscore* patio.identifierInputMethod = "underscore";*** //CHANGING IDENTIFIER OUTPUT METHOD** //use whatever the db returns* patio.identifierOutputMethod = null;* //convert to uppercase* patio.identifierOutputMethod = "toUpperCase";* //convert to camelCase* patio.identifierOutputMethod = "camelize";* //convert to underscore* patio.identifierOutputMethod = "underscore";** //TURN QUOTING OFF* patio.quoteIdentifiers = false** @constructs* @augments patio.Time* @param options*/constructor: function () {- 1
this._super(arguments);- 1
var constants = SQL.Constants;- 1
for (var i in constants) {- 9
this[i] = constants[i];}},/*** Returns a {@link patio.Database} object that can be used to for querying.** <p>This method is the entry point for all interactions with a database including getting* {@link patio.Dataset}s for creating queries(see {@link patio.Database#from}).* </p>** <p>The {@link patio.Database} returned can also be used to create({@link patio.Database#createTable}),* alter(@link patio.Database#alterTable}), rename({@link patio.Database#renameTable}), and* drop({@link patio.Database#dropTable}) as well as many other {@link patio.Database} actions.* </p>** @example** //connect using an object* var DB = patio.createConnection({* host : "127.0.0.1",* port : 3306,* type : "mysql",* maxConnections : 1,* minConnections : 1,* user : "test",* password : "testpass",* database : 'test'* });* //connect using a connection string* var CONNECT_STRING = "mysql://test:testpass@localhost:3306/test?maxConnections=1&minConnections=1";* var DB = patio.createConnection(CONNECT_STRING);** //...do something* DB.createTable("myTable", function(){* this.name("text");* this.value("integer");* }).chain(function(){* //tables created!!!* });** @param {String|Object} options the options used to initialize the database connection.* This may be a database connetion string or object.* @param {Number} [options.maxConnections = 10] the number of connections to pool.* @param {Number} [options.minConnections = 3] the number of connections to pool.* @param {String} [options.type = "mysql"] the type of database to communicate with.* @param {String} options.user the user to authenticate as.* @param {String} options.password the password of the user.* @param {String} options.database the name of the database to use, the database* specified here is the default database for all connections.*/createConnection: function (options) {- 38
var ret = Database.connect(options);- 38
this.emit("connect", ret);- 38
return ret;},/*** @see patio#createConnection*/connect: function () {- 14
return this.createConnection.apply(this, arguments);},/*** This method allows one to connect to a database and immediately execute code.* For connection options @see patio#createConnection** @example*** var DB;* var CONNECT_STRING = "dummyDB://test:testpass@localhost/dummySchema";* var connectPromise = patio.connectAndExecute(CONNECT_STRING, function (db) {* db.dropTable("test");* db.createTable("test", function () {* this.primaryKey("id");* this.name(String);* this.age(Number);* });* });** connectPromise.chain(function (db) {* //do more stuff!* });** @param {String|Object} options @see patio#createConnection* @param {Function} cb the function to callback once connected.** @returns {comb.Promise} a promise that is resolved once the database execution has finished.*/connectAndExecute: function (options, cb) {- 23
if (!isFunction(cb)) {- 0
throw new PatioError("callback must be a function");}- 23
var db = this.createConnection.apply(this, arguments);- 23
return executeInOrder(db, patio, function (db, patio) {- 23
cb(db, patio);- 23
return db;});},/*** Disconnects all databases in use.** @param {Function} [cb=null] a callback to call when disconnect has completed* @return {comb.Promise} a promise that is resolved once all databases have disconnected.*/disconnect: function (cb) {- 39
var ret = Database.disconnect(cb), self = this;- 39
ret.classic(function (err) {- 39
if (err) {- 0
self.emit("error", err);} else {- 39
self.emit("disconnect");}});- 39
return ret.promise();},/*** This method is used to create a {@link patio.Model} object.** @example* var Flight = patio.addModel("flight", {* instance:{* toObject:function () {* var obj = this._super(arguments);* obj.weekdays = this.weekdaysArray;* obj.legs = this.legs.map(function (l) {* return l.toObject();* });* return obj;* },** _setWeekdays:function (weekdays) {* this.weekdaysArray = weekdays.split(",");* return weekdays;* }* },** static:{** init:function () {* this.oneToMany("legs", {* model:"flightLeg",* orderBy:"scheduledDepartureTime",* fetchType:this.fetchType.EAGER* });* },** byAirline:function (airline) {* return this.filter({airline:airline}).all();* },** arrivesAt:function (airportCode) {* return this.join(this.flightLeg.select("flightId").filter({arrivalCode:airportCode}).distinct(), {flightId:"id"}).all();* },** departsFrom:function (airportCode) {* return this.join(this.flightLeg.select("flightId").filter({departureCode:airportCode}).distinct(), {flightId:"id"}).all();* },** getters:{* flightLeg:function () {* if (!this.__flightLeg) {* this.__flightLeg = this.patio.getModel("flightLeg");* }* return this.__flightLeg;* }* }* }* });*** @param {String|patio.Dataset} table the table to use as the base for the model.* @param {patio.Model|patio.Model[]} Parent models of this model.* See {@link patio.plugins.ClassTableInheritancePlugin}.* @param {Object} [proto] an object to be used as the prototype for the model. See* <a href="http://c2fo.github.com/comb/symbols/comb.html#.define">comb.define</a>.* @param [Object[]] [proto.plugins] this can be used to specify additional plugins to use such as.* <ul>* <li>{@link patio.plugins.TimeStampPlugin</li>* <li>{@link patio.plugins.CachePlugin</li>* </ul>****/addModel: function (table, supers, proto) {- 86
return model.create.apply(model, arguments);},/*** Returns a model from the name of the table for which the model was created.** {@code* var TestModel = patio.addModel("test_model").sync(function(err){* if(err){* console.log(err.stack);* }else{* var TestModel = patio.getModel("test_model");* }* });* }** If you have two tables with the same name in different databases then you can use the db parameter also.** {@code** var DB1 = patio.createConnection("mysql://test:testpass@localhost:3306/test_1");* var DB2 = patio.createConnection("mysql://test:testpass@localhost:3306/test_2");* var Test1 = patio.addModel(DB1.from("test");* var Test2 = patio.addModel(DB2.from("test");** //sync the models* patio.syncModels().chain(function(){* //now you can use them* var test1Model = new Test1();* var test2Model = new Test2();* });* }*** @param {String} name the name of the table that the model represents.* @param {@patio.Database} [db] optional database in case you have two models with the same table names in* different databases.*/getModel: function (name, db) {- 79
return model.getModel(name, db);},/*** Helper method to sync all models at once.** @example** var User = patio.addModel("user");* var Blog = patio.addModel("blog");** //using promise api* patio.syncModels().chain(function(){* var user = new User();* }, function(error){* console.log(err);* });** //using a callback** patio.syncModels(function(err){* if(err){* console.log(err);* }else{* var user = new User();* }* });** @param {Function} [cb] an optional callback to be invoked when all models have been synced* @return {comb.Promise} a promise that will be resolved when the models have been synced.*/syncModels: function (cb) {- 31
return model.syncModels(cb);},resetIdentifierMethods: function () {- 49
this.quoteIdentifiers = true;- 49
this.identifierOutputMethod = null;- 49
this.identifierInputMethod = null;- 49
Model.identifierOutputMethod = null;- 49
Model.identifierInputMethod = null;},/*** Migrates the database using migration files found in the supplied directory.* <br/>* <br/>* <div>* <h3>Integer Migrations</h3>* Integer migrations are the simpler of the two migrations but are less flexible than timestamp based migrations.* In order for patio to determine which versions to use the file names must end in <versionNumber>.js where* versionNumber is a integer value representing the version number. <b>NOTE:</b>With integer migrations* missing versions are not allowed.* <br/>* <br/>* An example directory structure might look like the following:** <pre class="code">* -migrations* - createFirstTables.0.js* - shortDescription.1.js* - another.2.js* .* .* .* -lastMigration.n.js* </pre>* In order to easily identify where certain schema alterations have taken place it is a good idea to provide a brief* but meaningful migration name.* <pre class="code">* createEmployee.0.js* alterEmployeeNameColumn.1.js* </pre>*</div>** <div>* <h3>Timestamp Migrations</h3>* Timestamp migrations are the more complex of the two migrations but offer greater flexibility especially* with development teams. This is because Timestamp migrations do not require consecutive version numbers,* ,allow for duplicate version numbers(but this should be avoided), keeps track of all currently applied migrations,* and it will merge missing migrations. In order for patio to determine the order of the migration files* the file names must end in <timestamp>.js where the timestamp can be any form of a time stamp.* <pre class="code">* //yyyyMMdd* 20110131* //yyyyMMddHHmmss* 20110131123940* //unix epoch timestamp* 1328035161* </pre>* as long as it is greater than 20000101 other wise it will be assumed to be part of an integer migration.* <br/>* <br/>* An example directory structure might look like the following:** <pre class="code">* -migrations* - createFirstTables.1328035161.js* - shortDescription.1328035360.js* - another.1328035376.js* .* .* .* -lastMigration.n.js* </pre>* In order to easily identify where certain schema alterations have taken place it is a good idea to provide a brief* but meaningful migration name.* <pre class="code">* createEmployee.1328035161.js* alterEmployeeNameColumn.1328035360.js* </pre>*</div>** <b>NOTE:</b>If you start with IntegerBased migrations and decide to transition to Timestamp migrations the* patio will attempt the migrate the current schema to the timestamp based migration schema.** <div>* In order to run a migraton all one has to do is call patio.migrate(DB, directory, options);** <pre class="code">* var DB = patio.connect("my://connection/string");* patio.migrate(DB, __dirname + "/migrations").chain(function(){* console.log("migrations finished");* });* </pre>** <b>Example migration file</b>* <pre class="code">** //Up function used to migrate up a version* exports.up = function(db) {* //create a new table* db.createTable("company", function() {* this.primaryKey("id");* this.companyName(String, {size : 20, allowNull : false});* });* db.createTable("employee", function(table) {* this.primaryKey("id");* this.firstName(String);* this.lastName(String);* this.middleInitial("char", {size : 1});* });*};** //Down function used to migrate down version*exports.down = function(db) {* db.dropTable("employee", "company");*};* </pre>**</div>** @param {String|patio.Database} db the database or connection string to a 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.** @return {Promise} a promise that is resolved once the migration is complete.*/migrate: function (db) {- 31
db = isString(db) ? this.connect(db) : db;- 31
var args = argsToArray(arguments);- 31
args.splice(0, 1);- 31
return migrate.run.apply(migrate, [db].concat(args));},/*** This can be used to configure logging. If a options* hash is passed in then it will passed to the comb.logging.PropertyConfigurator.* If the options are omitted then a ConsoleAppender will be added and the level will* be set to info.** @example* var config = {* "patio" : {* level : "INFO",* appenders : [* {* type : "RollingFileAppender",* file : "/var/log/patio.log",* },* {* type : "RollingFileAppender",* file : "/var/log/patio-error.log",* name : "errorFileAppender",* level : "ERROR"* }* ]* };** patio.configureLogging(config);** @param opts*/configureLogging: function (opts) {- 0
comb.logger.configure(opts);- 0
if (!opts) {- 0
LOGGER.level = "info";}},/*** Logs an INFO level message to the "patio" logger.*/logInfo: function () {- 0
if (LOGGER.isInfo) {- 0
LOGGER.info.apply(LOGGER, arguments);}},/*** Logs a DEBUG level message to the "patio" logger.*/logDebug: function () {- 0
if (LOGGER.isDebug) {- 0
LOGGER.debug.apply(LOGGER, arguments);}},/*** Logs an ERROR level message to the "patio" logger.*/logError: function () {- 0
if (LOGGER.isError) {- 0
LOGGER.error.apply(LOGGER, arguments);}},/*** Logs a WARN level message to the "patio" logger.*/logWarn: function () {- 0
if (LOGGER.isWarn) {- 0
LOGGER.warn.apply(LOGGER, arguments);}},/*** Logs a TRACE level message to the "patio" logger.*/logTrace: function () {- 0
if (LOGGER.isTrace) {- 0
LOGGER.trace.apply(LOGGER, arguments);}},/*** Logs a FATAL level message to the "patio" logger.*/logFatal: function () {- 0
if (LOGGER.isFatal) {- 0
LOGGER.fatal.apply(LOGGER, arguments);}},/**@ignore*/getters: {/**@lends patio.prototype*//*** An array of databases that are currently connected.* @field* @type patio.Database[]* @default []*/DATABASES: function () {- 645
return Database.DATABASES;},/*** Returns the default database. This is the first database created using {@link patio#connect}.* @field* @type patio.Database* @default null*/defaultDatabase: function () {- 344
return this.DATABASES.length ? this.DATABASES[0] : null;},/**@ignore*/Database: function () {- 9
return Database;},/**@ignore*/Dataset: function () {- 57
return Dataset;},/**@ignore*/SQL: function () {- 26
return SQL;},/**@ignore*/sql: function () {- 864
return SQL;},/**@ignore*/plugins: function () {- 7
return plugins;},/**@ignore*/migrations: function () {- 0
return migrate;},/*** Returns the root comb logger using this logger you* can set the levels add appenders etc.** @type Logger* @field* @default comb.logger("patio")*/LOGGER: function () {- 0
return LOGGER;},/*** Returns the default method used to transform identifiers sent to the database.* See (@link patio.Database.identifierInputMethod}* @ignore* @field* @type String* @default Database.identifierInputMethod*/identifierInputMethod: function () {- 3
return Database.identifierInputMethod;},/*** Returns the default method used to transform identifiers returned from the database.* See (@link patio.Database.identifierOutputMethod}* @ignore* @field* @type String**/identifierOutputMethod: function () {- 3
return Database.identifierOutputMethod;},/*** @ignore* @type Boolean* Returns whether or not identifiers are quoted before being sent to the database.*/quoteIdentifiers: function (value) {- 1
return Database.quoteIdentifiers;},/**@ignore*/camelize: function () {- 1
return this.__camelize;},/**@ignore*/underscore: function () {- 1
return this.__underscore;}},/**@ignore*/setters: {/**@lends patio.prototype*//*** Set the method to call on identifiers going into the database. This affects* how identifiers are sent to the database. So if you use camelCased and the db identifiers are all underscored* use camelize. The method can include* <ul>* <li>toUpperCase</li>* <li>toLowerCase</li>* <li>camelize</li>* <li>underscore</li>* <li>Other String instance method names.</li>* </ul>** patio uses toUpperCase identifiers in all SQL strings for most databases.** @field* @type String* @ignoreCode* @example* //use whatever is passed in* patio.identifierInputMethod = null;* //convert to uppercase* patio.identifierInputMethod = "toUpperCase";* //convert to camelCase* patio.identifierInputMethod = "camelize";* //convert to underscore* patio.identifierInputMethod = "underscore";** */identifierInputMethod: function (value) {- 77
Database.identifierInputMethod = value;},/*** Set the method to call on identifiers coming out of the database. This affects* the how identifiers are represented by calling the method on them.* The method can include* <ul>* <li>toUpperCase</li>* <li>toLowerCase</li>* <li>camelize</li>* <li>underscore</li>* <li>Other String instance method names.</li>* </ul>* most database implementations in patio use toLowerCase* @ignoreCode* @field* @type String* @example* //use whatever the db returns* patio.identifierOutputMethod = null;* //convert to uppercase* patio.identifierOutputMethod = "toUpperCase";* //convert to camelCase* patio.identifierOutputMethod = "camelize";* //convert to underscore* patio.identifierOutputMethod = "underscore";** */identifierOutputMethod: function (value) {- 77
Database.identifierOutputMethod = value;},/*** Set whether to quote identifiers for all databases by default. By default,* patio quotes identifiers in all SQL strings.** @ignoreCode* @field* @type Boolean** @example* //Turn quoting off* patio.quoteIdentifiers = false* */quoteIdentifiers: function (value) {- 80
Database.quoteIdentifiers = value;},/*** Sets the whether or not to camelize identifiers coming from the database and to underscore* identifiers when sending identifiers to the database. Setting this property to true has the same effect* as:* <pre class="code">* patio.identifierOutputMethod = "camelize";* patio.identifierInputMethod = "underscore";* </pre>* @field* @ignoreCode* @example* patio.camelize = true;* patio.connectAndExecute("mysql://test:testpass@localhost:3306/airports", function (db) {* db.createTable("airport", function () {* this.primaryKey("id");* this.airportCode(String, {size:4, allowNull:false, unique:true});* this.name(String, {allowNull:false});* this.city(String, {allowNull:false});* this.state(String, {size:2, allowNull:false});* });* //=> CREATE TABLE `airport`(* // id integer PRIMARY KEY AUTO_INCREMENT,* // airport_code varchar(4) UNIQUE NOT NULL,* // name varchar(255) NOT NULL,* // city varchar(255) NOT NULL,* // state varchar(2) NOT NULL* //);* }):** @param {Boolean} camelize set to true to camelize all identifiers coming from the database and to* underscore all identifiers sent to the database.*/camelize: function (camelize) {- 21
camelize = camelize === true;- 21
Model.camelize = camelize;- 21
this.identifierOutputMethod = camelize ? "camelize" : "underscore";- 21
this.identifierInputMethod = camelize ? "underscore" : "camelize";- 21
this.__underscore = !camelize;- 21
this.__camelize = camelize;},/*** Sets the whether or not to underscore identifiers coming from the database and to camelize* identifiers when sending identifiers to the database. Setting this property to true has the same effect* as:* <pre class="code">* patio.identifierOutputMethod = "underscore";* patio.identifierInputMethod = "camelize";* </pre>*** @field* @ignoreCode* @example* patio.camelize = true;* patio.connectAndExecute("mysql://test:testpass@localhost:3306/airports", function (db) {* db.createTable("airport", function () {* this.primaryKey("id");* this.airport_code(String, {size:4, allowNull:false, unique:true});* this.name(String, {allowNull:false});* this.city(String, {allowNull:false});* this.state(String, {size:2, allowNull:false});* });* //=> CREATE TABLE `airport`(* // id integer PRIMARY KEY AUTO_INCREMENT,* // airportCode varchar(4) UNIQUE NOT NULL,* // name varchar(255) NOT NULL,* // city varchar(255) NOT NULL,* // state varchar(2) NOT NULL* //);* }):** @param {Boolean} camelize set to true to underscore all identifiers coming from the database and to* camelize all identifiers sent to the database.*/underscore: function (underscore) {- 1
underscore = underscore === true;- 1
Model.underscore = underscore;- 1
this.identifierOutputMethod = underscore ? "underscore" : "camelize";- 1
this.identifierInputMethod = underscore ? "camelize" : "underscore";- 1
this.__camelize = !underscore;- 1
this.__underscore = underscore;}}}});- 1
module.exports = patio = new Patio();- 1
patio.__Patio = Patio;- 1
var adapters = Database.ADAPTERS;- 1
for (var i in adapters) {- 4
patio[i] = adapters[i];}
|
associations/oneToMany.js
|
Coverage77.84
SLOC366
LOC167
Missed37
|
- 1
var comb = require("comb-proxy"),isArray = comb.isArray,isUndefinedOrNull = comb.isUndefinedOrNull,isBoolean = comb.isBoolean,define = comb.define,Promise = comb.Promise,isNull = comb.isNull,when = comb.when,isInstanceOf = comb.isInstanceOf,PromiseList = comb.PromiseList,isUndefined = comb.isUndefined,singularize = comb.singularize,_Association = require("./_Association");/*** @class Class to define a one to many association.** </br>* <b>NOT to be instantiated directly</b>* Its just documented for reference.** Adds the following methods to each model.* <ul>* <li>add{ModelName} - add an association</li>* <li>add{comb.pluralize(ModelName)} - add multiple associations</li>* <li>remove{ModelName} - remove an association</li>* <li>remove{comb.pluralize(ModelName)} - remove multiple association</li>* <li>removeAll - removes all associations of this type</li>* </ul>** @name OneToMany* @augments patio.associations.Association* @memberOf patio.associations** */- 1
module.exports = define(_Association, {instance: {/**@lends patio.associations.OneToMany.prototype*/type: "oneToMany",createSetter: true,_postSave: function (next, model) {- 257
var loaded = this.associationLoaded(model), vals;- 257
if (loaded && (vals = this.getAssociation(model))) {- 54
if (isArray(vals) && vals.length) {- 54
this._clearAssociations(model);- 54
var pl = this.addAssociations(vals, model);- 54
if (this.isEager()) {- 17
var self = this;- 17
pl.chain(function () {- 17
return self.fetch(model);}).classic(next);} else {- 37
pl.classic(next);}} else {- 0
next();}- 203
} else if (this.isEager() && !loaded) {- 63
this.fetch(model).classic(next);} else {- 140
next();}},_postUpdate: function (next, model) {- 2
var removeAssociationFlagName = this.removeAssociationFlagName;- 2
if (model[removeAssociationFlagName]) {- 0
var oldVals = this._getCachedOldVals(model);- 0
this._clearCachedOldVals(model);- 0
var pl = oldVals.length ? this.removeItems(oldVals, model, false) : null, self = this;- 0
when(pl).chain(function () {- 0
return self.addAssociations(self.getAssociation(model), model);}).classic(next);- 0
model[removeAssociationFlagName] = false;} else {- 2
next();}},_postLoad: function (next, model) {- 690
if (this.isEager() && !this.associationLoaded(model)) {- 155
this.fetch(model).classic(next);} else {- 535
next();}},/*** Middleware called before a model is removed.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being acted up.*/_preRemove: function (next, model) {- 89
this.removeAllItems(model).classic(next);},_getCachedOldVals: function (model) {- 0
return model[this.oldAssocationCacheName] || [];},_clearCachedOldVals: function (model) {- 0
model[this.oldAssocationCacheName] = [];},_cacheOldVals: function (model) {- 0
var oldVals = model[this.oldAssocationCacheName] || [];- 0
oldVals = oldVals.concat(this.getAssociation(model));- 0
model[this.oldAssocationCacheName] = oldVals;},_setter: function (vals, model) {- 54
if (!isUndefined(vals)) {- 54
if (model.isNew) {- 54
if (!isNull(vals)) {- 54
this.addAssociations(vals, model);//this.__setValue(model, vals);} else {- 0
this.__setValue(model, []);}} else {- 0
model.__isChanged = true;- 0
model[this.removeAssociationFlagName] = true;- 0
this._cacheOldVals(model);- 0
if (!isNull(vals)) {//ensure its an array!- 0
vals = (isArray(vals) ? vals : [vals]).map(function (m) {- 0
return this._toModel(m);}, this);} else {- 0
vals = [];}- 0
this.__setValue(model, vals);}}},addAssociation: function (item, model, reload) {- 262
reload = isBoolean(reload) ? reload : false;- 262
var ret;- 262
if (!isUndefinedOrNull(item)) {- 262
if (!model.isNew) {- 150
item = this._toModel(item);- 150
var loaded = this.associationLoaded(model), self = this;- 150
this._setAssociationKeys(model, item);- 150
var recip = this.model._findAssociation(this);- 150
if (recip) {- 149
recip[1].__setValue(item, model);}- 150
ret = model._checkTransaction(function () {- 150
return item.save().chain(function () {- 150
if (loaded && reload) {- 3
return self.parent._reloadAssociationsForType(self.type, self.model, model);}}).chain(function () {- 150
return model;});});} else {- 112
ret = new Promise().callback(model);- 112
item = this._toModel(item);- 112
var items = this.getAssociation(model);- 112
if (isUndefinedOrNull(items)) {- 39
this.__setValue(model, [item]);} else {- 73
items.push(item);}}} else {- 0
new Promise().callback(model);}- 262
return ret.promise();},addAssociations: function (items, model) {- 124
var ret, self = this;- 124
items = (isArray(items) ? items : [items]);- 124
if (model.isNew) {- 54
items.map(function (item) {- 156
return self.addAssociation(item, model, false);}, this);- 54
ret = when(model);} else {- 70
ret = model._checkTransaction(function () {- 70
return new PromiseList(items.map(function (item) {- 204
return self.addAssociation(item, model, false);}));}).chain(function () {- 70
if (!model.isNew && self.associationLoaded(model)) {- 5
return self.parent._reloadAssociationsForType(self.type, self.model, model);}}).chain(function () {- 70
return model;});}- 124
return ret.promise();},removeItem: function (item, model, remove, reload) {- 54
reload = isBoolean(reload) ? reload : false;- 54
remove = isBoolean(remove) ? remove : false;- 54
var ret;- 54
if (!isUndefinedOrNull(item)) {- 54
if (!model.isNew) {- 54
if (isInstanceOf(item, this.model) && !item.isNew) {- 54
if (!remove) {- 26
this._setAssociationKeys(model, item, null);}- 54
var loaded = this.associationLoaded(model), self = this;- 54
ret = model._checkTransaction(function () {- 54
return item[remove ? "remove" : "save"]().chain(function () {- 54
if (loaded && reload) {- 18
return self.parent._reloadAssociationsForType(self.type, self.model, model);}});}).chain(function () {- 54
return model;});} else {- 0
ret = when(model);}} else {- 0
item = this._toModel(item);- 0
var items = this.getAssociation(model), index;- 0
if (!isUndefinedOrNull(items) && (index = items.indexOf(item)) !== -1) {- 0
items.splice(index, 1);}- 0
ret = when(model);}} else {- 0
ret = when(model);}- 54
return ret.promise();},removeItems: function (items, model, remove) {//todo make this more efficient!!!!- 26
var ret, self = this;- 26
items = isArray(items) ? items : [items];- 26
if (model.isNew) {- 0
items.map(function (item) {- 0
return self.removeItem(item, model, remove, false);});- 0
ret = when(model);} else {- 26
ret = model._checkTransaction(function () {- 26
return new PromiseList(items.map(function (item) {- 50
return self.removeItem(item, model, remove, false);}));}).chain(function () {- 26
if (self.associationLoaded(model)) {- 26
return self.parent._reloadAssociationsForType(self.type, self.model, model);}}).chain(function () {- 26
return model;});}- 26
return ret.promise();},removeAllItems: function (model, remove) {- 93
remove = isBoolean(remove) ? remove : false;- 93
var ret;- 93
if (!model.isNew) {- 93
var q = {}, removeQ = {};- 93
this._setAssociationKeys(model, q);- 93
this._setAssociationKeys(model, removeQ, null);- 93
var loaded = this.associationLoaded(model), self = this;- 93
return model._checkTransaction(function () {- 93
return self._filter(model).forEach(function (m) {- 99
return remove ? m.remove() : m.update(removeQ);}).chain(function () {- 93
if (loaded) {- 35
return self.parent._reloadAssociationsForType(self.type, self.model, model);}}).chain(function () {- 93
return model;});});} else {//todo we may want to check if any of the items were previously saved items;- 0
this._clearAssociations(model);- 0
ret = when(model);}- 0
return ret.promise();},inject: function (parent, name) {- 25
this._super(arguments);- 25
var singular = singularize(name);- 25
if (this._model === name) {- 15
this._model = singular;}- 25
singular = singular.charAt(0).toUpperCase() + singular.slice(1);- 25
if (!this.readOnly) {- 25
this.removedKey = "__removed" + name + "";- 25
this.addedKey = "__added_" + name + "";- 25
parent.prototype[this.removedKey] = [];- 25
parent.prototype[this.addedKey] = [];- 25
var self = this;- 25
name = name.charAt(0).toUpperCase() + name.slice(1);- 25
var addName = "add" + singular;- 25
var addNames = "add" + name;- 25
var removeName = "remove" + singular;- 25
var removeNames = "remove" + name;- 25
var removeAllName = "removeAll" + name;- 25
parent.prototype[addName] = function (item) {- 14
return isArray(item) ? self.addAssociations(item, this) : self.addAssociation(item, this, true);};- 25
parent.prototype[addNames] = function (items) {- 18
return isArray(items) ? self.addAssociations(items, this) : self.addAssociation(items, this);};- 25
parent.prototype[removeName] = function (item, remove) {- 28
return isArray(item) ? self.removeItems(item, this, remove) : self.removeItem(item, this, remove, true);};- 25
parent.prototype[removeNames] = function (item, remove) {- 26
return isArray(item) ? self.removeItems(item, this, remove) : self.removeItem(item, this, remove);};- 25
parent.prototype[removeAllName] = function (remove) {- 4
return self.removeAllItems(this, remove);};}},getters: {oldAssocationCacheName: function () {- 0
return "_" + this.name + "OldValues";},//Returns our modelmodel: function () {- 5625
var model;- 5625
try {- 5625
model = this["__model__"] || (this["__model__"] = this.patio.getModel(this._model, this.parent.db));} catch (e) {- 0
model = this["__model__"] = this.patio.getModel(this.name, this.parent.db);}- 5625
return model;}}}});
|
database/schemaGenerators.js
|
Coverage78.13
SLOC650
LOC96
Missed21
|
- 1
var comb = require("comb-proxy"),argsToArray = comb.argsToArray,merge = comb.merge,isFunction = comb.isFunction,isDefined = comb.isDefined,isHash = comb.isHash,isString = comb.isString,isArray = comb.isArray,toArray = comb.array.toArray,methodMissing = comb.methodMissing,define = comb.define;- 1
var Generator = define(null, {instance:{/**@lends patio.SchemaGenerator.prototype*/__primaryKey:null,/*** An internal class that the user is not expected to instantiate directly.* Instances are created by {@link patio.Database#createTable}.* It is used to specify table creation parameters. It takes a Database* object and a block of column/index/constraint specifications, and* gives the Database a table description, which the database uses to* create a table.** {@link patio.SchemaGenerator} has some methods but also includes method_missing,* allowing users to specify column type as a method instead of using* the column method, which makes for a cleaner code.* @constructs* @example*comb.executeInOrder(DB, function(DB){* DB.createTable("airplane_type", function () {* this.primaryKey("id");* this.name(String, {allowNull:false});* this.max_seats(Number, {size:3, allowNull:false});* this.company(String, {allowNull:false});* });* DB.createTable("airplane", function () {* this.primaryKey("id");* this.total_no_of_seats(Number, {size:3, allowNull:false});* this.foreignKey("typeId", "airplane_type", {key:"id"});* });* DB.createTable("flight_leg", function () {* this.primaryKey("id");* this.scheduled_departure_time("time");* this.scheduled_arrival_time("time");* this.foreignKey("departure_code", "airport", {key:"airport_code", type : String, size : 4});* this.foreignKey("arrival_code", "airport", {key:"airport_code", type : String, size : 4});* this.foreignKey("flight_id", "flight", {key:"id"});* });* DB.createTable("leg_instance", function () {* this.primaryKey("id");* this.date("date");* this.arr_time("datetime");* this.dep_time("datetime");* this.foreignKey("airplane_id", "airplane", {key:"id"});* this.foreignKey("flight_leg_id", "flight_leg", {key:"id"});* });*});* @param {patio.Database} the database this generator is for*/constructor:function (db) {- 213
this.db = db;- 213
this.columns = [];- 213
this.indexes = [];- 213
this.constraints = [];- 213
this.__primaryKey = null;},/*** Add an unnamed constraint to the DDL, specified by the given block* or args:** @example* db.createTable("test", function(){* this.check({num : {between : [1,5]}})* //=> CHECK num >= 1 AND num <= 5* this.check(function(){return this.num.gt(5);});* //=> CHECK num > 5* });**/check:function () {- 1
return this.constraint.apply(this, [null].concat(argsToArray(arguments)));},/*** Add a column with the given name, type, and opts to the DDL.** <pre class="code">* DB.createTable("test", function(){* this.column("num", "integer");* //=> num INTEGER* this.column("name", String, {allowNull : false, "default" : "a");* //=> name varchar(255) NOT NULL DEFAULT 'a'* this.column("ip", "inet");* //=> ip inet* });* </pre>** You can also create columns via method missing, so the following are* equivalent:* <pre class="code">* DB.createTable("test", function(){* this.column("number", "integer");* this.number("integer");* });* </pre>** @param {String|patio.sql.Identifier} name the name of the column* @param type the datatype of the column.* @param {Object} [opts] additional options** @param [opts.default] The default value for the column.* @param [opts.deferrable] This ensure Referential Integrity will work even if* reference table will use for its foreign key a value that does not* exists(yet) on referenced table. Basically it adds* DEFERRABLE INITIALLY DEFERRED on key creation.* @param {Boolean} [opts.index] Create an index on this column.* @param {String|patio.sql.Identifier} [key] For foreign key columns, the column in the associated table* that this column references. Unnecessary if this column references the primary key of the* associated table.* @param {Boolean} [opts.allowNull] Mark the column as allowing NULL values (if true),* or not allowing NULL values (if false). If unspecified, will default* to whatever the database default is.* @param {String} [opts.onDelete] Specify the behavior of this column when being deleted* ("restrict", "cascade", "setNull", "setDefault", "noAction").* @param {String} [opts.onUpdate] Specify the behavior of this column when being updated* Valid options ("restrict", "cascade", "setNull", "setDefault", "noAction").* @param {Boolean} [opts.primaryKey] Make the column as a single primary key column. This should only* be used if you have a single, non-autoincrementing primary key column.* @param {Number} [opts.size] The size of the column, generally used with string* columns to specify the maximum number of characters the column will hold.* An array of two integers can be provided to set the size and the* precision, respectively, of decimal columns.* @param {String} [opts.unique] Mark the column as unique, generally has the same effect as* creating a unique index on the column.* @param {String} [opts.unsigned] Make the column type unsigned, only useful for integer* columns.* @param {Array} [opts.elements] Available items used for set and enum columns.***/column:function (name, type, opts) {- 524
opts = opts || {};- 524
this.columns.push(merge({name:name, type:type}, opts));- 524
if (opts.index) {- 1
this.index(name);}},/*** Adds a named constraint (or unnamed if name is nil) to the DDL,* with the given block or args.* @example* DB.createTable("test", function(){* this.constraint("blah", {num : {between : [1,5]}})* //=> CONSTRAINT blah CHECK num >= 1 AND num <= 5* this.check("foo", function(){* return this.num.gt(5);* }); # CONSTRAINT foo CHECK num > 5* });* @param {String|patio.sql.Identifier} name the name of the constraint* @param {...} args variable number of arguments to create the constraint filter.* See {@link patio.Dataset#filter} for valid filter arguments.* */constraint:function (name, args) {- 2
args = argsToArray(arguments).slice(1);- 2
var block = isFunction(args[args.length - 1]) ? args.pop : null;- 2
this.constraints.push({name:name, type:"check", check:block || args});},/*** Add a foreign key in the table that references another table to the DDL. See {@link patio.SchemaGenerator#column{* for options.** @example* DB.createTable("flight_leg", function () {* this.primaryKey("id");* this.scheduled_departure_time("time");* this.scheduled_arrival_time("time");* this.foreignKey("departure_code", "airport", {key:"airport_code", type : String, size : 4});* this.foreignKey("arrival_code", "airport", {key:"airport_code", type : String, size : 4});* this.foreignKey("flight_id", "flight", {key:"id"});* });**/foreignKey:function (name, table, opts) {- 35
opts = opts || {};- 35
opts = isHash(table) ? merge({}, table, opts) : isString(table) ? merge({table:table}, opts) : opts;- 35
if (isArray(name)) {- 5
return this.__compositeForeignKey(name, opts);} else {- 30
return this.column(name, "integer", opts);}},/***Add a full text index on the given columns to the DDL.** @example* DB.createTable("posts", function () {* this.title("text");* this.body("text");* this.fullTextIndex("title");* this.fullTextIndex(["title", "body"]);* });*/fullTextIndex:function (columns, opts) {- 4
opts = opts || {};- 4
return this.index(columns, merge({type:"fullText"}, opts));},/**** Check if the DDL includes the creation of a column with the given name.* @return {Boolean} true if the DDL includes the creation of a column with the given name.*/hasColumn:function (name) {- 71
return this.columns.some(function (c) {- 300
return c.name === name;});},/*** Add an index on the given column(s) with the given options to the DDL.* The available options are:* @example* DB.createTable("test", function(table) {* table.primaryKey("id", "integer", {null : false});* table.column("name", "text");* table.index("name", {unique : true});* });** @param columns the column/n to create the index from.* @param {Object} [opts] Additional options* @param {String} [opts.type] The type of index to use (only supported by some databases)* @param {Boolean} [opts.unique] :: Make the index unique, so duplicate values are not allowed.* @param [opts.where] :: Create a partial index (only supported by some databases)**/index:function (columns, opts) {- 18
this.indexes.push(merge({columns:toArray(columns)}, opts || {}));},/*** Adds an auto-incrementing primary key column or a primary key constraint to the DDL.* To create a constraint, the first argument should be an array of columns* specifying the primary key columns. To create an auto-incrementing primary key* column, a single column can be used. In both cases, an options hash can be used* as the second argument.** If you want to create a primary key column that is not auto-incrementing, you* should not use this method. Instead, you should use the regular {@link patio.SchemaGenerator#column}* method with a {primaryKey : true} option.** @example* db.createTable("airplane_type", function () {* this.primaryKey("id");* //=> id integer NOT NULL PRIMARY KEY AUTOINCREMENT* this.name(String, {allowNull:false});* this.max_seats(Number, {size:3, allowNull:false});* this.company(String, {allowNull:false});* });* */primaryKey:function (name) {- 71
if (isArray(name)) {- 0
return this.__compositePrimaryKey.apply(this, arguments);} else {- 71
var args = argsToArray(arguments, 1), type;- 71
var opts = args.pop();- 71
this.__primaryKey = merge({}, this.db.serialPrimaryKeyOptions, {name:name}, opts);- 71
if (isDefined((type = args.pop()))) {- 4
merge(opts, {type:type});}- 71
merge(this.__primaryKey, opts);- 71
return this.__primaryKey;}},/*** Add a spatial index on the given columns to the DDL.*/spatialIndex:function (columns, opts) {- 2
opts = opts || {};- 2
return this.index(columns, merge({type:"spatial"}, opts));},/*** Add a unique constraint on the given columns to the DDL. See {@link patio.SchemaGenerator#constraint}* for argument types.* @example* DB.createTable("test", function(){* this.unique("name");* //=> UNIQUE (name)* });* */unique:function (columns, opts) {- 0
opts = opts || {};- 0
this.constraints.push(merge({type:"unique", columns:toArray(columns)}, opts));},/*** @private* Add a composite primary key constraint*/__compositePrimaryKey:function (columns) {- 0
var args = argsToArray(arguments, 1);- 0
var opts = args.pop() || {};- 0
this.constraints.push(merge({type:"primaryKey", columns:columns}, opts));},/*** @private* Add a composite foreign key constraint*/__compositeForeignKey:function (columns, opts) {- 5
this.constraints.push(merge({type:"foreignKey", columns:columns}, opts));},/**@ignore*/getters:{// The name of the primary key for this generator, if it has a primary key.primaryKeyName:function () {- 71
return this.__primaryKey ? this.__primaryKey.name : null;}}}});- 1
exports.SchemaGenerator = function (db, block) {- 213
var gen = new Generator(db);- 213
var prox = methodMissing(gen, function (name) {- 407
return function (type, opts) {- 407
name = name || null;- 407
opts = opts || {};- 407
if (name) {- 407
return this.column(name, type, opts);} else {- 0
throw new TypeError("name required got " + name);}};}, Generator);- 213
block.apply(prox, [prox]);- 213
gen.columns = prox.columns;- 213
if (gen.__primaryKey && !gen.hasColumn(gen.primaryKeyName)) {- 71
gen.columns.unshift(gen.__primaryKey);}- 213
return gen;};- 1
var AlterTableGenerator = define(null, {instance:{/**@lends patio.AlterTableGenerator.prototype*//*** An internal class that the user is not expected to instantiate directly.* Instances are created by {@link patio.Database#alterTable}.* It is used to specify table alteration parameters. It takes a Database* object and a function which is called in the scope of the {@link patio.AlterTableGenerator}* to perform on the table, and gives the Database an array of table altering operations,* which the database uses to alter a table's description.** @example* DB.alterTable("xyz", function() {* this.addColumn("aaa", "text", {null : false, unique : true});* this.dropColumn("bbb");* this.renameColumn("ccc", "ddd");* this.setColumnType("eee", "integer");* this.setColumnDefault("hhh", 'abcd');* this.addIndex("fff", {unique : true});* this.dropIndex("ggg");* });** //or using the passed in generator* DB.alterTable("xyz", function(table) {* table.addColumn("aaa", "text", {null : false, unique : true});* table.dropColumn("bbb");* table.renameColumn("ccc", "ddd");* table.setColumnType("eee", "integer");* table.setColumnDefault("hhh", 'abcd');* table.addIndex("fff", {unique : true});* table.dropIndex("ggg");* });* @constructs** @param {patio.Database} db the database object which is performing the alter table operation.* @param {Function} block a block which performs the operations. The block is called in the scope* of the {@link patio.AlterTableGenerator} and is passed an instance of {@link patio.AlterTableGenerator}* as the first argument.*/constructor:function (db, block) {- 84
this.db = db;- 84
this.operations = [];- 84
block.apply(this, [this]);},/*** Add a column with the given name, type, and opts to the DDL for the table.* See {@link patio.SchemaGenerator#column} for the available options.** @example* DB.alterTable("test", function(){* this.addColumn("name", String);* //=> ADD COLUMN name varchar(255)* });**/addColumn:function (name, type, opts) {- 13
opts = opts || {};- 13
this.operations.push(merge({op:"addColumn", name:name, type:type}, opts));},/*** Add a constraint with the given name and args to the DDL for the table.* See {@link patio.SchemaGenerator#constraint}.** @example* var sql = patio.sql;* DB.alterTable("test", function(){* this.addConstraint("valid_name", sql.name.like('A%'));* //=>ADD CONSTRAINT valid_name CHECK (name LIKE 'A%')* });* */addConstraint:function (name) {- 2
var args = argsToArray(arguments).slice(1);- 2
var block = isFunction(args[args.length - 1]) ? args[args.length - 1]() : null;- 2
this.operations.push({op:"addConstraint", name:name, type:"check", check:block || args});},/*** Add a unique constraint to the given column(s).* See {@link patio.SchemaGenerator#constraint}.* @example* DB.alterTable("test", function(){* this.addUniqueConstraint("name");* //=> ADD UNIQUE (name)* this.addUniqueConstraint("name", {name : "uniqueName});* //=> ADD CONSTRAINT uniqueName UNIQUE (name)* });**/addUniqueConstraint:function (columns, opts) {- 0
opts = opts || {};- 0
this.operations.push(merge({op:"addConstraint", type:"unique", columns:toArray(columns)}, opts));},/*** Add a foreign key with the given name and referencing the given table* to the DDL for the table. See {@link patio.SchemaGenerator#column}* for the available options.** You can also pass an array of column names for creating composite foreign* keys. In this case, it will assume the columns exists and will only add* the constraint.** NOTE: If you need to add a foreign key constraint to a single existing column* use the composite key syntax even if it is only one column.* @example* DB.alterTable("albums", function(){* this.addForeignKey("artist_id", "table");* //=>ADD COLUMN artist_id integer REFERENCES table* this.addForeignKey(["name"], "table")* //=>ADD FOREIGN KEY (name) REFERENCES table* });*/addForeignKey:function (name, table, opts) {- 3
opts = opts;- 3
if (isArray(name)) {- 1
return this.__addCompositeForeignKey(name, table, opts);} else {- 2
return this.addColumn(name, "integer", merge({table:table}, opts));}},/*** Add a full text index on the given columns to the DDL for the table.* See @{link patio.SchemaGenerator#index} for available options.*/addFullTextIndex:function (columns, opts) {- 0
opts = opts || {};- 0
return this.addIndex(columns, merge({type:"fullText"}, opts));},/*** Add an index on the given columns to the DDL for the table. See* {@link patio.SchemaGenerator#index} for available options.* @example* DB.alterTable("table", function(){* this.addIndex("artist_id");* //=> CREATE INDEX table_artist_id_index ON table (artist_id)* });*/addIndex:function (columns, opts) {- 5
opts = opts || {};- 5
this.operations.push(merge({op:"addIndex", columns:toArray(columns)}, opts));},/*** Add a primary key to the DDL for the table. See {@link patio.SchemaGenerator#column}* for the available options. Like {@link patio.ALterTableGenerator#addForeignKey}, if you specify* the column name as an array, it just creates a constraint:** @example* DB.alterTable("albums", function(){* this.addPrimaryKey("id");* //=> ADD COLUMN id serial PRIMARY KEY* this.addPrimaryKey(["artist_id", "name"])* //=>ADD PRIMARY KEY (artist_id, name)* });*/addPrimaryKey:function (name, opts) {- 0
opts = opts || {};- 0
if (isArray(name)) {- 0
return this.__addCompositePrimaryKey(name, opts);} else {- 0
opts = merge({}, this.db.serialPrimaryKeyOptions, opts);- 0
delete opts.type;- 0
return this.addColumn(name, "integer", opts);}},/*** Add a spatial index on the given columns to the DDL for the table.* See {@link patio.SchemaGenerator#index} for available options.* */addSpatialIndex:function (columns, opts) {- 0
opts = opts || {};- 0
this.addIndex(columns, merge({}, {type:"spatial"}, opts));},/*** Remove a column from the DDL for the table.** @example* DB.alterTable("albums", function(){* this.dropColumn("artist_id");* //=>DROP COLUMN artist_id* });** @param {String|patio.sql.Identifier} name the name of the column to drop.*/dropColumn:function (name) {- 4
this.operations.push({op:"dropColumn", name:name});},/*** Remove a constraint from the DDL for the table.* @example* DB.alterTable("test", function(){* this.dropConstraint("constraint_name");* //=>DROP CONSTRAINT constraint_name* });* @param {String|patio.sql.Identifier} name the name of the constraint to drop.*/dropConstraint:function (name) {- 0
this.operations.push({op:"dropConstraint", name:name});},/*** Remove an index from the DDL for the table.** @example* DB.alterTable("albums", function(){* this.dropIndex("artist_id")* //=>DROP INDEX table_artist_id_index* this.dropIndex(["a", "b"])* //=>DROP INDEX table_a_b_index* this.dropIndex(["a", "b"], {name : "foo"})* //=>DROP INDEX foo* });*/dropIndex:function (columns, opts) {- 2
opts = opts || {};- 2
this.operations.push(merge({op:"dropIndex", columns:toArray(columns)}, opts));},/*** Modify a column's name in the DDL for the table.** @example* DB.alterTable("artist", function(){* this.renameColumn("name", "artistName");* //=> RENAME COLUMN name TO artist_name* });*/renameColumn:function (name, newName, opts) {- 55
opts = opts || {};- 55
this.operations.push(merge({op:"renameColumn", name:name, newName:newName}, opts));},/*** Modify a column's default value in the DDL for the table.* @example* DB.alterTable("artist", function(){* //=>this.setColumnDefault("artist_name", "a");* //=> ALTER COLUMN artist_name SET DEFAULT 'a'* });* */setColumnDefault:function (name, def) {- 5
this.operations.push({op:"setColumnDefault", name:name, "default":def});},/*** Modify a column's type in the DDL for the table.** @example* DB.alterTable("artist", function(){* this.setColumnType("artist_name", 'char(10)');* //=> ALTER COLUMN artist_name TYPE char(10)* });*/setColumnType:function (name, type, opts) {- 7
opts = opts || {};- 7
this.operations.push(merge({op:"setColumnType", name:name, type:type}, opts));},/*** Modify a column's NOT NULL constraint.* @example* DB.alterTable("artist", function(){* this.setColumnAllowNull("artist_name", false);* //=> ALTER COLUMN artist_name SET NOT NULL* });**/setAllowNull:function (name, allowNull) {- 1
this.operations.push({op:"setColumnNull", name:name, "null":allowNull});},/*** @private* Add a composite primary key constraint**/__addCompositePrimaryKey:function (columns, opts) {- 0
this.operations.push(merge({op:"addConstraint", type:"primaryKey", columns:columns}, opts));},/*** @private* Add a composite foreign key constraint* */__addCompositeForeignKey:function (columns, table, opts) {- 1
this.operations.push(merge({op:"addConstraint", type:"foreignKey", columns:columns, table:table}, opts));}}}).as(exports, "AlterTableGenerator");
|
database/index.js
|
Coverage79.49
SLOC311
LOC117
Missed24
|
- 1
var comb = require("comb"),format = comb.string.format,merge = comb.merge,hitch = comb.hitch,isNull = comb.isNull,define = comb.define,isBoolean = comb.isBoolean,isUndefined = comb.isUndefined,isFunction = comb.isFunction,isString = comb.isString,isObject = comb.isObject,isDate = comb.isDate,isArray = Array.isArray,isHash = comb.isHash,isNumber = comb.isNumber,isInstanceOf = comb.isInstanceOf,isEmpty = comb.isEmpty,sql = require("../sql").sql,DateTime = sql.DateTime,TimeStamp = sql.TimeStamp,Json = sql.Json,Year = sql.Year,Time = sql.Time,json = sql.json,ConnectionPool = require("../ConnectionPool"),DatabaseError = require("../errors").DatabaseError,ConnectDB = require("./connect"),DatasetDB = require("./dataset"),DefaultsDB = require("./defaults"),LoggingDB = require("./logging"),QueryDB = require("./query"),SchemaDB = require("./schema"), patio;- 1
var DATABASES = [];- 1
var Database = define([ConnectDB, DatasetDB, DefaultsDB, LoggingDB, QueryDB, SchemaDB], {instance: {/**@lends patio.Database.prototype*//*** A Database object represents a virtual connection to a database.* The Database class is meant to be subclassed by database adapters in order* to provide the functionality needed for executing queries.** @constructs* @param {Object} opts options used to create the database** @property {String} uri A database URI used to create the database connection. This property is* available even if an object was used to create the database connection.* @property {patio.Dataset} dataset returns an empty adapter specific {@link patio.Dataset} that can* be used to query the {@link patio.Database} with.*/constructor: function (opts) {- 120
opts = opts || {};- 120
if (!patio) {- 1
patio = require("../index");}- 120
this.patio = patio;- 120
this._super(arguments, [opts]);- 120
opts = merge(this.connectionPoolDefaultOptions, opts);- 120
this.schemas = {};- 120
this.type = opts.type;- 120
this.defaultSchema = opts.defaultSchema || this.defaultSchemaDefault;- 120
this.preparedStatements = {};- 120
this.opts = opts;- 120
this.pool = ConnectionPool.getPool(opts, hitch(this, this.createConnection), hitch(this, this.closeConnection), hitch(this, this.validate));},/*** Casts the given type to a SQL type.** @example* DB.castTypeLiteral(Number) //=> numeric* DB.castTypeLiteral("foo") //=> foo* DB.castTypeLiteral(String) //=> varchar(255)* DB.castTypeLiteral(Boolean) //=> boolean**@param type the javascript type to cast to a SQL type.** @return {String} the SQL data type.**/castTypeLiteral: function (type) {- 2
return this.typeLiteral({type: type});},/*** This function acts as a proxy to {@link patio.Dataset#literal}.** See {@link patio.Dataset#literal}.**/literal: function (v) {- 156
return this.dataset.literal(v);},/*** Typecast the value to the given columnType. Calls* typecastValue{ColumnType} if the method exists,* otherwise returns the value.** @example* DB.typeCastValue("boolean", 0) //=> false* DB.typeCastValue("boolean", 1) //=> true* DB.typeCastValue("timestamp", '2004-02-01 12:12:12')* //=> new patio.sql.TimeStamp(2004, 1, 1, 12, 12, 12);** @throws {patio.DatabaseError} if there is an error converting the value to the column type.** @param {String} columnType the SQL datatype of the column* @param value the value to typecast.** @return the typecasted value.* */typecastValue: function (columnType, value) {- 32056
if (isNull(value) || isUndefined(value)) {- 6496
return null;}- 25560
var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();- 25560
try {- 25560
if (isFunction(this[meth])) {- 25560
return this[meth](value);} else {- 0
return value;}} catch (e) {- 11
throw e;}},// Typecast the value to true, false, or null__typecastValueJson: function (value) {- 1356
var ret = value;- 1356
if (!isInstanceOf(value, Json)) {- 931
ret = json(value);}- 1354
return ret;},// Typecast the value to true, false, or null__typecastValueBoolean: function (value) {- 9
if (isBoolean(value)) {- 7
return value;- 2
} else if (value === 0 || value === "0" || (isString(value) && value.match(/^f(alse)?$/i) !== null)) {- 1
return false;} else {- 1
return (isObject(value) && isEmpty(value)) || !value ? null : true;}},// Typecast the value to blob, false, or null__typecastValueBlob: function (value) {- 28
if (isInstanceOf(value, Buffer)) {- 12
return value;- 16
} else if (isArray(value) || isString(value)) {- 14
return new Buffer(value);} else {- 2
throw new DatabaseError("Invalid value for blob " + value);}},// Typecast the value to true, false, or null__typecastValueText: function (value) {- 357
return value.toString();},// Typecast the value to a Date__typecastValueDate: function (value) {- 15
if (isDate(value)) {- 12
return value;- 3
} else if (isString(value)) {- 2
var ret = patio.stringToDate(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for date %j", [value]));}- 1
return ret;- 1
} else if (isHash(value) && !isEmpty(value)) {- 0
return new Date(value.year, value.month, value.day);} else {- 1
throw new DatabaseError(format("Invalid value for date %j", [value]));}},// Typecast the value to a patio.sql.DateTime.__typecastValueDatetime: function (value) {- 62
var ret;- 62
if (isInstanceOf(value, DateTime)) {- 0
return value;- 62
} else if (isDate(value)) {- 60
ret = value;- 2
} else if (isHash(value) && !isEmpty(value)) {- 0
ret = new Date(value.year, value.month, value.day, value.hour, value.minute, value.second);- 2
} else if (isString(value)) {- 2
ret = patio.stringToDateTime(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for datetime %j", [value]));}- 1
ret = ret.date;} else {- 0
throw new DatabaseError(format("Invalid value for datetime %j", [value]));}- 61
return new DateTime(ret);},// Typecast the value to a patio.sql.DateTime__typecastValueTimestamp: function (value) {- 1
var ret;- 1
if (isInstanceOf(value, TimeStamp)) {- 0
return ret;- 1
} else if (isDate(value)) {- 0
ret = value;- 1
} else if (isHash(value) && !isEmpty(value)) {- 0
ret = new Date(value.year, value.month, value.day, value.hour, value.minute, value.second);- 1
} else if (isString(value)) {- 1
ret = patio.stringToTimeStamp(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for timestamp %j", [value]));}- 1
ret = ret.date;} else {- 0
throw new DatabaseError(format("Invalid value for timestamp %j", [value]));}- 1
return new TimeStamp(ret);},// Typecast the value to a patio.sql.Year__typecastValueYear: function (value) {- 1
if (isInstanceOf(value, Year)) {- 0
return value;- 1
} else if (isNumber(value)) {- 0
return new Year(value);- 1
} else if (isString(value)) {- 1
var ret = patio.stringToYear(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for date %j", [value]));}- 1
return ret;- 0
} else if (isHash(value) && !isEmpty(value)) {- 0
return new Date(value.year, value.month, value.day);} else {- 0
throw new DatabaseError(format("Invalid value for date %j", [value]));}},// Typecast the value to a patio.sql.Time__typecastValueTime: function (value) {- 2
var ret;- 2
if (isInstanceOf(value, Time)) {- 0
return value;- 2
} else if (isDate(value)) {- 0
ret = value;- 2
} else if (isString(value)) {- 2
ret = patio.stringToTime(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for time %j", [value]));}- 1
ret = ret.date;- 0
} else if (isHash(value) && !isEmpty(value)) {- 0
ret = new Date(0, 0, 0, value.hour, value.minute, value.second);} else {- 0
throw new DatabaseError(format("Invalid value for time %j", [value]));}- 1
return new Time(ret);},// Typecast the value to a Number__typecastValueDecimal: function (value) {- 36
var ret = parseFloat(value);- 36
if (isNaN(ret)) {- 1
throw new DatabaseError(format("Invalid value for decimal %j", [value]));}- 35
return ret;},// Typecast the value to a Number__typecastValueFloat: function (value) {- 320
var ret = parseFloat(value);- 320
if (isNaN(ret)) {- 1
throw new DatabaseError(format("Invalid value for float %j", [value]));}- 319
return ret;},// Typecast the value to a Number__typecastValueInteger: function (value) {- 6033
var ret = parseInt(value, 10);- 6033
if (isNaN(ret)) {- 1
throw new DatabaseError(format("Invalid value for integer %j", [value]));}- 6032
return ret;},// Typecast the value to a String__typecastValueString: function (value) {- 17340
return "" + value;}},"static": {/**@lends patio.Database*//*** A list of currently connected Databases.* @type patio.Database[]*/DATABASES: DATABASES}}).as(module);
|
associations/manyToOne.js
|
Coverage80.65
SLOC100
LOC31
Missed6
|
- 1
var comb = require("comb"),when = comb.when,Promise = comb.Promise,PromiseList = comb.PromiseList,define = comb.define,isNull = comb.isNull,isUndefinedOrNull = comb.isUndefinedOrNull,_Association = require("./_Association");/*** @class Class to define a many to one association.** </br>* <b>NOT to be instantiated directly</b>* Its just documented for reference.** @name ManyToOne* @augments patio.associations.Association* @memberOf patio.associations** */- 1
module.exports = exports = define(_Association, {instance: {/**@lends patio.associations.ManyToOne.prototype*/_fetchMethod: "one",type: "manyToOne",isOwner: false,__checkAndSetAssociation: function (next, model) {- 326
var assoc;- 326
if (this.associationLoaded(model) && !isUndefinedOrNull((assoc = this.getAssociation(model)))) {- 278
if (assoc.isNew) {- 8
var self = this;- 8
assoc.save().both(function () {- 8
var recip = self.model._findAssociation(self);- 8
if (recip) {//set up our association- 8
recip[1]._setAssociationKeys(assoc, model);}}).classic(next);} else {- 270
next();}} else {- 48
this._clearAssociations(model);- 48
next();}},_preSave: function (next, model) {- 199
this.__checkAndSetAssociation(next, model);},_preUpdate: function (next, model) {- 127
this.__checkAndSetAssociation(next, model);},//override//@see _Association_postSave: function (next, model) {- 199
if (this.isEager() && !this.associationLoaded(model)) {- 12
this.fetch(model).classic(next);} else {- 187
next();}},//override//@see _Association_postLoad: function (next, model) {- 712
if (this.isEager() && !this.associationLoaded(model)) {- 90
this.fetch(model).classic(next);} else {- 622
next();}},//override//@see _Association_setter: function (val, model) {- 8
if (!isUndefinedOrNull(val)) {- 8
val = this._toModel(val);- 8
this.__setValue(model, val);- 8
if (!val.isNew) {- 0
var recip = this.model._findAssociation(this);- 0
if (recip) {//set up our association- 0
recip[1]._setAssociationKeys(val, model);}}- 0
} else if (!model.isNew && isNull(val)) {- 0
var keys = this._getAssociationKey(model)[0].forEach(function (k) {- 0
model[k] = null;});}}}});
|
plugins/query.js
|
Coverage83.67
SLOC675
LOC147
Missed24
|
- 1
var comb = require("comb"),asyncArray = comb.async.array,when = comb.when,isBoolean = comb.isBoolean,isArray = comb.isArray,isHash = comb.isHash,isUndefined = comb.isUndefined,isInstanceOf = comb.isInstanceOf,isEmpty = comb.isEmpty,Dataset = require("../dataset"),ModelError = require("../errors").ModelError,Promise = comb.Promise,PromiseList = comb.PromiseList;- 1
var QueryPlugin = comb.define(null, {instance: {/**@lends patio.Model.prototype*/_getPrimaryKeyQuery: function () {- 1777
var q = {}, pk = this.primaryKey;- 1777
for (var i = 0, l = pk.length; i < l; i++) {- 1777
var k = pk[i];- 1777
q[k] = this[k];}- 1777
return q;},_clearPrimaryKeys: function () {- 430
var pk = this.primaryKey;- 430
for (var i = 0, l = pk.length; i < l; i++) {- 430
this.__values[pk[i]] = null;}},__callHooks: function (event, options, cb) {- 1654
var self = this;- 1654
return this._hook("pre", event, options).chain(function () {- 1609
return cb();}).chain(function () {- 1609
return self._hook("post", event, options);});},reload: function () {- 11
if (this.synced) {- 11
var self = this;- 11
return this.__callHooks("load", null, function () {- 11
return self.__reload();}).chain(function () {- 11
return self;});} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},/*** Forces the reload of the data for a particular model instance. The Promise returned is resolved with the* model.** @example** myModel.reload().chain(function(model){* //model === myModel* });** @return {comb.Promise} resolved with the model instance.*/__reload: function () {- 1184
if (!this.__isNew) {- 1184
var self = this;- 1184
return this.dataset.naked().filter(this._getPrimaryKeyQuery()).one().chain(function (values) {- 1184
self.__setFromDb(values, true);- 1184
return self;});} else {- 0
return when(this);}},/*** This method removes the instance of the model. If the model {@link patio.Model#isNew} then the promise is* resolved with a 0 indicating no rows were affected. Otherwise the model is removed, primary keys are cleared* and the model's isNew flag is set to true.** @example* myModel.remove().chain(function(){* //model is deleted* assert.isTrue(myModel.isNew);* });** //dont use a transaction to remove this model* myModel.remove({transaction : false}).chain(function(){* //model is deleted* assert.isTrue(myModel.isNew);* });** @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* removing the model.** @return {comb.Promise} called back after the deletion is successful.*/remove: function (options) {- 430
if (this.synced) {- 430
if (!this.__isNew) {- 430
var self = this;- 430
return this._checkTransaction(options, function () {- 430
return self.__callHooks("remove", [options], function () {- 430
return self._remove(options);}).chain(function () {- 430
self._clearPrimaryKeys();- 430
self.__isNew = true;- 430
if (self._static.emitOnRemove) {- 430
self.emit("remove", this);- 430
self._static.emit("remove", this);}}).chain(function () {- 430
return self;});});} else {- 0
return when(0);}} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},_remove: function () {- 424
return this.dataset.filter(this._getPrimaryKeyQuery()).remove();},/*** @private* Called after a save action to reload the model properties,* abstracted out so this can be overidden by sub classes*/_saveReload: function (options) {- 1017
options || (options = {});- 1017
var reload = isBoolean(options.reload) ? options.reload : this._static.reloadOnSave;- 1017
return reload ? this.__reload() : when(this);},/*** @private* Called after an update action to reload the model properties,* abstracted out so this can be overidden by sub classes*/_updateReload: function (options) {- 160
options = options || {};- 160
options || (options = {});- 160
var reload = isBoolean(options.reload) ? options.reload : this._static.reloadOnUpdate;- 160
return reload ? this.__reload() : when(this);},/*** Updates a model. This action checks if the model is not new and values have changed.* If the model is new then the {@link patio.Model#save} action is called.** When updating a model you can pass values you want set as the first argument.** {@code** someModel.update({* myVal1 : "newValue1",* myVal2 : "newValue2",* myVal3 : "newValue3"* }).chain(function(){* //do something* }, errorHandler);** }** Or you can set values on the model directly** {@code** someModel.myVal1 = "newValue1";* someModel.myVal2 = "newValue2";* someModel.myVal3 = "newValue3";** //update model with current values* someModel.update().chain(function(){* //do something* });** }** Update also accepts an options object as the second argument allowing the overriding of default behavior.** To override <code>useTransactions</code> you can set the <code>transaction</code> option.** {@code* someModel.update(null, {transaction : false});* }** You can also override the <code>reloadOnUpdate</code> property by setting the <code>reload</code> option.* {@code* someModel.update(null, {reload : false});* }** @param {Object} [vals] optional values hash to set on the model before saving.* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* updating the model.* @param {Boolean} [options.reload] boolean indicating if the model should be reloaded after the update. This will take* precedence over {@link patio.Model.reloadOnUpdate}** @return {comb.Promise} resolved when the update action has completed.*/update: function (vals, options) {- 156
if (this.synced) {- 156
if (!this.__isNew) {- 156
var self = this;- 156
return this._checkTransaction(options, function () {- 156
if (isHash(vals)) {- 107
self.__set(vals);}- 156
var saveChange = !isEmpty(self.__changed);- 156
return self.__callHooks("update", [options], function () {- 156
return saveChange ? self._update(options) : null;}).chain(function () {- 156
return self._updateReload(options);}).chain(function () {- 156
if (self._static.emitOnUpdate) {- 156
self.emit("update", self);- 156
self._static.emit("update", self);}}).chain(function () {- 156
return self;});});- 0
} else if (this.__isNew && this.__isChanged) {- 0
return this.save(vals, options);} else {- 0
return when(this);}} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},_update: function (options) {- 136
var ret = this.dataset.filter(this._getPrimaryKeyQuery()).update(this.__changed);- 136
this.__changed = {};- 136
return ret;},/*** Saves a model. This action checks if the model is new and values have changed.* If the model is not new then the {@link patio.Model#update} action is called.** When saving a model you can pass values you want set as the first argument.** {@code** someModel.save({* myVal1 : "newValue1",* myVal2 : "newValue2",* myVal3 : "newValue3"* }).chain(function(){* //do something* }, errorHandler);** }** Or you can set values on the model directly** {@code** someModel.myVal1 = "newValue1";* someModel.myVal2 = "newValue2";* someModel.myVal3 = "newValue3";** //update model with current values* someModel.save().chain(function(){* //do something* });** }** Save also accepts an options object as the second argument allowing the overriding of default behavior.** To override <code>useTransactions</code> you can set the <code>transaction</code> option.** {@code* someModel.save(null, {transaction : false});* }** You can also override the <code>reloadOnSave</code> property by setting the <code>reload</code> option.* {@code* someModel.save(null, {reload : false});* }*** @param {Object} [vals] optional values hash to set on the model before saving.* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* saving the model.* @param {Boolean} [options.reload] boolean indicating if the model should be reloaded after the save. This will take* precedence over {@link patio.Model.reloadOnSave}** @return {comb.Promise} resolved when the save action has completed.*/save: function (vals, options) {- 1103
if (this.synced) {- 1103
if (this.__isNew) {- 1057
var self = this;- 1057
return this._checkTransaction(options, function () {- 1057
if (isHash(vals)) {- 0
self.__set(vals);}- 1057
return self.__callHooks("save", [options], function () {- 1012
return self._save(options);}).chain(function () {- 1012
return self._saveReload(options);}).chain(function () {- 1012
if (self._static.emitOnSave) {- 1012
self.emit("save", self);- 1012
self._static.emit("save", self);}}).chain(function () {- 1012
return self;});});} else {- 46
return this.update(vals, options);}} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},_save: function (options) {- 1007
var pk = this._static.primaryKey[0], self = this;- 1007
return this.dataset.insert(this._toObject()).chain(function (id) {- 1007
self.__ignore = true;- 1007
if (id) {- 1007
self[pk] = id;}- 1007
self.__ignore = false;- 1007
self.__isNew = false;- 1007
self.__isChanged = false;- 1007
return self;});},getUpdateSql: function () {- 4
return this.updateDataset.filter(this._getPrimaryKeyQuery()).updateSql(this.__changed);},getInsertSql: function () {- 4
return this.insertDataset.insertSql(this._toObject());},getRemoveSql: function () {- 2
return this.removeDataset.filter(this._getPrimaryKeyQuery()).deleteSql;},getters: {updateSql: function () {- 4
return this.getUpdateSql();},insertSql: function () {- 4
return this.getInsertSql();},removeSql: function () {- 2
return this.getRemoveSql();},deleteSql: function () {- 1
return this.removeSql;}}},static: {/**@lends patio.Model*//*** Set to false to prevent the emitting on an event when a model is saved.* @default true*/emitOnSave: true,/*** Set to false to prevent the emitting on an event when a model is updated.* @default true*/emitOnUpdate: true,/*** Set to false to prevent the emitting on an event when a model is removed.* @default true*/emitOnRemove: true,/*** Retrieves a record by the primaryKey/s of a table.** @example** var User = patio.getModel("user");** User.findById(1).chain(function(userOne){** });** //assume the primary key is a compostie of first_name and last_name* User.findById(["greg", "yukon"]).chain(function(userOne){** });*** @param {*} id the primary key of the record to find.** @return {comb.Promise} called back with the record or null if one is not found.*/findById: function (id) {- 4
var pk = this.primaryKey;- 4
pk = pk.length === 1 ? pk[0] : pk;- 4
var q = {};- 4
if (isArray(id) && isArray(pk)) {- 0
if (id.length === pk.length) {- 0
pk.forEach(function (k, i) {- 0
q[k] = id[i];});} else {- 0
throw new ModelError("findById : ids length does not equal the primaryKeys length.");}} else {- 4
q[pk] = id;}- 4
return this.filter(q).one();},/*** Finds a single model according to the supplied filter.* See {@link patio.Dataset#filter} for filter options.**** @param id*/find: function (id) {- 0
return this.filter.apply(this, arguments).first();},/*** Finds a single model according to the supplied filter.* See {@link patio.Dataset#filter} for filter options. If the model* does not exist then a new one is created as passed back.* @param q*/findOrCreate: function (q) {- 0
var self = this;- 0
return this.find(q).chain(function (res) {- 0
return res || self.create(q);});},/*** Update multiple rows with a set of values.** @example* var User = patio.getModel("user");** //BEGIN* //UPDATE `user` SET `password` = NULL WHERE (`last_accessed` <= '2011-01-27')* //COMMIT* User.update({password : null}, function(){* return this.lastAccessed.lte(comb.date.add(new Date(), "year", -1));* });* //same as* User.update({password : null}, {lastAccess : {lte : comb.date.add(new Date(), "year", -1)}});** //UPDATE `user` SET `password` = NULL WHERE (`last_accessed` <= '2011-01-27')* User.update({password : null}, function(){* return this.lastAccessed.lte(comb.date.add(new Date(), "year", -1));* }, {transaction : false});** @param {Object} vals a hash of values to update. See {@link patio.Dataset#update}.* @param query a filter to apply to the UPDATE. See {@link patio.Dataset#filter}.** @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* updating the models.** @return {Promise} a promise that is resolved once the update statement has completed.*/update: function (vals, /*?object*/query, options) {- 2
options = options || {};- 2
var dataset = this.dataset;- 2
return this._checkTransaction(options, function () {- 2
if (!isUndefined(query)) {- 2
dataset = dataset.filter(query);}- 2
return dataset.update(vals);});},/*** Remove(delete) models. This can be used to do a mass delete of models.** @example* var User = patio.getModel("user");** //remove all users* User.remove();** //remove all users who's names start with a.* User.remove({name : /A%/i});** //remove all users who's names start with a, without a transaction.* User.remove({name : /A%/i}, {transaction : false});** @param {Object} [q] query to filter the rows to remove. See {@link patio.Dataset#filter}.* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* removing the models.* @param {Boolean} [options.load=true] boolean set to prevent the loading of each model. This is more efficient* but the pre/post remove hooks not be notified of the deletion.** @return {comb.Promise} called back when the removal completes.*/remove: function (q, options) {- 284
options = options || {};- 284
var loadEach = isBoolean(options.load) ? options.load : true;//first find all records so we call alert the middleware for each model- 284
var ds = this.dataset.filter(q);- 284
return this._checkTransaction(options, function () {- 284
if (loadEach) {- 284
return ds.map(function (r) {//todo this sucks find a better way!- 366
return r.remove(options);});} else {- 0
return ds.remove();}});},/*** Similar to remove but takes an id or an array for a composite key.** @example** User.removeById(1);** @param id id or an array for a composite key, to find the model by* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* removing the model.** @return {comb.Promise} called back when the removal completes.*/removeById: function (id, options) {- 0
var self = this;- 0
return this._checkTransaction(options, function () {- 0
return self.findById(id).chain(function (model) {- 0
return model ? model.remove(options) : null;});});},/*** Save either a new model or list of models to the database.** @example* var Student = patio.getModel("student");* Student.save([* {* firstName:"Bob",* lastName:"Yukon",* gpa:3.689,* classYear:"Senior"* },* {* firstName:"Greg",* lastName:"Horn",* gpa:3.689,* classYear:"Sohpmore"* },* {* firstName:"Sara",* lastName:"Malloc",* gpa:4.0,* classYear:"Junior"* },* {* firstName:"John",* lastName:"Favre",* gpa:2.867,* classYear:"Junior"* },* {* firstName:"Kim",* lastName:"Bim",* gpa:2.24,* classYear:"Senior"* },* {* firstName:"Alex",* lastName:"Young",* gpa:1.9,* classYear:"Freshman"* }* ]).chain(function(users){* //work with the users* });** Save a single record* MyModel.save(m1);** @param {patio.Model|Object|patio.Model[]|Object[]} record the record/s to save.* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* saving the models.** @return {comb.Promise} called back with the saved record/s.*/save: function (items, options) {- 39
options = options || {};- 39
var isArr = isArray(items), Self = this;- 39
return this._checkTransaction(options, function () {- 39
return asyncArray(items).map(function (o) {- 517
if (!isInstanceOf(o, Self)) {- 57
o = new Self(o);}- 517
return o.save(null, options);}).chain(function (res) {- 39
return isArr ? res : res[0];});});}}}).as(exports, "QueryPlugin");- 1
Dataset.ACTION_METHODS.concat(Dataset.QUERY_METHODS).forEach(function (m) {- 163
if (!QueryPlugin[m]) {- 99
QueryPlugin[m] = function () {- 295
if (this.synced) {- 295
var ds = this.dataset;- 295
return ds[m].apply(ds, arguments);} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}};}});
|
associations/_Association.js
|
Coverage83.73
SLOC530
LOC166
Missed27
|
- 1
var comb = require("comb-proxy"),define = comb.define,isUndefined = comb.isUndefined,isUndefinedOrNull = comb.isUndefinedOrNull,isBoolean = comb.isBoolean,isString = comb.isString,isHash = comb.isHash,when = comb.when,isFunction = comb.isFunction,isInstanceOf = comb.isInstanceOf,Promise = comb.Promise,PromiseList = comb.PromiseList,array = comb.array,toArray = array.toArray,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) {- 49
options = options || {};- 49
if (!options.model) {- 0
throw new Error("Model is required for " + this.type + " association");}- 49
this._model = options.model;- 49
this.patio = patio;- 49
this.__opts = options;- 49
!isUndefined(options.isCascading) && (this.isCascading = options.isCascading);- 49
this.filter = filter;- 49
this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;- 49
this.__hooks ={before: {add: null, remove: null, "set": null, load: null}, after: {add: null, remove: null, "set": null, load: null}};- 49
var hooks = ["Add", "Remove", "Set", "Load"];- 49
["before", "after"].forEach(function (h) {- 98
hooks.forEach(function (a) {- 392
var hookName = h + a, hook;- 392
if (isFunction((hook = options[hookName]))) {- 0
this.__hooks[h][a.toLowerCase()] = hook;}}, this);}, this);- 49
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) {- 102
if (!this.readOnly) {- 102
delete model.__associations[this.name];}},_forceReloadAssociations: function (model) {- 217
if (!this.readOnly) {- 217
delete model.__associations[this.name];- 217
return model[this.name];}},/*** @return {Boolean} true if the association is eager.*/isEager: function () {- 2043
return this.fetchType === fetch.EAGER;},_checkAssociationKey: function (parent) {- 672
var q = {};- 672
this._setAssociationKeys(parent, q);- 672
return Object.keys(q).every(function (k) {- 672
return q[k] !== null;});},_getAssociationKey: function () {- 4149
var options = this.__opts, key, ret = [], lk, rk;- 4149
if (!isUndefinedOrNull((key = options.key))) {- 606
if (this.supportsStringKey && isString(key)) {//normalize the key first!- 339
ret = [[this.isOwner ? this.defaultLeftKey : key],[this.isOwner ? key : this.defaultRightKey]];- 267
} else if (this.supportsHashKey && isHash(key)) {- 267
var leftKey = Object.keys(key)[0];- 267
var rightKey = key[leftKey];- 267
ret = [[leftKey],[rightKey]];- 0
} else if (this.supportsCompositeKey && isArray(key)) {- 0
ret = [[key],null];}- 3543
} else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey)) && !isUndefinedOrNull((rk = options.rightKey)))) {- 140
ret = [toArray(lk), toArray(rk)];} else {//todo handle composite primary keys- 3403
ret = [[this.defaultLeftKey],[this.defaultRightKey]];}- 4149
return ret;},_setAssociationKeys: function (parent, model, val) {- 1489
var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1], i = leftKey.length - 1;- 1489
if (leftKey && rightKey) {- 1489
for (; i >= 0; i--) {- 1489
model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]] ? parent[leftKey[i]] : null;}} else {- 0
for (; i >= 0; i--) {- 0
var k = leftKey[i];- 0
model[k] = !isUndefined(val) ? val : parent[k] ? parent[k] : null;}}},_setDatasetOptions: function (ds) {- 764
var options = this.__opts || {};- 764
var order, limit, distinct, select, query;//allow for multi key ordering- 764
if (!isUndefined((select = this.select))) {- 0
ds = ds.select.apply(ds, toArray(select));}- 764
if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {- 0
ds = ds.filter(query);}- 764
if (isFunction(this.filter)) {- 302
var ret = this.filter.apply(this, [ds]);- 302
if (isInstanceOf(ret, ds._static)) {- 302
ds = ret;}}- 764
if (!isUndefined((distinct = options.distinct))) {- 0
ds = ds.limit.apply(ds, toArray(distinct));}- 764
if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {- 0
ds = ds.order.apply(ds, toArray(order));}- 764
if (!isUndefined((limit = options.limit))) {- 0
ds = ds.limit.apply(ds, toArray(limit));}- 764
return ds;},/***Filters our associated dataset to load our association.**@return {Dataset} the dataset with all filters applied.**/_filter: function (parent) {- 559
var options = this.__opts || {};- 559
var ds, self = this;- 559
if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {- 0
ds = ds.apply(parent, [parent]);}- 559
if (!ds) {- 559
var q = {};- 559
this._setAssociationKeys(parent, q);- 559
ds = this.model.dataset.naked().filter(q);- 559
var recip = this.model._findAssociation(this);- 559
recip && (recip = recip[1]);- 559
ds.rowCb = function (item) {- 500
var model = self._toModel(item, true);- 500
recip && recip.__setValue(model, parent);//call hook to finish other model associations- 500
return model._hook("post", "load").chain(function () {- 500
return model;});};- 0
} else if (!ds.rowCb && this.model) {- 0
ds.rowCb = function (item) {- 0
var model = self._toModel(item, true);//call hook to finish other model associations- 0
return model._hook("post", "load").chain(function () {- 0
return model;});};}- 559
return this._setDatasetOptions(ds);},__setValue: function (parent, model) {- 1714
var associations;- 1714
if (this._fetchMethod === "all") {- 891
associations = !isArray(model) ? [model] : model;} else {- 823
associations = isArray(model) ? model[0] : model;}- 1714
parent.__associations[this.name] = associations;- 1714
return parent.__associations[this.name];},fetch: function (parent) {- 672
var ret = new Promise();- 672
if (this._checkAssociationKey(parent)) {- 617
var self = this;- 617
return this._filter(parent)[this._fetchMethod]().chain(function (result) {- 616
self.__setValue(parent, result);- 616
parent = null;- 616
return result;});} else {- 55
this.__setValue(parent, null);- 55
ret.callback(null);}- 55
return ret;},/*** Middleware called before a model is removed.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being acted up.*/_preRemove: function (next, model) {- 217
if (this.isOwner && !this.isCascading) {- 46
var q = {};- 46
this._setAssociationKeys(model, q, null);- 46
model[this.associatedDatasetName].update(q).classic(next);} else {- 171
next();}},/*** Middleware called aft era model is removed.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_postRemove: function (next, model) {- 451
next();},/*** Middleware called before a model is saved.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_preSave: function (next, model) {- 257
next();},/*** Middleware called after a model is saved.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_postSave: function (next, model) {- 0
next();},/*** Middleware called before a model is updated.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_preUpdate: function (next, model) {- 2
next();},/*** Middleware called before a model is updated.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_postUpdate: function (next, model) {- 127
next();},/*** Middleware called before a model is loaded.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_preLoad: function (next, model) {- 1402
next();},/*** Middleware called after a model is loaded.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_postLoad: function (next, model) {- 0
next();},/*** Alias used to explicitly set an association on a model.* @param {*} val the value to set the association to* @param {_Association} self reference to the Association that is being called.*/_setter: function (val, model) {- 0
model.__associations[this.name] = val;},associationLoaded: function (model) {- 2375
return model.__associations.hasOwnProperty(this.name);},getAssociation: function (model) {- 758
return model.__associations[this.name];},/*** Alias used to explicitly get an association on a model.* @param {_Association} self reference to the Association that is being called.*/_getter: function (model) {//if we have them return them;- 520
if (this.associationLoaded(model)) {- 185
var assoc = this.getAssociation(model);- 185
return this.isEager() ? assoc : when(assoc);- 335
} else if (model.isNew) {- 0
return null;} else {- 335
return this.fetch(model);}},_toModel: function (val, fromDb) {- 1137
var Model = this.model;- 1137
if (!isUndefinedOrNull(Model)) {- 1137
if (!isInstanceOf(val, Model)) {- 964
val = new this.model(val, fromDb);}} else {- 0
throw new PatioError("Invalid model " + this.name);}- 1137
return val;},/*** Method to inject functionality into a model. This method alters the model* to prepare it for associations, and initializes all required middleware calls* to fulfill requirements needed to loaded the associations.** @param {Model} parent the model that is having an associtaion set on it.* @param {String} name the name of the association.*/inject: function (parent, name) {- 49
this.name = name;- 49
var self = this;- 49
this.parent = parent;- 49
var parentProto = parent.prototype;- 49
parentProto["__defineGetter__"](name, function () {- 520
return self._getter(this);});- 49
parentProto["__defineGetter__"](this.associatedDatasetName, function () {- 54
return self._filter(this);});- 49
if (!this.readOnly && this.createSetter) {//define a setter because we arent read only- 49
parentProto["__defineSetter__"](name, function (vals) {- 115
self._setter(vals, this);});}//set up all callbacks- 49
["pre", "post"].forEach(function (op) {- 98
["save", "update", "remove", "load"].forEach(function (type) {- 392
parent[op](type, function (next) {- 5004
return self["_" + op + type.charAt(0).toUpperCase() + type.slice(1)](next, this);});}, this);}, this);},getters: {select: function () {- 559
return this.__opts.select;},defaultLeftKey: function () {- 2603
var ret = "";- 2603
if (this.isOwner) {- 1915
ret = this.__opts.primaryKey || this.parent.primaryKey[0];} else {- 688
ret = this.model.tableName + "Id";}- 2603
return ret;},defaultRightKey: function () {- 2500
return this.associatedModelKey;},//Returns our modelmodel: function () {- 2734
return this["__model__"] || (this["__model__"] = this.patio.getModel(this._model, this.parent.db));},associatedModelKey: function () {- 2500
var ret = "";- 2500
if (this.isOwner) {- 1694
ret = this.__opts.primaryKey || this.parent.tableName + "Id";} else {- 806
ret = this.model.primaryKey[0];}- 2500
return ret;},associatedDatasetName: function () {- 102
return this.name + "Dataset";},removeAssociationFlagName: function () {- 27
return "__remove" + this.name + "association";}}},static: {/**@lends patio.associations.Association*/fetch: {LAZY: "lazy",EAGER: "eager"}}}).as(module);
|
database/connect.js
|
Coverage85.19
SLOC163
LOC54
Missed8
|
- 1
var comb = require("comb"),define = comb.define,merge = comb.merge,isHash = comb.isHash,isString = comb.isString,PromiseList = comb.PromiseList,format = comb.string.format,errors = require("../errors"),NotImplemented = errors.NotImplemented,URL = require("url"),DatabaseError = errors.DatabaseError;- 1
var ADAPTERS = {};- 1
var DB = define(null, {instance: {/**@lends patio.Database.prototype*//*** Disconnects the database closing all connections.* @return Promise a promise that is resolved once all the connections have been closed.*/disconnect: function () {- 38
var self = this;- 38
return this.pool.endAll().chain(function () {- 38
return self.onDisconnect(self);}).chain(function () {- 38
return null;});},onDisconnect: function () {},/*** This is an abstract method that should be implemented by adapters to create* a connection to the database.* @param {Object} options options that are adapter specific.*/createConnection: function (options) {- 0
throw new NotImplemented("Create connection must be implemented by the adapter");},/*** This is an abstract method that should be implemented by adapters to close* a connection to the database.* @param conn the database connection to close.*/closeConnection: function (conn) {- 0
throw new NotImplemented("Close connection must be implemented by the adapter");},/*** Validates a connection before it is returned to the {@link patio.ConnectionPool}. This* method should be implemented by the adapter.* @param conn*/validate: function (conn) {- 0
throw new NotImplemented("Validate must be implemented by the adapter");},/*** @ignore*/getters: {uri: function () {/*** @ignore*/- 2
if (!this.opts.uri) {- 0
var opts = {protocol: this.type,hostname: this.opts.host,auth: format("{user}:{password}", this.opts),port: this.opts.port,pathname: "/" + this.opts.database};- 0
return URL.format(opts);}- 2
return this.opts.uri;},url: function () {- 1
return this.uri;}}},"static": {/**@lends patio.Database*//*** Creates a connection to a Database see {@link patio#createConnection}.*/connect: function (connectionString, opts) {- 38
opts = opts || {};- 38
if (isString(connectionString)) {- 38
var url = URL.parse(connectionString, true);- 38
if (url.auth) {- 37
var parts = url.auth.split(":");- 37
if (!opts.user) {- 37
opts.user = parts[0];}- 37
if (!opts.password) {- 37
opts.password = parts[1];}}- 38
opts.type = url.protocol.replace(":", "");- 38
opts.host = url.hostname;- 38
if (url.port) {- 33
opts.port = url.port;}- 38
if (url.pathname) {- 38
var path = url.pathname;- 38
var pathParts = path.split("/").slice(1);- 38
if (pathParts.length >= 1) {- 38
opts.database = pathParts[0];}}- 38
opts = merge(opts, url.query, {uri: connectionString});} else {- 0
opts = merge({}, connectionString, opts);}- 38
if (opts && isHash(opts) && (opts.adapter || opts.type)) {- 38
var type = (opts.type = opts.adapter || opts.type);- 38
var Adapter = ADAPTERS[type];- 38
if (Adapter) {- 38
var adapter = new Adapter(opts);- 38
this.DATABASES.push(adapter);- 38
return adapter;} else {- 0
throw new DatabaseError(type + " adapter was not found");}} else {- 0
throw new DatabaseError("Options required when connecting.");}},setAdapterType: function (type) {- 6
type = type.toLowerCase();- 6
this.type = type;- 6
ADAPTERS[type] = this;},disconnect: function (cb) {- 39
var dbs = this.DATABASES;- 39
var ret = new PromiseList(dbs.map(function (d) {- 37
return d.disconnect();}), true);- 39
dbs.length = 0;- 39
ret.classic(cb);- 39
return ret.promise();},ADAPTERS: ADAPTERS}}).as(module);- 1
DB.setAdapterType("default");
|
dataset/index.js
|
Coverage87.18
SLOC461
LOC78
Missed10
|
- 1
var comb = require("comb"),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,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;- 1
var LOGGER = comb.logger("patio.Dataset");- 1
define([actions, graph, features, query, sql], {instance: {/**@lends patio.Dataset.prototype*//*** Class that is used for querying/retrieving datasets from a database.** <p> Dynamically generated 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) {- 27052
this._super(arguments);- 27052
this.db = db;- 27052
this.__opts = {};- 27052
this.__rowCb = null;- 27052
if (db) {- 13669
this.__quoteIdentifiers = db.quoteIdentifiers;- 13669
this.__identifierInputMethod = db.identifierInputMethod;- 13669
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) {- 13885
opts = isUndefined(opts) ? {} : opts;- 13885
var ds = new this._static(this.db, {});- 13885
ds.rowCb = this.rowCb;- 13885
this._static.FEATURES.forEach(function (f) {- 194390
ds[f] = this[f];}, this);- 13885
var dsOpts = ds.__opts = merge({}, this.__opts, opts);- 13885
ds.identifierInputMethod = this.identifierInputMethod;- 13885
ds.identifierOutputMethod = this.identifierOutputMethod;- 13885
var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;- 13885
if (Object.keys(opts).some(function (o) {- 12701
return columnChangeOpts.indexOf(o) !== -1;})) {- 2593
dsOpts.columns = null;}- 13885
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) {- 14174
if (isString(name)) {- 9735
var parts = this._splitString(name),schema = parts[0], table = parts[1], alias = parts[2],identifier;- 9735
if (schema && table && alias) {- 172
identifier = new AliasedExpression(new QualifiedIdentifier(schema, table), alias);- 9563
} else if (schema && table) {- 2170
identifier = new QualifiedIdentifier(schema, table);- 7393
} else if (table && alias) {- 22
identifier = new AliasedExpression(new Identifier(table), alias);} else {- 7371
identifier = new Identifier(table);}- 9735
return identifier;} else {- 4439
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) {- 13073
var ret, m;- 13073
if ((m = s.match(this._static.COLUMN_REF_RE1)) !== null) {- 175
ret = m.slice(1);}- 12898
else if ((m = s.match(this._static.COLUMN_REF_RE2)) !== null) {- 24
ret = [null, m[1], m[2]];}- 12874
else if ((m = s.match(this._static.COLUMN_REF_RE3)) !== null) {- 2178
ret = [m[1], m[2], null];}else {- 10696
ret = [null, s, null];}- 13073
return ret;},/*** @ignore**/getters: {rowCb: function () {- 19768
return this.__rowCb;},identifierInputMethod: function () {- 13885
return this.__identifierInputMethod;},identifierOutputMethod: function () {- 13885
return this.__identifierOutputMethod;},firstSourceAlias: function () {- 488
var source = this.__opts.from;- 488
if (isUndefinedOrNull(source) || !source.length) {- 2
throw new DatasetError("No source specified for the query");}- 486
source = source[0];- 486
if (isInstanceOf(source, AliasedExpression)) {- 20
return source.alias;- 466
} else if (isString(source)) {- 0
var parts = this._splitString(source);- 0
var alias = parts[2];- 0
return alias ? alias : source;} else {- 466
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
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) {- 13956
this.__identifierInputMethod = meth;},identifierOutputMethod: function (meth) {- 13956
this.__identifierOutputMethod = meth;},rowCb: function (cb) {- 16957
if (isFunction(cb) || isNull(cb)) {- 16952
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);
|
model.js
|
Coverage87.69
SLOC1119
LOC333
Missed41
|
- 1
var comb = require("comb"),asyncArray = comb.async.array,isFunction = comb.isFunction,isUndefined = comb.isUndefined,isDefined = comb.isDefined,isBoolean = comb.isBoolean,isString = comb.isString,argsToArray = comb.argsToArray,isInstanceOf = comb.isInstanceOf,isHash = comb.isHash,when = comb.when,merge = comb.merge,toArray = comb.array.toArray,ModelError = require("./errors").ModelError,plugins = require("./plugins"),isUndefinedOrNull = comb.isUndefinedOrNull,AssociationPlugin = plugins.AssociationPlugin,QueryPlugin = plugins.QueryPlugin,Promise = comb.Promise,PromiseList = comb.PromiseList,HashTable = comb.collections.HashTable,hitch = comb.hitch,Middleware = comb.plugins.Middleware,EventEmitter = require("events").EventEmitter,util = require("util"),define = comb.define,patio;- 1
var MODELS = new HashTable();- 1
var applyColumnTransformMethod = function (val, meth) {- 763
return !isUndefinedOrNull(meth) ? isFunction(val[meth]) ? val[meth] : isFunction(comb[meth]) ? comb[meth](val) : val : val;};- 1
var Model = define([QueryPlugin, Middleware], {instance: {/*** @lends patio.Model.prototype*/__ignore: false,__changed: null,__values: null,/*** patio - read only** @type patio*/patio: null,/*** The database type such as mysql** @type String** */type: null,/*** Whether or not this model is new* */__isNew: true,/*** Signifies if the model has changed* */__isChanged: false,/*** Base class for all models.* <p>This is used through {@link patio.addModel}, <b>NOT directly.</b></p>** @constructs* @augments comb.plugins.Middleware** @param {Object} columnValues values of each column to be used by this Model.** @property {patio.Dataset} dataset a dataset to use to retrieve models from the database. The dataset* has the {@link patio.Dataset#rowCb} set to create instances of this model.* @property {String[]} columns a list of columns this models table contains.* @property {Object} schema the schema of this models table.* @property {String} tableName the table name of this models table.* @property {*} primaryKeyValue the value of this models primaryKey* @property {Boolean} isNew true if this model is new and does not exist in the database.* @property {Boolean} isChanged true if the model has been changed and not saved.** @borrows patio.Dataset#all as all* @borrows patio.Dataset#one as one* @borrows patio.Dataset#avg as avg* @borrows patio.Dataset#count as count* @borrows patio.Dataset#columns as columns* @borrows patio.Dataset#forEach as forEach* @borrows patio.Dataset#isEmpty as empty* @borrows patio.Dataset#first as first* @borrows patio.Dataset#get as get* @borrows patio.Dataset#import as import* @borrows patio.Dataset#insert as insert* @borrows patio.Dataset#insertMultiple as insertMultiple* @borrows patio.Dataset#saveMultiple as saveMultiple* @borrows patio.Dataset#interval as interval* @borrows patio.Dataset#last as last* @borrows patio.Dataset#map as map* @borrows patio.Dataset#max as max* @borrows patio.Dataset#min as min* @borrows patio.Dataset#multiInsert as multiInsert* @borrows patio.Dataset#range as range* @borrows patio.Dataset#selectHash as selectHash* @borrows patio.Dataset#selectMap as selectMap* @borrows patio.Dataset#selectOrderMap as selectOrderMap* @borrows patio.Dataset#set as set* @borrows patio.Dataset#singleRecord as singleRecord* @borrows patio.Dataset#singleValue as singleValue* @borrows patio.Dataset#sum as sum* @borrows patio.Dataset#toCsv as toCsv* @borrows patio.Dataset#toHash as toHash* @borrows patio.Dataset#truncate as truncate* @borrows patio.Dataset#addGraphAliases as addGraphAliases* @borrows patio.Dataset#and as and* @borrows patio.Dataset#distinct as distinct* @borrows patio.Dataset#except as except* @borrows patio.Dataset#exclude as exclude* @borrows patio.Dataset#is as is* @borrows patio.Dataset#isNot as isNot* @borrows patio.Dataset#eq as eq* @borrows patio.Dataset#neq as neq* @borrows patio.Dataset#lt as lt* @borrows patio.Dataset#lte as lte* @borrows patio.Dataset#gt as gt* @borrows patio.Dataset#gte as gte* @borrows patio.Dataset#forUpdate as forUpdate* @borrows patio.Dataset#from as from* @borrows patio.Dataset#fromSelf as fromSelf* @borrows patio.Dataset#graph as graph* @borrows patio.Dataset#grep as grep* @borrows patio.Dataset#group as group* @borrows patio.Dataset#groupAndCount as groupAndCount* @borrows patio.Dataset#groupBy as groupBy* @borrows patio.Dataset#having as having* @borrows patio.Dataset#intersect as intersect* @borrows patio.Dataset#invert as invert* @borrows patio.Dataset#limit as limit* @borrows patio.Dataset#lockStyle as lockStyle* @borrows patio.Dataset#naked as naked* @borrows patio.Dataset#or as or* @borrows patio.Dataset#order as order* @borrows patio.Dataset#orderAppend as orderAppend* @borrows patio.Dataset#orderBy as orderBy* @borrows patio.Dataset#orderMore as orderMore* @borrows patio.Dataset#orderPrepend as orderPrepend* @borrows patio.Dataset#qualify as qualify* @borrows patio.Dataset#reverse as reverse* @borrows patio.Dataset#reverseOrder as reverseOrder* @borrows patio.Dataset#select as select* @borrows patio.Dataset#selectAll as selectAll* @borrows patio.Dataset#selectAppend as selectAppend* @borrows patio.Dataset#selectMore as selectMore* @borrows patio.Dataset#setDefaults as setDefaults* @borrows patio.Dataset#setGraphAliases as setGraphAliases* @borrows patio.Dataset#setOverrides as setOverrides* @borrows patio.Dataset#unfiltered as unfiltered* @borrows patio.Dataset#ungraphed as ungraphed* @borrows patio.Dataset#ungrouped as ungrouped* @borrows patio.Dataset#union as union* @borrows patio.Dataset#unlimited as unlimited* @borrows patio.Dataset#unordered as unordered* @borrows patio.Dataset#where as where* @borrows patio.Dataset#with as with* @borrows patio.Dataset#withRecursive as withRecursive* @borrows patio.Dataset#withSql as withSql* @borrows patio.Dataset#naturalJoin as naturalJoin* @borrows patio.Dataset#naturalLeftJoin as naturalLeftJoin* @borrows patio.Dataset#naturalRightJoin as naturalRightJoin* @borrows patio.Dataset#naturalFullJoin as naturalFullJoin* @borrows patio.Dataset#crossJoin as crossJoin* @borrows patio.Dataset#innerJoin as innerJoin* @borrows patio.Dataset#fullOuterJoin as fullOuterJoin* @borrows patio.Dataset#rightOuterJoin as rightOuterJoin* @borrows patio.Dataset#leftOuterJoin as leftOuterJoin* @borrows patio.Dataset#fullJoin as fullJoin* @borrows patio.Dataset#rightJoin as rightJoin* @borrows patio.Dataset#leftJoin as leftJoin* */constructor: function (options, fromDb) {- 2793
if (this.synced) {- 2793
this.__emitter = new EventEmitter();- 2793
this._super(arguments);- 2793
this.patio = patio || require("./index");- 2793
fromDb = isBoolean(fromDb) ? fromDb : false;- 2793
this.__changed = {};- 2793
this.__values = {};- 2793
if (fromDb) {- 1535
this._hook("pre", "load");- 1535
this.__isNew = false;- 1535
this.__setFromDb(options, true);- 1535
if (this._static.emitOnLoad) {- 1535
this.emit("load", this);- 1535
this._static.emit("load", this);}} else {- 1258
this.__isNew = true;- 1258
this.__set(options);}} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},__set: function (values, ignore) {- 1382
values = values || {};- 1382
this.__ignore = ignore === true;- 1382
Object.keys(values).forEach(function (attribute) {- 6019
var value = values[attribute];//check if the column is a constrained value and is allowed to be set- 6019
!ignore && this._checkIfColumnIsConstrained(attribute);- 6019
this[attribute] = value;}, this);- 1382
this.__ignore = false;},__setFromDb: function (values, ignore) {- 2719
values = values || {};- 2719
this.__ignore = ignore === true;- 2719
var schema = this.schema;- 2719
Object.keys(values).forEach(function (column) {- 24689
var value = values[column];// Typecast value retrieved from db- 24689
if (schema.hasOwnProperty(column)) {- 24687
this.__values[column] = this._typeCastValue(column, value, ignore);} else {- 2
this[column] = value;}}, this);- 2719
this.__ignore = false;},/*** Set multiple values at once. Useful if you have a hash of properties that you want to set.** <b>NOTE:</b> This method will use the static restrictedColumns property of the model.** @example** myModel.setValues({firstName : "Bob", lastName : "yukon"});** //this will throw an error by default, assuming id is a pk.* myModel.setValues({id : 1, firstName : "Bob", lastName : "yukon"});** @param {Object} values value to set on the model.** @return {patio.Model} return this for chaining.*/setValues: function (values) {- 17
this.__set(values, false);- 17
return this;},_toObject: function () {- 1011
if (this.synced) {- 1011
var columns = this._static.columns, ret = {};- 1011
for (var i in columns) {- 10498
var col = columns[i];- 10498
var val = this.__values[col];- 10498
if (!isUndefined(val)) {- 6105
ret[col] = val;}}- 1011
return ret;} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},_addColumnToIsChanged: function (name, val) {- 7342
if (!this.isNew && !this.__ignore) {- 171
this.__isChanged = true;- 171
this.__changed[name] = val;}},_checkIfColumnIsConstrained: function (name) {- 6019
if (this.synced && !this.__ignore) {- 6019
var col = this.schema[name], restrictedCols = this._static.restrictedColumns || [];- 6019
if (!isUndefined(col) && (col.primaryKey && this._static.isRestrictedPrimaryKey) || restrictedCols.indexOf(name) !== -1) {- 0
throw new ModelError("Cannot set primary key of model " + this._static.tableName);}}},_getColumnValue: function (name) {- 5896
var val = this.__values[name];- 5896
var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];- 5896
var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;- 5896
return columnValue;},_setColumnValue: function (name, val) {- 7343
var ignore = this.__ignore;- 7343
val = this._typeCastValue(name, val, ignore);- 7342
var setterFunc = this["_set" + name.charAt(0).toUpperCase() + name.substr(1)];- 7342
var columnValue = isFunction(setterFunc) ? setterFunc.call(this, val, ignore) : val;- 7342
this._addColumnToIsChanged(name, columnValue);- 7342
this.__values[name] = columnValue;- 7342
if (this._static.emitOnColumnSet) {- 7342
this.emit("column", name, columnValue, ignore);}},//Typecast the value to the column's type if typecasting. Calls the database's//typecast_value method, so database adapters can override/augment the handling//for database specific column types._typeCastValue: function (column, value, fromDatabase) {- 32030
var colSchema, clazz = this._static;- 32030
if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {- 32030
var type = colSchema.type;- 32030
if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {- 3
value = null;}- 32030
var raiseOnError = clazz.raiseOnTypecastError;- 32030
if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {- 1
throw new ModelError("null is not allowed for the " + column + " column on model " + clazz.tableName);}- 32029
try {- 32029
value = clazz.db.typecastValue(type, value);} catch (e) {- 0
if (raiseOnError === true) {- 0
throw e;}}}- 32029
return value;},/*** Convert this model to an object, containing column, value pairs.** @return {Object} the object version of this model.**/toObject: function () {- 0
return this._toObject(false);},/*** Convert this model to JSON, containing column, value pairs.** @return {JSON} the JSON version of this model.**/toJSON: function () {- 0
return this.toObject();},/*** Convert this model to a string, containing column, value pairs.** @return {String} the string version of this model.**/toString: function () {- 0
return JSON.stringify(this.toObject(), null, 4);},/*** Convert this model to a string, containing column, value pairs.** @return {String} the string version of this model.**/valueOf: function () {- 0
return this.toObject();},_checkTransaction: function (options, cb) {- 2130
return this._static._checkTransaction(options, cb);},addListener: function () {- 0
var emitter = this.__emitter;- 0
return emitter.addListener.apply(emitter, arguments);},on: function () {- 6
var emitter = this.__emitter;- 6
return emitter.on.apply(emitter, arguments);},once: function () {- 0
var emitter = this.__emitter;- 0
return emitter.once.apply(emitter, arguments);},removeListener: function () {- 6
var emitter = this.__emitter;- 6
return emitter.removeListener.apply(emitter, arguments);},removeAllListeners: function () {- 0
var emitter = this.__emitter;- 0
return emitter.removeAllListeners.apply(emitter, arguments);},setMaxListeners: function () {- 0
var emitter = this.__emitter;- 0
return emitter.setMaxListeners.apply(emitter, arguments);},listeners: function () {- 0
var emitter = this.__emitter;- 0
return emitter.listeners.apply(emitter, arguments);},emit: function () {- 10475
var emitter = this.__emitter;- 10475
return emitter.emit.apply(emitter, arguments);},getters: {/**@lends patio.Model.prototype*//*Returns my actual primary key value*/primaryKeyValue: function () {- 47
return this[this.primaryKey];},/*Return if Im a new object*/isNew: function () {- 8951
return this.__isNew;},/*Return if Im changed*/isChanged: function () {- 0
return this.__isChanged;},/**@lends patio.Model.prototype*/primaryKey: function () {- 2259
return this._static.primaryKey;},tableName: function () {- 40
return this._static.tableName;},dataset: function () {- 2751
return this.__dataset || (this.__dataset = this._static.dataset);},removeDataset: function () {- 2
return this.__removeDataset || (this.__removeDataset = this._static.removeDataset);},queryDataset: function () {- 0
return this.__queryDataset || (this.__queryDataset = this._static.queryDataset);},updateDataset: function () {- 4
return this.__updateDataset || (this.__updateDataset = this._static.updateDataset);},insertDataset: function () {- 4
return this.__insertDataset || (this.__insertDataset = this._static.insertDataset);},db: function () {- 24
return this._static.db;},schema: function () {- 72798
return this._static.schema;},columns: function () {- 0
return this._static.columns;},synced: function () {- 11523
return this._static.synced;}}},static: {/*** @lends patio.Model*/synced: false,/*** Set to false to prevent the emitting of an event on load* @default true*/emitOnLoad: true,/*** Set to false to prevent the emitting of an event on the setting of a column value* @default true*/emitOnColumnSet: true,/*** Set to false to prevent empty strings from being type casted to null* @default true*/typecastEmptyStringToNull: true,/*** Set to false to prevent properties from being type casted when loaded from the database.* See {@link patio.Database#typecastValue}* @default true*/typecastOnLoad: true,/*** Set to false to prevent properties from being type casted when manually set.* See {@link patio.Database#typecastValue}* @default true*/typecastOnAssignment: true,/*** Set to false to prevent errors thrown while type casting a value from being propogated.* @default true*/raiseOnTypecastError: true,/*** Set to false to allow the setting of primary keys.* @default false*/isRestrictedPrimaryKey: true,/*** Set to false to prevent models from using transactions when saving, deleting, or updating.* This applies to the model associations also.*/useTransactions: true,/*** See {@link patio.Dataset#identifierOutputMethod}* @default null*/identifierOutputMethod: null,/*** See {@link patio.Dataset#identifierInputMethod}* @default null*/identifierInputMethod: null,/*** Set to false to prevent the reload of a model after saving.* @default true*/reloadOnSave: true,/*** Columns that should be restriced when setting values through the {@link patio.Model#set} method.**/restrictedColumns: null,/*** Set to false to prevent the reload of a model after updating.* @default true*/reloadOnUpdate: true,__camelize: false,__underscore: false,__columns: null,__schema: null,__primaryKey: null,__dataset: null,__db: null,__tableName: null,/*** The table that this Model represents.* <b>READ ONLY</b>*/table: null,/*** patio - read only** @type patio*/patio: null,init: function () {- 85
var emitter = new EventEmitter();- 85
["addListener", "on", "once", "removeListener","removeAllListeners", "setMaxListeners", "listeners", "emit"].forEach(function (name) {- 680
this[name] = hitch(emitter, emitter[name]);}, this);- 85
if (this.__tableName) {- 84
this._setTableName(this.__tableName);}- 85
if (this.__db) {- 41
this._setDb(this.__db);}},sync: function (cb) {- 2594
var ret = new Promise();- 2594
if (!this.synced) {- 86
var db = this.db, tableName = this.tableName, supers = this.__supers, self = this;- 86
ret = db.schema(tableName).chain(function (schema) {- 86
if (!self.synced && schema) {- 86
self._setSchema(schema);- 86
if (supers && supers.length) {- 3
return when(supers.map(function (sup) {- 3
return sup.sync();})).chain(function () {- 3
self.synced = true;- 3
supers.forEach(self.inherits, self);- 3
return self;});} else {- 83
self.synced = true;- 83
return self;}} else {- 0
var error = new ModelError("Unable to find schema for " + tableName);- 0
self.emit("error", error);- 0
throw error;}});} else {- 2508
ret.callback(this);}- 2594
if (isFunction(cb)) {- 0
ret.classic(cb);}- 2594
return ret.promise();},/*** Stub for plugins to notified of model inheritance** @param {patio.Model} model a model class to inherit from*/inherits: function (model) {},/*** Create a new model initialized with the specified values.** @param {Object} values the values to initialize the model with.** @returns {Model} instantiated model initialized with the values passed in.*/create: function (values) {//load an object from an object- 12
var Self = this;- 12
return new Self(values, false);},load: function (vals) {- 833
var Self = this, ret;- 833
if (!this.synced) {//sync our model- 0
ret = this.sync().chain(function () {- 0
var m = new Self(vals, true);//call the hooks!- 0
return m._hook("post", "load").chain(function () {- 0
return m;});});} else {- 833
var m = new Self(vals, true);//call the hooks!- 833
ret = m._hook("post", "load").chain(function () {- 833
return m;});}- 833
return ret;},_checkTransaction: function (opts, cb) {- 2455
if (isFunction(opts)) {- 487
cb = opts;- 487
opts = {};} else {- 1968
opts = opts || {};}- 2455
var retVal = null, errored = false, self = this;- 2455
return this.sync().chain(function () {- 2455
if (self.useTransaction(opts)) {- 2455
return self.db.transaction(opts, function () {- 2455
return when(cb()).chain(function (val) {- 2410
retVal = val;}, function (err) {- 45
retVal = err;- 45
errored = true;});}).chain(function () {- 2455
if (errored) {- 45
throw retVal;} else {- 2410
return retVal;}}, function (err) {- 45
if (errored) {- 45
throw retVal;} else {- 0
throw err;}});} else {- 0
return when(cb());}});},/*** @private* Returns a boolean indicating whether or not to use a transaction.* @param {Object} [opts] set a transaction property to override the {@link patio.Model#useTransaction}.*/useTransaction: function (opts) {- 2455
opts = opts || {};- 2455
return isBoolean(opts.transaction) ? opts.transaction === true : this.useTransactions === true;},_setDataset: function (ds) {- 3
this.__dataset = ds;- 3
if (ds.db) {- 3
this._setDb(ds.db);}},_setDb: function (db) {- 44
this.__db = db;},_setTableName: function (name) {- 84
this.__tableName = name;},_setColumns: function (cols) {- 89
var proto = this.prototype;- 89
if (this.__columns) {- 6
this.__columns.forEach(function (name) {- 16
delete proto[name];});}- 89
this.__columns = cols;- 89
cols.forEach(function (name) {- 763
this._defineColumnSetter(name);- 763
this._defineColumnGetter(name);}, this);},_setPrimaryKey: function (pks) {- 92
this.__primaryKey = pks || [];},_setSchema: function (schema) {- 89
var columns = [];- 89
var pks = [];- 89
for (var i in schema) {- 763
var col = schema[i];- 763
var name = applyColumnTransformMethod(i, this.identifierOutputMethod);- 763
schema[name] = col;- 763
columns.push(name);- 763
col.primaryKey && pks.push(name);}- 89
this.__schema = schema;- 89
this._setPrimaryKey(pks);- 89
this._setColumns(columns);},_defineColumnSetter: function (name) {/*Adds a setter to an object*/- 763
this.prototype["__defineSetter__"](name, function (val) {- 7343
this._setColumnValue(name, val);});},_defineColumnGetter: function (name) {- 763
this.prototype["__defineGetter__"](name, function () {- 5896
return this._getColumnValue(name);});},_getDataset: function () {- 2964
var ds = this.__dataset, self = this;- 2964
if (!ds) {- 77
ds = this.db.from(this.tableName);- 77
ds.rowCb = function (vals) {- 784
return self.load(vals);};- 77
this.identifierInputMethod && (ds.identifierInputMethod = this.identifierInputMethod);- 77
this.identifierOutputMethod && (ds.identifierOutputMethod = this.identifierOutputMethod);- 77
this.__dataset = ds;- 2887
} else if (!ds.rowCb) {- 6
ds.rowCb = function rowCb(vals) {- 23
return self.load(vals);};}- 2964
return ds;},_getQueryDataset: function () {- 0
return this._getDataset();},_getUpdateDataset: function () {- 4
return this._getDataset();},_getRemoveDataset: function () {- 2
return this._getDataset();},_getInsertDataset: function () {- 4
return this._getDataset();},/*** @ignore*/getters: {/**@lends patio.Model*//*** Set to true if this models column names should be use the "underscore" method when sending* keys to the database and to "camelize" method on columns returned from the database. If set to false see* {@link patio.Model#underscore}.* @field* @default false* @type {Boolean}*/camelize: function (camelize) {- 1
return this.__camelize;},/*** Set to true if this models column names should be use the "camelize" method when sending* keys to the database and to "underscore" method on columns returned from the database. If set to false see* {@link patio.Model#underscore}.* @field* @default false* @type {Boolean}*/underscore: function (underscore) {- 1
return this.__underscore;},/**@lends patio.Model*//*** The name of the table all instances of the this {@link patio.Model} use.* @field* @ignoreCode* @type String*/tableName: function () {- 4646
return this.__tableName;},/*** The database all instances of this {@link patio.Model} use.* @field* @ignoreCode* @type patio.Database*/db: function () {- 34720
var db = this.__db;- 34720
if (!db) {- 45
db = this.__db = patio.defaultDatabase;}- 34720
if (!db) {- 0
throw new ModelError("patio has not been connected to a database");}- 34720
return db;},/*** A dataset to use to retrieve instances of this {@link patio.Model{ from the database. The dataset* has the {@link patio.Dataset#rowCb} set to create instances of this model.* @field* @ignoreCode* @type patio.Dataset*/dataset: function () {- 2954
return this._getDataset();},removeDataset: function () {- 2
return this._getRemoveDataset();},queryDataset: function () {- 0
return this._getQueryDataset();},updateDataset: function () {- 4
return this._getUpdateDataset();},insertDataset: function () {- 4
return this._getInsertDataset();},/*** A list of columns this models table contains.* @field* @ignoreCode* @type String[]*/columns: function () {- 1015
return this.__columns;},/*** The schema of this {@link patio.Model}'s table. See {@link patio.Database#schema} for details* on the schema object.* @field* @ignoreCode* @type Object*/schema: function () {- 72802
if (this.synced) {- 72802
return this.__schema;} else {- 0
throw new ModelError("Model has not been synced yet");}},/*** The primaryKey column/s of this {@link patio.Model}* @field* @ignoreCode*/primaryKey: function () {- 6943
if (this.synced) {- 6943
return this.__primaryKey.slice(0);} else {- 0
throw new ModelError("Model has not been synced yet");}},/*** A reference to the global {@link patio}.* @field* @ignoreCode*/patio: function () {- 75
return patio || require("./index");}},/**@ignore*/setters: {/**@lends patio.Model*//**@ignore*/camelize: function (camelize) {- 26
camelize = camelize === true;- 26
if (camelize) {- 24
this.identifierOutputMethod = "camelize";- 24
this.identifierInputMethod = "underscore";}- 26
this.__camelize = camelize;- 26
this.__underscore = !camelize;},/**@ignore*/underscore: function (underscore) {- 1
underscore = underscore === true;- 1
if (underscore) {- 1
this.identifierOutputMethod = "underscore";- 1
this.identifierInputMethod = "camelize";}- 1
this.__underscore = underscore;- 1
this.__camelize = !underscore;}}}}).as(exports, "Model");- 1
function checkAndAddDBToTable(db, table) {- 86
if (!table.contains(db)) {- 30
table.set(db, new HashTable());}}- 1
var allModels = [];/**@ignore*/- 1
exports.create = function (name, supers, modelOptions) {- 86
if (!patio) {- 1
(patio = require("./index"));- 1
patio.on("disconnect", function () {- 36
allModels.length = 0;- 36
MODELS.clear();});}- 86
var db, ds, tableName, modelKey;- 86
if (isString(name)) {- 80
tableName = name;- 80
db = patio.defaultDatabase || "default";- 6
} else if (isInstanceOf(name, patio.Dataset)) {- 6
ds = name;- 6
tableName = ds.firstSourceAlias.toString();- 6
db = ds.db;}- 86
var hasSuper = false;- 86
if (isHash(supers) || isUndefinedOrNull(supers)) {- 83
modelOptions = supers;- 83
supers = [Model];} else {- 3
supers = toArray(supers);- 3
supers = supers.map(function (sup) {- 3
return exports.getModel(sup, db);});- 3
hasSuper = true;}- 86
var model;- 86
checkAndAddDBToTable(db, MODELS);- 86
var DEFAULT_PROTO = {instance: {}, "static": {}};- 86
modelOptions = merge(DEFAULT_PROTO, modelOptions || {});- 86
modelOptions.instance._hooks = ["save", "update", "remove", "load"];- 86
modelOptions.instance.__hooks = {pre: {}, post: {}};//Mixin the column setter/getters- 86
modelOptions["static"].synced = false;- 86
modelOptions["static"].__tableName = tableName;- 86
modelOptions["static"].__db = (db === "default" ? null : db);- 86
modelOptions["static"].__supers = hasSuper ? supers : [];- 86
modelOptions["static"].__dataset = ds;- 86
model = define(supers.concat(modelOptions.plugins || []).concat([AssociationPlugin]), modelOptions);- 86
["pre", "post"].forEach(function (op) {- 172
var optionsOp = modelOptions[op];- 172
if (optionsOp) {- 0
for (var i in optionsOp) {- 0
model[op](i, optionsOp[i]);}}});- 86
allModels.push(model);- 86
if (!(MODELS.get(db).contains(tableName))) {- 51
MODELS.get(db).set(tableName, model);}- 86
return model;};- 1
exports.syncModels = function (cb) {- 31
return asyncArray(allModels).forEach(function (model) {- 53
return model.sync();}, 1).classic(cb).promise();};- 1
var checkAndGetModel = function (db, name) {- 154
var ret;- 154
if (MODELS.contains(db)) {- 79
ret = MODELS.get(db).get(name);}- 154
return ret;};- 1
exports.getModel = function (name, db) {- 82
var ret = null;- 82
if (isDefined(name)) {- 82
!patio && (patio = require("./index"));- 82
if (isFunction(name)) {- 3
ret = name;} else {- 79
if (!db && isInstanceOf(name, patio.Dataset)) {- 2
db = name.db;- 2
name = name.firstSourceAlias.toString();}- 79
var defaultDb = patio.defaultDatabase;- 79
if (db) {- 53
ret = checkAndGetModel(db, name);- 53
if (!ret && db === defaultDb) {- 49
ret = checkAndGetModel("default", name);}} else {- 26
db = patio.defaultDatabase;- 26
ret = checkAndGetModel(db, name);- 26
if (!ret) {- 26
ret = checkAndGetModel("default", name);}}}} else {- 0
ret = name;}- 82
if (isUndefinedOrNull(ret)) {- 0
throw new ModelError("Model " + name + " has not been registered with patio");}- 82
return ret;};
|
database/schema.js
|
Coverage90.78
SLOC1265
LOC282
Missed26
|
- 1
"use strict";- 1
var comb = require("comb"),asyncArray = comb.async.array,isFunction = comb.isFunction,argsToArray = comb.argsToArray,array = comb.array,isArray = comb.isArray,isString = comb.isString,isUndefined = comb.isUndefined,isNumber = comb.isNumber,toArray = comb.array.toArray,hitch = comb.hitch,format = comb.string.format,Dataset = require("../dataset"),Promise = comb.Promise,PromiseList = comb.PromiseList,errors = require("../errors"),DatabaseError = errors.DatabaseError,generators = require("./schemaGenerators"),SchemaGenerator = generators.SchemaGenerator,AlterTableGenerator = generators.AlterTableGenerator,sql = require("../sql").sql,Time = sql.Time,TimeStamp = sql.TimeStamp,DateTime = sql.DateTime,Year = sql.Year,Float = sql.Float,Decimal = sql.Decimal,Json = sql.Json,isInstanceOf = comb.isInstanceOf,Identifier = sql.Identifier,QualifiedIdentifier = sql.QualifiedIdentifier,define = comb.define;- 1
define(null, {instance: {/**@lends patio.Database.prototype*//**@ignore*/constructor: function () {- 120
this._super(arguments);- 120
this.schemas = {};},/*** Adds a column to the specified table. This method expects a column name,* a datatype and optionally a hash with additional constraints and options:** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* addColumn call.* </p>*** @example* //Outside of a table* //ALTER TABLE test ADD COLUMN name text UNIQUE'* DB.addColumn("test", "name", "text", {unique : true});** @param {String} table the table to add the column to.* @param {String} column the name of the column to add.* @param type datatype of the column* @param {Object} [opts] additional options that can be used when adding a column.* @param {Boolean} [opts.primaryKey] set to true if this column is a primary key.* @param {Boolean} [opts.allowNull] whether or not this column should allow null.* @param {Boolean} [opts.unique] set to true to add a UNIQUE constraint to a column,** @return {Promise} a promise that is resolved when the ADD COLUMN action is complete.**/addColumn: function (table, column, type, opts) {- 9
var args = argsToArray(arguments).slice(1);- 9
return this.alterTable(table, function () {- 9
this.addColumn.apply(this, args);});},/*** Adds an index to a table for the given columns** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* addIndex call.* </p>* @example* DB.addIndex("test", "name", {unique : true});* //=> 'CREATE UNIQUE INDEX test_name_index ON test (name)'* DB.addIndex("test", ["one", "two"]);* //=> ''CREATE INDEX test_one_two_index ON test (one, two)''** @param {String} table the table to add the index to.* @param {String|String[]} columns the name of the column/s to create an index for.* @param {Object} [options] additional options that can be used when adding an index.* @param {Boolean} [options.unique] set to true if this this index should have a UNIQUE constraint.* @param {Boolean} [options.ignoreErrors] set to true to ignore errors.** @return {Promise} a promise that is resolved when the CREATE INDEX action is complete.* */addIndex: function (table, columns, options) {- 4
options = options || {};- 4
var ignoreErrors = options.ignoreErrors === true;- 4
return this.alterTable(table, function () {- 4
this.addIndex(columns, options);}).chain(function (res) {- 4
return res;}, function (err) {- 0
if (!ignoreErrors) {- 0
throw err;}});},/*** Removes a column from the specified table.* <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* dropColumn call.* </p>** @example* DB.dropColumn("items", "category");* //=> 'ALTER TABLE items DROP COLUMN category',** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the column to drop.** @return {Promise} a promise that is resolved once the DROP COLUMN action is complete.* */dropColumn: function (table, column) {- 3
column = argsToArray(arguments).slice(1);- 3
return this.alterTable(table, function () {- 3
this.dropColumn.apply(this, column);});},/*** Removes an index for the given table and column/s.** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* dropIndex call.* </p>** @example* DB.dropIndex("posts", "title");* //=>'DROP INDEX posts_title_index* DB.dropIndex("posts", ["author", "title"]);* //'DROP INDEX posts_author_title_index'** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the name of the column/s the index was created from.** @return {Promise} a promise that is resolved once the DROP INDEX action is complete.* */dropIndex: function (table, columns, options) {- 1
var args = argsToArray(arguments).slice(1);- 1
return this.alterTable(table, function () {- 1
this.dropIndex.apply(this, args);});},/*** Renames a column in the specified table.** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* renameColumn call.* </p>** @example* DB.renameColumn("items", "cntr", "counter");* //=> ALTER TABLE items RENAME COLUMN cntr TO counter** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the name of the column to rename.* @param {String|patio.sql.Identifier} newColumn the new name of the column.** @return {Promise} a promise that is resolved once the RENAME COLUMN action is complete.* */renameColumn: function (table, column, newColumn) {- 4
var args = argsToArray(arguments).slice(1);- 4
return this.alterTable(table, function () {- 4
this.renameColumn.apply(this, args);});},/***Sets the default value for the given column in the given table:** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* setColumnDefault call.* </p>** @example* DB.setColumnDefault("items", "category", "misc");* //=> ALTER TABLE items ALTER COLUMN category SET DEFAULT 'misc'** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the name of the column to set the DEFAULT on.* @param def the new default value of the column.** @return {Promise} a promise that is resolved once the SET DEFAULT action is complete.* */setColumnDefault: function (table, column, def) {- 1
var args = argsToArray(arguments).slice(1);- 1
return this.alterTable(table, function () {- 1
this.setColumnDefault.apply(this, args);});},/*** Set the data type for the given column in the given table:* <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* setColumnType call.* </p>** @example* DB.setColumnType("items", "category", String);* //=> ALTER TABLE items ALTER COLUMN category TYPE varchar(255)** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the name of the column to set the TYPE on.* @param type the datatype of the column.** @return {Promise} a promise that is resolved once the SET TYPE action is complete.* */setColumnType: function (table, column, type) {- 3
var args = argsToArray(arguments).slice(1);- 3
return this.alterTable(table, function () {- 3
this.setColumnType.apply(this, args);});},/*** Alters the given table with the specified block.* <p>* <b>NOTE:</b> The block is invoked in the scope of the table that is being altered. The block* is also called with the table as the first argument. Within the block you must use* <b>this</b>(If the block has not been bound to a different scope), or the table object* that is passed in for all alter table operations. See {@link patio.AlterTableGenerator} for* avaiable operations.* </p>** <p>* <b>Note</b> that addColumn accepts all the options available for column* definitions using createTable, and addIndex accepts all the options* available for index definition.* </p>** @example* //using the table object* DB.alterTable("items", function(table){* //you must use the passed in table object.* table.addColumn("category", "text", {default : 'javascript'});* table.dropColumn("category");* table.renameColumn("cntr", "counter");* table.setColumnType("value", "float");* table.setColumnDefault("value", "float");* table.addIndex(["group", "category"]);* table.dropIndex(["group", "category"]);* });** //using this* DB.alterTable("items", function(){* this.addColumn("category", "text", {default : 'javascript'});* this.dropColumn("category");* this.renameColumn("cntr", "counter");* this.setColumnType("value", "float");* this.setColumnDefault("value", "float");* this.addIndex(["group", "category"]);* this.dropIndex(["group", "category"]);* });** //This will not work* DB.alterTable("items", comb.hitch(someObject, function(){* //This is called in the scope of someObject so this* //will not work and will throw an error* this.addColumn("category", "text", {default : 'javascript'});* }));** //This will work* DB.alterTable("items", comb.hitch(someObject, function(table){* //This is called in the scope of someObject so you must* //use the table argument* table.category("text", {default : 'javascript'});* }));*** @param {String|patio.sql.Identifier} table to the table to perform the ALTER TABLE operations on.* @param {Function} block the block to invoke for the ALTER TABLE operations** @return {Promise} a promise that is resolved once all ALTER TABLE operations have completed.* */alterTable: function (name, generator, block) {- 84
if (isFunction(generator)) {- 84
block = generator;- 84
generator = new AlterTableGenerator(this, block);}- 84
var self = this;- 84
return this.__alterTableSqlList(name, generator.operations).chain(function (res) {- 84
return asyncArray(comb(res).pluck("1").flatten()).forEach(function (sql) {- 97
return self.executeDdl(sql);}).chain(function () {- 84
return self.removeCachedSchema(name);});});},/*** Creates a table with the columns given in the provided block:** <p>* <b>NOTE:</b> The block is invoked in the scope of the table that is being created. The block* is also called with the table as the first argument. Within the block you must use* <b>this</b>(If the block has not been bound to a different scope), or the table object* that is passed in for all create table operations. See {@link patio.SchemaGenerator} for* available operations.* </p>*** @example** //using the table to create the table* DB.createTable("posts", function(table){* table.primaryKey("id");* table.column('title", "text");* //you may also invoke the column name as* //function on the table* table.content(String);* table.index(title);* });** //using this to create the table* DB.createTable("posts", function(){* this.primaryKey("id");* this.column('title", "text");* //you may also invoke the column name as* //function on the table* this.content(String);* this.index(title);* });** @param {String|patio.sql.Identifier} name the name of the table to create.* @param {Object} [options] an optional options object* @param {Boolean} [options.temp] set to true if this table is a TEMPORARY table.* @param {Boolean} [options.ignoreIndexErrors] Ignore any errors when creating indexes.* @param {Function} block the block to invoke when creating the table.** @return {Promise} a promise that is resolved when the CREATE TABLE action is completed.**/createTable: function (name, options, block) {- 213
if (isFunction(options)) {- 202
block = options;- 202
options = {};}- 213
this.removeCachedSchema(name);- 213
if (isInstanceOf(options, SchemaGenerator)) {- 0
options = {generator: options};}- 213
var generator = options.generator || new SchemaGenerator(this, block), self = this;- 213
return this.__createTableFromGenerator(name, generator, options).chain(function () {- 213
return self.__createTableIndexesFromGenerator(name, generator, options);});},/*** Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.* <p>* See {@link patio.Database#createTable} for parameter types.* </p>** @example* // DROP TABLE a* // CREATE TABLE a (a integer)* DB.forceCreateTable("a", function(){* this.a("integer");* });***/forceCreateTable: function (name, options, block) {- 21
var self = this;- 21
return this.dropTable(name).chainBoth(function () {- 21
return self.createTable(name, options, block);});},/*** Creates the table unless the table already exists.* <p>* See {@link patio.Database#createTable} for parameter types.* </p>*/createTableUnlessExists: function (name, options, block) {- 0
var self = this;- 0
return this.tableExists(name).chain(function (exists) {- 0
if (!exists) {- 0
return self.createTable(name, options, block);}});},/*** Creates a view, replacing it if it already exists:* @example* DB.createOrReplaceView("cheapItems", "SELECT * FROM items WHERE price < 100");* //=> CREATE OR REPLACE VIEW cheapItems AS SELECT * FROM items WHERE price < 100* DB.createOrReplaceView("miscItems", DB.from("items").filter({category : 'misc'}));* //=> CREATE OR REPLACE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'** @param {String|patio.sql.Identifier} name the name of the view to create.* @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the* view.** @return {Promise} a promise that is resolved when the CREATE OR REPLACE VIEW action is complete.**/createOrReplaceView: function (name, source, opts) {- 4
if (isInstanceOf(source, Dataset)) {- 2
source = source.sql;}- 4
opts = opts || {};- 4
opts.replace = true;- 4
var self = this;- 4
return this.executeDdl(this.__createViewSql(name, source, opts)).chain(function () {- 4
return self.removeCachedSchema(name);});},/*** Creates a view based on a dataset or an SQL string:* @example* DB.createView("cheapItems", "SELECT * FROM items WHERE price < 100");* //=> CREATE VIEW cheapItems AS SELECT * FROM items WHERE price < 100* DB.createView("miscItems", DB.from("items").filter({category : 'misc'}));* //=> CREATE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'** @param {String|patio.sql.Identifier} name the name of the view to create.* @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the* view.**/createView: function (name, source, opts) {- 5
if (isInstanceOf(source, Dataset)) {- 3
source = source.sql;}- 5
return this.executeDdl(this.__createViewSql(name, source, opts));},/*** Drops one or more tables corresponding to the given names.** @example* DB.dropTable("test");* //=>'DROP TABLE test'* DB.dropTable("a", "bb", "ccc");* //=>'DROP TABLE a',* //=>'DROP TABLE bb',* //=>'DROP TABLE ccc'** @param {String|String[]|patio.sql.Identifier|patio.sql.Identifier[]} names the names of the tables* to drop.** @return {Promise} a promise that is resolved once all tables have been dropped.**/dropTable: function (names) {- 48
if (!isArray(names)) {- 33
names = comb(arguments).toArray();}- 48
names = names.filter(function (t) {- 65
return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);});- 48
var self = this;- 48
return asyncArray(names).forEach(function (name) {- 65
return self.executeDdl(self.__dropTableSql(name)).chain(function () {- 55
return self.removeCachedSchema(name);});}, 1);},/*** Forcible drops one or more tables corresponding to the given names, ignoring errors.** @example* DB.dropTable("test");* //=>'DROP TABLE test'* DB.dropTable("a", "bb", "ccc");* //=>'DROP TABLE a',* //=>'DROP TABLE bb',* //=>'DROP TABLE ccc'** @param {String|String[]|patio.sql.Identifier|patio.sql.Identifier[]} names the names of the tables* to drop.** @return {Promise} a promise that is resolved once all tables have been dropped.**/forceDropTable: function (names) {- 94
if (!isArray(names)) {- 68
names = comb(arguments).toArray();}- 94
names = names.filter(function (t) {- 133
return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);});- 94
var l = names.length, ret = new Promise(), self = this;- 94
var drop = function (i) {- 227
if (i < l) {- 133
var name = names[i++];- 133
self.executeDdl(self.__dropTableSql(name)).both(function () {- 133
self.removeCachedSchema(name);- 133
drop(i);});} else {- 94
ret.callback();}};- 94
drop(0);- 94
return ret.promise();},/*** Drops one or more views corresponding to the given names.** @example* DB.dropView("test_view");* //=>'DROP VIEW test_view'* DB.dropTable("test_view_1", "test_view_2", "test_view_3");* //=>'DROP VIEW test_view_1',* //=>'DROP VIEW test_view_2',* //=>'DROP VIEW test_view_3'** @param {String|String[]|patio.sql.Identifier|patio.sql.Identifier[]} names the names of the views* to drop.* @param {Hash} [opts={}] Additional options that very based on the database adapter.** @return {Promise} a promise that is resolved once the view/s have been dropped.**/dropView: function (names, opts) {- 9
if (isArray(names)) {- 5
opts = opts || {};- 5
var self = this;- 5
return asyncArray(names).forEach(function (name) {- 5
return self.executeDdl(self.__dropViewSql(name, opts)).chain(function () {- 5
self.removeCachedSchema(name);});}, null, 1).chain(function () {- 5
return null;});} else {- 4
var args = argsToArray(arguments);- 4
if (comb.isHash(args[args.length - 1])) {- 0
opts = args.pop();}- 4
return this.dropView(args.filter(function (t) {- 4
return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);}), opts);}},/*** Renames a table.** @example* comb.executeInOrder(DB, function(DB){* DB.tables(); //=> ["items"]* DB.renameTable("items", "old_items");* //=>'ALTER TABLE items RENAME TO old_items'* DB.tables; //=> ["old_items"]*});** @param {String|patio.sql.Identifier} name the name of the table to rename* @param {String|patio.sql.Identifier} newName the new name of the table* @return {Promise} a promise that is resolved once the table is renamed.**/renameTable: function (name, newName) {- 2
var self = this;- 2
return this.executeDdl(this.__renameTableSql(name, newName)).chain(function () {- 2
self.removeCachedSchema(name);}).promise();},/*** @private* The SQL to execute to modify the DDL for the given table name. op* should be one of the operations returned by the AlterTableGenerator.* */__alterTableSql: function (table, op) {- 85
var ret = new Promise();- 85
var quotedName = op.name ? this.__quoteIdentifier(op.name) : null;- 85
var alterTableOp = null;- 85
switch (op.op) {case "addColumn":- 13
alterTableOp = format("ADD COLUMN %s", this.__columnDefinitionSql(op));- 13
break;case "dropColumn":- 4
alterTableOp = format("DROP COLUMN %s", quotedName);- 4
break;case "renameColumn":- 52
alterTableOp = format("RENAME COLUMN %s TO %s", quotedName, this.__quoteIdentifier(op.newName));- 52
break;case "setColumnType":- 3
alterTableOp = format("ALTER COLUMN %s TYPE %s", quotedName, this.typeLiteral(op));- 3
break;case "setColumnDefault":- 2
alterTableOp = format("ALTER COLUMN %s SET DEFAULT %s", quotedName, this.literal(op["default"]));- 2
break;case "setColumnNull":- 0
alterTableOp = format("ALTER COLUMN %s %s NOT NULL", quotedName, op["null"] ? "DROP" : "SET");- 0
break;case "addIndex":- 5
return ret.callback(this.__indexDefinitionSql(table, op)).promise();case "dropIndex":- 2
return ret.callback(this.__dropIndexSql(table, op)).promise();case "addConstraint":- 3
alterTableOp = format("ADD %s", this.__constraintDefinitionSql(op));- 3
break;case "dropConstraint":- 0
alterTableOp = format("DROP CONSTRAINT %s", quotedName);- 0
break;default :- 1
throw new DatabaseError("Invalid altertable operator");}- 77
return ret.callback(format("ALTER TABLE %s %s", this.__quoteSchemaTable(table), alterTableOp)).promise();},/*** @private** Creates the DROP VIEW SQL fragment.*/__dropViewSql: function (name) {- 4
return format("DROP VIEW %s", this.__quoteSchemaTable(name));},/*** @private** Creates a view sql*/__createViewSql: function (name, source, opts) {- 8
var sql = "CREATE";- 8
opts = opts || {};- 8
if (opts.replace) {- 4
sql += " OR REPLACE";}- 8
sql += " VIEW %s AS %s";- 8
return format(sql, this.__quoteSchemaTable(name), source);},/*** @private* Array of SQL DDL modification statements for the given table,* corresponding to the DDL changes specified by the operations.* */__alterTableSqlList: function (table, operations) {- 84
var self = this;- 84
return new PromiseList(operations.map(function (operation) {- 95
return self.__alterTableSql(table, operation);}));},/*** @private* SQL DDL fragment containing the column creation SQL for the given column.** @param column*/__columnDefinitionSql: function (column) {- 619
var sql = [format("%s %s", this.__quoteIdentifier(column.name), this.typeLiteral(column))];- 619
column.unique && sql.push(this._static.UNIQUE);- 619
(column.allowNull === false || column["null"] === false) && sql.push(this._static.NOT_NULL);- 619
(column.allowNull === true || column["null"] === true) && sql.push(this._static.NULL);- 619
!isUndefined(column["default"]) && sql.push(format(" DEFAULT %s", this.literal(column["default"])));- 619
column.primaryKey && sql.push(this._static.PRIMARY_KEY);- 619
column.autoIncrement && sql.push(" " + this.autoIncrementSql);- 619
column.table && sql.push(this.__columnReferencesColumnConstraintSql(column));- 619
return sql.join("");},/*** @private* SQL DDL fragment containing the column creation* SQL for all given columns, used inside a CREATE TABLE block.*/__columnListSql: function (generator) {- 213
var self = this;- 213
return generator.columns.map(function (column) {- 595
return self.__columnDefinitionSql(column);}).concat(generator.constraints.map(function (constraint) {- 7
return self.__constraintDefinitionSql(constraint);})).join(this._static.COMMA_SEPARATOR);},/*** @private*SQL DDL fragment for column foreign key references (column constraints)*/__columnReferencesColumnConstraintSql: function (column) {- 28
return this.__columnReferencesSql(column);},/*** @private* SQL DDL fragment for column foreign key references*/__columnReferencesSql: function (column) {- 36
var sql = format(" REFERENCES %s", this.__quoteSchemaTable(column.table));- 36
column.key && (sql += format("(%s)", array.toArray(column.key).map(this.__quoteIdentifier, this).join(this._static.COMMA_SEPARATOR)));- 36
column.onDelete && (sql += format(" ON DELETE %s", this.__onDeleteClause(column.onDelete)));- 36
column.onUpdate && (sql += format(" ON UPDATE %s", this.__onUpdateClause(column.onUpdate)));- 36
column.deferrable && (sql += " DEFERRABLE INITIALLY DEFERRED");- 36
return sql;},/*** @private* SQL DDL fragment for table foreign key references (table constraints)* */__columnReferencesTableConstraintSql: function (constraint) {- 6
return format("FOREIGN KEY %s%s", this.literal(constraint.columns.map(function (c) {- 6
return isString(c) ? sql.stringToIdentifier(c) : c;})), this.__columnReferencesSql(constraint));},/*** @private* SQL DDL fragment specifying a constraint on a table.*/__constraintDefinitionSql: function (constraint) {- 10
var ret = [constraint.name ? format("CONSTRAINT %s ", this.__quoteIdentifier(constraint.name)) : ""];- 10
switch (constraint.type) {case "check":- 4
var check = constraint.check;- 4
ret.push(format("CHECK %s", this.__filterExpr(isArray(check) && check.length === 1 ? check[0] : check)));- 4
break;case "primaryKey":- 0
ret.push(format("PRIMARY KEY %s", this.literal(constraint.columns.map(function (c) {- 0
return isString(c) ? sql.stringToIdentifier(c) : c;}))));- 0
break;case "foreignKey":- 6
ret.push(this.__columnReferencesTableConstraintSql(constraint));- 6
break;case "unique":- 0
ret.push(format("UNIQUE %s", this.literal(constraint.columns.map(function (c) {- 0
return isString(c) ? sql.stringToIdentifier(c) : c;}))));- 0
break;default:- 0
throw new DatabaseError(format("Invalid constriant type %s, should be 'check', 'primaryKey', foreignKey', or 'unique'", constraint.type));}- 10
return ret.join("");},/*** @private* Execute the create table statements using the generator.* */__createTableFromGenerator: function (name, generator, options) {- 213
return this.executeDdl(this.__createTableSql(name, generator, options));},/*** @private* Execute the create index statements using the generator.* */__createTableIndexesFromGenerator: function (name, generator, options) {- 213
var e = options.ignoreIndexErrors;- 213
var ret;- 213
var promises = generator.indexes.map(function (index) {- 18
var ps = this.__indexSqlList(name, [index]).map(this.executeDdl, this);- 18
return new PromiseList(ps);}, this);- 213
if (promises.length) {- 16
ret = new PromiseList(promises).chain(function (res) {- 16
return res;}, function (err) {- 0
if (!e) {- 0
throw err;}});} else {- 197
ret = new Promise().callback();}- 213
return ret.promise();},/*** @private* DDL statement for creating a table with the given name, columns, and options* */__createTableSql: function (name, generator, options) {- 213
return format("CREATE %sTABLE %s (%s)", options.temp ? this.temporaryTableSql : "", this.__quoteSchemaTable(name), this.__columnListSql(generator));},/*** @private* Default index name for the table and columns, may be too long* for certain databases.*/__defaultIndexName: function (tableName, columns) {- 25
var parts = this.__schemaAndTable(tableName);- 25
var schema = parts[0], table = parts[1];- 25
var index = [];- 25
if (schema && schema !== this.defaultSchema) {- 0
index.push(schema);}- 25
index.push(table);- 25
index = index.concat(columns.map(function (c) {- 28
return isString(c) ? c : this.literal(c).replace(/\W/g, "");}, this));- 25
index.push("index");- 25
return index.join(this._static.UNDERSCORE);},/*** @private* The SQL to drop an index for the table.* */__dropIndexSql: function (table, op) {- 2
return format("DROP INDEX %s", this.__quoteIdentifier(op.name || this.__defaultIndexName(table, op.columns)));},/*** @private** SQL DDL statement to drop the table with the given name.**/__dropTableSql: function (name) {- 198
return format("DROP TABLE %s", this.__quoteSchemaTable(name));},/*** @private* Proxy the filterExpr call to the dataset, used for creating constraints.* */__filterExpr: function (args, block) {- 6
var ds = this.__schemaUtiltyDataset;- 6
return ds.literal(ds._filterExpr.apply(ds, arguments));},/*** @private* SQL DDL statement for creating an index for the table with the given name* and index specifications.*/__indexDefinitionSql: function (tableName, index) {- 7
var indexName = index.name || this.__defaultIndexName(tableName, index.columns);- 7
if (index.type) {- 0
throw new DatabaseError("Index types are not supported for this database");- 7
} else if (index.where) {- 0
throw new DatabaseError("Partial indexes are not supported for this database");} else {- 7
return format("CREATE %sINDEX %s ON %s %s", index.unique ? "UNIQUE " : "", this.__quoteIdentifier(indexName), this.__quoteSchemaTable(tableName), this.literal(index.columns.map(function (c) {- 8
return isString(c) ? new Identifier(c) : c;})));}},/*** Array of SQL DDL statements, one for each index specification,* for the given table.*/__indexSqlList: function (tableName, indexes) {- 18
var self = this;- 18
return indexes.map(function (index) {- 18
return self.__indexDefinitionSql(tableName, index);});},/*** @private* SQL DDL ON DELETE fragment to use, based on the given action.*The following actions are recognized:* <ul>* <li>cascade - Delete rows referencing this row.</li>* <li>noAction (default) - Raise an error if other rows reference this* row, allow deferring of the integrity check.* </li>* <li>restrict - Raise an error if other rows reference this row,* but do not allow deferring the integrity check.</li>* <li> setDefault - Set columns referencing this row to their default value.</li>* <li>setNull - Set columns referencing this row to NULL.</li>* </ul>*/__onDeleteClause: function (action) {- 21
return this._static[action.toUpperCase()] || this._static.NO_ACTION;},/*** @private* SQL DDL ON UPDATE fragment to use, based on the given action.*The following actions are recognized:* <ul>* <li>cascade - Delete rows referencing this row.</li>* <li>noAction (default) - Raise an error if other rows reference this* row, allow deferring of the integrity check.* </li>* <li>restrict - Raise an error if other rows reference this row,* but do not allow deferring the integrity check.</li>* <li> setDefault - Set columns referencing this row to their default value.</li>* <li>setNull - Set columns referencing this row to NULL.</li>* </ul>*/__onUpdateClause: function (action) {- 1
return this._static[action.toUpperCase()] || this._static.NO_ACTION;},/*** @private* Proxy the quoteSchemaTable method to the dataset* */__quoteSchemaTable: function (table) {- 1814
return this.__schemaUtiltyDataset.quoteSchemaTable(table);},/*** @private* Proxy the quoteIdentifier method to the dataset, used for quoting tables and columns.* */__quoteIdentifier: function (v) {- 824
return this.__schemaUtiltyDataset.quoteIdentifier(v);},/*** @private* SQL DDL statement for renaming a table.* */__renameTableSql: function (name, newName) {- 1
return format("ALTER TABLE %s RENAME TO %s", this.__quoteSchemaTable(name), this.__quoteSchemaTable(newName));},/*** @private* Remove the cached schemaUtilityDataset, because the identifier* quoting has changed.*/__resetSchemaUtilityDataset: function () {- 0
this.__schemaUtiltyDs = null;},/*** @private* Split the schema information from the table* */__schemaAndTable: function (tableName) {- 195
return this.__schemaUtiltyDataset.schemaAndTable(tableName);},/*** @private* Return true if the given column schema represents an autoincrementing primary key.**/_schemaAutoincrementingPrimaryKey: function (schema) {- 0
return !!schema.primaryKey;},/*** @private* SQL fragment specifying the type of a given column.* */typeLiteral: function (column) {- 667
return this.__typeLiteralGeneric(column);},/*** @private* SQL fragment specifying the full type of a column,* consider the type with possible modifiers.*/__typeLiteralGeneric: function (column) {- 667
var type = column.type;- 667
var meth = "__typeLiteralGeneric";- 667
var isStr = isString(type);- 667
var proper = isStr ? type.charAt(0).toUpperCase() + type.substr(1) : null;- 667
if (type === String || (isStr && type.match(/string/i))) {- 183
meth += "String";- 484
} else if ((isStr && type.match(/number/i)) || type === Number) {- 12
meth += "Numeric";- 472
} else if ((isStr && type.match(/datetime/i)) || type === DateTime) {- 8
meth += "DateTime";- 464
} else if ((isStr && type.match(/date/i)) || type === Date) {- 11
meth += "Date";- 453
} else if ((isStr && type.match(/year/i)) || type === Year) {- 2
meth += "Year";- 451
} else if ((isStr && type.match(/timestamp/i)) || type === TimeStamp) {- 4
meth += "Timestamp";- 447
} else if ((isStr && type.match(/time/i)) || type === Time) {- 3
meth += "Time";- 444
} else if ((isStr && type.match(/decimal/i)) || type === Decimal) {- 2
meth += "Decimal";- 442
} else if ((isStr && type.match(/float/i)) || type === Float) {- 15
meth += "Float";- 427
} else if ((isStr && type.match(/boolean/i)) || type === Boolean) {- 5
meth += "Boolean";- 422
} else if ((isStr && type.match(/buffer/i)) || type === Buffer) {- 25
meth += "Blob";- 397
} else if ((isStr && type.match(/json/i)) || type === Json) {- 11
meth += "Json";- 386
} else if (isStr && isFunction(this[meth + proper])) {- 133
meth += proper;} else {- 253
return this.__typeLiteralSpecific(column);}- 414
return this[meth](column);},/*** @private* patio uses the date type by default for Dates.* <ul>* <li>if onlyTime is present then time is used</li>* <li>if timeStamp is present then timestamp is used,</li>* <li>if dateTime is present then datetime is used</li>* <li>if yearOnly is present then year is used</li>* <li>else date is used</li>* </ul>*/__typeLiteralGenericDate: function (column) {- 11
var type = column.type, ret = "date";- 11
if (column.onlyTime) {- 2
ret = "time";- 9
} else if (column.timeStamp) {- 2
ret = "timestamp";- 7
} else if (column.dateTime) {- 2
ret = "datetime";- 5
} else if (column.yearOnly) {- 2
ret = "year";}- 11
return ret;},/*** @private* * patio uses the blob type by default for Buffers.*/__typeLiteralGenericBlob: function (column) {- 21
return "blob";},/*** @private* * patio uses the year type by default for {@link patio.sql.DateTime}.*/__typeLiteralGenericDateTime: function (column) {- 2
return "datetime";},/*** @private* patio uses the timestamp type by default for {@link patio.sql.TimeStamp}.*/__typeLiteralGenericTimestamp: function (column) {- 4
return "timestamp";},/*** @private* patio uses the time type by default for {@link patio.sql.Time}.*/__typeLiteralGenericTime: function (column) {- 3
return "time";},/*** @private* patio uses the year type by default for {@link patio.sql.Year}.*/__typeLiteralGenericYear: function (column) {- 2
return "year";},/*** @private* patio uses the boolean type by default for Boolean class* */__typeLiteralGenericBoolean: function (column) {- 3
return "boolean";},/*** @private* patio uses the numeric type by default for NumericTypes* If a size is given, it is used, otherwise, it will default to whatever* the database default is for an unsized value.* <ul>* <li> if isInt is present the int is used</li>* <li> if isDouble is present then double precision is used</li>* </ul>*/__typeLiteralGenericNumeric: function (column) {- 10
return column.size ? format("numeric(%s)", array.toArray(column.size).join(', ')) : column.isInt ? "integer" : column.isDouble ? "double precision" : "numeric";},/*** @private*/__typeLiteralGenericFloat: function (column) {- 15
return "double precision";},/*** @private*/__typeLiteralGenericDecimal: function (column) {- 2
return "double precision";},/*** @private*/__typeLiteralGenericJson: function (column) {- 11
return "json";},/*** @private* patio uses the varchar type by default for Strings. If a* size isn't present, patio assumes a size of 255. If the* fixed option is used, patio uses the char type. If the* text option is used, patio uses the `text` type.*/__typeLiteralGenericString: function (column) {- 40
return column.text ? "text" : format("%s(%s)", column.fixed ? "char" : "varchar", column.size || 255);},/*** @private* SQL fragment for the given type of a column if the column is not one of the* generic types specified with a native javascript type class.*/__typeLiteralSpecific: function (column) {- 320
var type = column.type;- 320
type = type === "double" ? "double precision" : type;- 320
if (type === "varchar") {- 7
column.size = isNumber(column.size) ? column.size : 255;}- 320
var elements = column.size || column.elements;- 320
return format("%s%s%s", type, elements ? this.literal(toArray(elements)) : "", column.unsigned ? " UNSIGNED" : "");},/**@ignore*/getters: {/**@lends patio.Database.prototype*//*** @private* The SQL string specify the autoincrement property, generally used by* primary keys.** @field* */autoIncrementSql: function () {- 10
return this._static.AUTOINCREMENT;},/*** @private* @field* */temporaryTableSql: function () {- 3
return this._static.TEMPORARY;},/*** @private* @field* */__schemaUtiltyDataset: function () {- 2839
this.__schemaUtiltyDs = this.__schemaUtiltyDs || this.dataset;- 2839
return this.__schemaUtiltyDs;}}},"static": {/**@lends patio.Database*//***Default AUTO INCREMENT SQL*/AUTOINCREMENT: 'AUTOINCREMENT',/***Default CASCACDE SQL*/CASCADE: 'CASCADE',/***Default comma*/COMMA_SEPARATOR: ', ',/***Default NO ACTION SQL*/NO_ACTION: 'NO ACTION',/***Default NOT NULL SQL*/NOT_NULL: ' NOT NULL',/*** Default NULL SQL*/NULL: ' NULL',/***Default PRIMARY KEY SQL*/PRIMARY_KEY: ' PRIMARY KEY',/***Default RESTRICT SQL*/RESTRICT: 'RESTRICT',/***Default SET DEFAULT SQL*/SET_DEFAULT: 'SET DEFAULT',/***Default SET NULL SQL*/SET_NULL: 'SET NULL',/***Default TEMPORARY SQL*/TEMPORARY: 'TEMPORARY ',/***Default UNDERSCORE SQL, used in index creation.*/UNDERSCORE: '_',/***Default UNIQUE SQL*/UNIQUE: ' UNIQUE',/*** Default UNSIGNED SQL*/UNSIGNED: ' UNSIGNED'}}).as(module);
|
sql.js
|
Coverage90.84
SLOC2825
LOC491
Missed45
|
- 1
var comb = require("comb-proxy"),array = comb.array,flatten = array.flatten,ExpressionError = require("./errors").ExpressionError,methodMissing = comb.methodMissing,createFunctionWrapper = comb.createFunctionWrapper,isUndefined = comb.isUndefined,isUndefinedOrNull = comb.isUndefinedOrNull,isNull = comb.isNull,isInstanceOf = comb.isInstanceOf,argsToArray = comb.argsToArray,isDate = comb.isDate,isHash = comb.isHash,merge = comb.merge,isArray = comb.isArray,toArray = array.toArray,format = comb.string.format,isBoolean = comb.isBoolean,isNumber = comb.isNumber,isObject = comb.isObject,isString = comb.isString,define = comb.define,isRegExp = comb.isRegExp,Dataset, patio, sql, Expression, AliasedExpression, CaseExpression, Cast, ColumnAll, BooleanExpression, JsonArray,BooleanConstant, NegativeBooleanConstant, Identifier, PlaceHolderLiteralString, SQLFunction, OrderedExpression,NumericExpression, QualifiedIdentifier, StringExpression, SubScript, LiteralString, Json;- 1
var virtualRow = function (name) {- 1253
var DOUBLE_UNDERSCORE = '__';- 1253
var parts = name.split(DOUBLE_UNDERSCORE);- 1253
var table = parts[0], column = parts[1];- 1253
var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);- 1253
var prox = methodMissing(ident, function (m) {- 3
return function () {- 3
var args = argsToArray(arguments);- 3
return SQLFunction.fromArgs([m, name].concat(args));};}, column ? QualifiedIdentifier : Identifier);- 1253
var ret = createFunctionWrapper(prox, function (m) {- 663
var args = argsToArray(arguments);- 663
if (args.length) {- 657
return SQLFunction.fromArgs([name].concat(args));} else {- 6
return prox;}}, function () {- 0
return SQLFunction.fromArgs(arguments);});- 1253
ret["__proto__"] = ident;- 1253
return ret;};- 1
var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds","getTime", "getTimezoneOffset", "getUTCDate", "getUTCDay", "getUTCFullYear", "getUTCHours", "getUTCMilliseconds","getUTCMinutes", "getUTCMonth", "getUTCSeconds", "getYear", "parse", "setDate", "setFullYear", "setHours", "setMilliseconds","setMinutes", "setMonth", "setSeconds", "setTime", "setUTCDate", "setUTCFullYear", "setUTCHours", "setUTCMilliseconds","setUTCMinutes", "setUTCMonth", "setUTCSeconds", "setYear", "toDateString", "toGMTString", "toLocaleDateString","toLocaleTimeString", "toLocaleString", "toTimeString", "toUTCString", "UTC", "valueOf"];- 1
var addDateMethod = function (op) {- 180
return function () {- 8
return this.date[op].apply(this.date, arguments);};};/*** @constructor* Creates a Year type to be used in queries that require a SQL year datatype.* All <i>Date</i> methods ar included in the prototype of the Year type. toString and toJSON* are overridden to return a year format instead of the default <i>Date</i> formatting.* See {@link patioTime#yearToString} for formatting information.** @example** var year = patio.Year(2009); //=> 2009* JSON.stringify(year)l //=> 2009** @memberOf patio.sql* @param {Number} y the year this year represents.*/- 1
var Year = function (y) {- 10
this.date = isUndefined(y) ? new Date() : isDate(y) ? y : new Date(y, 0, 1, 0, 0, 0);};- 1
Year.prototype.toJSON = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
Year.prototype.toString = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DATE_METHODS.forEach(function (op) {- 45
Year.prototype[op] = addDateMethod(op);}, this);/*** @constructor* Creates a Time type to be used in queries that require a SQL time datatype.* All <i>Date</i> methods ar included in the prototype of the Time type. toString and toJSON* are overridden to return a time format instead of the default <i>Date</i> formatting.* See {@link patioTime#timeToString} for formatting information.** @example** var time = patio.Time(12, 12, 12); //=> 12:12:12* JSON.stringify(time); //=> 12:12:12** @memberOf patio.sql* @param {Number} [h=0] the hour* @param {Number} [min=0] the minute/s* @param {Number} [s=0] the second/s* @param {Number} [ms=0] the millisecond/s, this paramater is not be used, but may depending on the adapter.*/- 1
var Time = function (h, min, s, ms) {- 17
var args = argsToArray(arguments);- 17
if (args.length === 0) {- 0
this.date = new Date();- 17
} else if (isDate(h)) {- 11
this.date = h;} else {- 6
var date = new Date(1970, 0, 1, 0, 0, 0);- 6
isNumber(h) && date.setHours(h);- 6
isNumber(min) && date.setMinutes(min);- 6
isNumber(s) && date.setSeconds(s);- 6
isNumber(ms) && date.setMilliseconds(ms);- 6
this.date = date;}};- 1
Time.prototype.toJSON = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
Time.prototype.toString = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DATE_METHODS.forEach(function (op) {- 45
Time.prototype[op] = addDateMethod(op);}, this);/*** @constructor* Creates a TimeStamp type to be used in queries that require a SQL timestamp datatype.* All <i>Date</i> methods ar included in the prototype of the TimeStamp type. toString and toJSON* are overridden to return a ISO8601 format instead of the default <i>Date</i> formatting.* See {@link patioTime#timeStampToString} for formatting information.** @example** var timeStamp = patio.TimeStamp(2009, 10, 10, 10, 10, 10); //=> '2009-11-10 10:10:10'* JSON.stringify(timeStamp); //=> '2009-11-10 10:10:10'** @memberOf patio.sql* @param {Number} [y=1970] the year* @param {Number} [m=0] the month* @param {Number} [d=1] the day* @param {Number} [h=0] the hour* @param {Number} [min=0] the minute/s* @param {Number} [s=0] the second/s* @param {Number} [ms=0] the millisecond/s, this paramater is not be used, but may depending on the adapter.*/- 1
var TimeStamp = function (y, m, d, h, min, s, ms) {- 54
var args = argsToArray(arguments);- 54
if (args.length === 0) {- 1
this.date = new Date();- 53
} else if (isDate(y)) {- 49
this.date = y;} else {- 4
var date = new Date(1970, 0, 1, 0, 0, 0);- 4
isNumber(y) && date.setYear(y);- 4
isNumber(m) && date.setMonth(m);- 4
isNumber(d) && date.setDate(d);- 4
isNumber(h) && date.setHours(h);- 4
isNumber(min) && date.setMinutes(min);- 4
isNumber(s) && date.setSeconds(s);- 4
isNumber(ms) && date.setMilliseconds(ms);- 4
this.date = date;}};- 1
TimeStamp.prototype.toJSON = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
TimeStamp.prototype.toString = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DATE_METHODS.forEach(function (op) {- 45
TimeStamp.prototype[op] = addDateMethod(op);}, this);/*** @constructor* Creates a DateTime type to be used in queries that require a SQL datetime datatype.* All <i>Date</i> methods ar included in the prototype of the DateTime type. toString and toJSON* are overridden to return a ISO8601 formatted date string instead of the default <i>Date</i> formatting.* See {@link patioTime#dateTimeToString} for formatting information.** @example** var dateTime = patio.DateTime(2009, 10, 10, 10, 10, 10); //=> '2009-11-10 10:10:10'* JSON.stringify(dateTime); //=> '2009-11-10 10:10:10'** @memberOf patio.sql* @param {Number} [y=1970] the year* @param {Number} [m=0] the month* @param {Number} [d=1] the day* @param {Number} [h=0] the hour* @param {Number} [min=0] the minute/s* @param {Number} [s=0] the second/s* @param {Number} [ms=0] the millisecond/s, this paramater is not be used, but may depending on the adapter.*/- 1
var DateTime = function (y, m, d, h, min, s, ms) {- 83
var args = argsToArray(arguments);- 83
if (args.length === 0) {- 0
this.date = new Date();- 83
} else if (isDate(y)) {- 77
this.date = y;} else {- 6
var date = new Date(1970, 0, 1, 0, 0, 0);- 6
isNumber(y) && date.setYear(y);- 6
isNumber(m) && date.setMonth(m);- 6
isNumber(d) && date.setDate(d);- 6
isNumber(h) && date.setHours(h);- 6
isNumber(min) && date.setMinutes(min);- 6
isNumber(s) && date.setSeconds(s);- 6
isNumber(ms) && date.setMilliseconds(ms);- 6
this.date = date;}};- 1
DateTime.prototype.toJSON = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DateTime.prototype.toString = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DATE_METHODS.forEach(function (op) {- 45
DateTime.prototype[op] = addDateMethod(op);}, this);/*** @class Represents a SQL Float type, by default is converted to double precision* @param {Number} number the number to be represented as a float* @memberOf patio.sql*/- 1
var Float = function (number) {- 0
this.number = number;};- 1
Float.prototype.toJSON = function () {- 0
return this.number;};/*** @class* Represents a SQL Decimal type, by default is converted to double precision* @param {Number} number the number to be represented as a decimal* @memberOf patio.sql*/- 1
var Decimal = function (number) {- 0
this.number = number;};- 1
Decimal.prototype.toJSON = function () {- 0
return this.number;};- 1
var hashToArray = function (hash) {- 3
var ret = [];- 3
if (isHash(hash)) {- 3
for (var i in hash) {- 3
var k = sql.stringToIdentifier(i), v = hash[i];- 3
v = isHash(v) ? hashToArray(v) : v;- 3
ret.push([k, v]);}}- 3
return ret;};/*** @namespace Collection of SQL related types, and expressions.** <p>* The {@link patio.sql} object* can be used directly to create {@link patio.sql.Expression}s, {@link patio.sql.Identifier}s, {@link patio.sql.SQLFunction}s,* and {@link patio.sql.QualifiedIdentifier}s.* <pre class='code'>* var sql = patio.sql;* //creating an identifier* sql.a; //=> a;** //creating a qualified identifier* sql.table__column; //table.column;** //BooleanExpression* sql.a.lt(sql.b); //=> a < 'b';** //SQL Functions* sql.sum(sql.a); //=> sum(a)* sql.avg(sql.b); //=> avg(b)* sql.a("b", 1); //=> a(b, 1)* sql.myDatabasesObjectFunction(sql.a, sql.b, sql.c); //=> myDatabasesObjectFunction(a, b, c);** //combined* sql.a.cast("boolean"); //=> 'CAST(a AS boolean)'* sql.a.plus(sql.b).lt(sql.c.minus(3) //=> ((a + b) < (c - 3))** </pre>** This is useful when combined with dataset filtering** <pre class="code">* var ds = DB.from("t");** ds.filter({a:[sql.b, sql.c]}).sql;* //=> SELECT * FROM t WHERE (a IN (b, c))** ds.select(sql["case"]({b:{c:1}}, false)).sql;* //=> SELECT (CASE WHEN b THEN (c = 1) ELSE 'f' END) FROM t** ds.select(sql.a).qualifyToFirstSource().sql;* //=> SELECT a FROM t** ds.order(sql.a.desc(), sql.b.asc()).sql;* //=> SELECT * FROM t ORDER BY a DESC, b ASC** ds.select(sql.a.as("b")).sql;* //=> SELECT a AS b FROM t** ds.filter(sql["case"]({a:sql.b}, sql.c, sql.d)).sql* //=> SELECT * FROM t WHERE (CASE d WHEN a THEN b ELSE c END)** ds.filter(sql.a.cast("boolean")).sql;* //=> SELECT * FROM t WHERE CAST(a AS boolean)** ds.filter(sql.a("b", 1)).sql* //=> SELECT * FROM t WHERE a(b, 1)* ds.filter(sql.a.plus(sql.b).lt(sql.c.minus(3)).sql;* //=> SELECT * FROM t WHERE ((a + b) < (c - 3))** ds.filter(sql.a.sqlSubscript(sql.b, 3)).sql;* //=> SELECT * FROM t WHERE a[b, 3]** ds.filter('? > ?', sql.a, 1).sql;* //=> SELECT * FROM t WHERE (a > 1);** ds.filter('{a} > {b}', {a:sql.c, b:1}).sql;* //=>SELECT * FROM t WHERE (c > 1)** ds.select(sql.literal("'a'"))* .filter(sql.a(3))* .filter('blah')* .order(sql.literal(true))* .group(sql.literal('a > ?', [1]))* .having(false).sql;* //=>"SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true");</pre>** </p>* @name sql* @memberOf patio*/- 1
sql = {/**@lends patio.sql*//*** Returns a {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},* or {@link patio.sql.ALiasedExpression} depending on the format of the string* passed in.** <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** patio.sql.identifier("a") //= > new patio.sql.Identifier("a");* patio.sql.identifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);* patio.sql.identifier("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.*/identifier: function (s) {- 1949
return sql.stringToIdentifier(s);},/*** @see patio.sql.identifier*/stringToIdentifier: function (name) {- 11429
!Dataset && (Dataset = require("./dataset"));- 11429
return new Dataset().stringToIdentifier(name);},/*** Creates a {@link patio.sql.LiteralString} or {@link patio.sql.PlaceHolderLiteralString}* depending on the arguments passed in. If a single string is passed in then* it is assumed to be a {@link patio.sql.LiteralString}. If more than one argument is* passed in then it is assumed to be a {@link patio.sql.PlaceHolderLiteralString}.** @example** //a literal string that will be placed in an SQL query with out quoting.* patio.sql.literal("a"); //=> new patio.sql.LiteralString('a');** //a placeholder string that will have ? replaced with the {@link patio.Dataset#literal} version of* //the arugment and replaced in the string.* patio.sql.literal("a = ?", 1) //=> a = 1* patio.sql.literal("a = ?", "b"); //=> a = 'b'* patio.sql.literal("a = {a} AND b = {b}", {a : 1, b : 2}); //=> a = 1 AND b = 2** @param {String ...} s variable number of arguments where the first argument* is a string. If multiple arguments are passed it is a assumed to be a {@link patio.sql.PlaceHolderLiteralString}** @return {patio.sql.LiteralString|patio.sql.PlaceHolderLiteralString} an expression that can be used as an argument* for {@link patio.Dataset} query methods.*/literal: function (s) {- 266
var args = argsToArray(arguments);- 266
return args.length > 1 ? PlaceHolderLiteralString.fromArgs(args) : new LiteralString(s);},/*** Creates a {@link patio.sql.Json}* depending on the arguments passed in. If a single string is passed in then* it is assumed that it's a valid json string. If an objects passed in it will stringify* it.** @param {String or Object ...} An object or string.** @return {patio.sql.Json} an expression that can be used as an argument* for {@link patio.Dataset} query methods.*/json: function (json) {- 1789
var ret = json;- 1789
if (!(isInstanceOf(ret, Json, JsonArray))) {- 1364
if (isString(ret)) {- 4
ret = JSON.parse(ret);}- 1363
if (isUndefinedOrNull(ret)) {- 0
ret = null;- 1363
} else if (isArray(ret)) {- 678
ret = new JsonArray(ret);- 685
} else if (isObject(ret)) {- 684
ret = new Json(ret);} else {- 1
throw new ExpressionError("Invalid value for json " + ret);}}- 1787
return ret;},/*** 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) {- 1253
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) {- 639
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");- 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 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({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"),/*** @function* Logical OR** @example** sql.a.or(sql.b) //=> "a" OR "b"** @return {patio.sql.BooleanExpression} a ORed boolean expression*/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, "BooleanMethods");/*** @class Defines case methods** @name CastMethods* @memberOf patio.sql*/- 1
var CastMethods = define({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);},/*** 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;},/*** 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");/*** @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({instance: {/**@ignore*/getters: {/*** @ignore*/sqlBoolean: function () {- 0
return new BooleanExpression("noop", this);},/*** @ignore*/sqlFunction: function () {- 42
return new SQLFunction(this);},/*** @ignore*/sqlNumber: function () {- 50
return new NumericExpression("noop", this);},/*** @ignore*/sqlString: function () {- 0
return new StringExpression("noop", this);}}}}).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 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"** @name InequalityMethods* @memberOf patio.sql*/- 1
var InequalityMethods = define({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"),/*** @private** Creates a boolean expression where the key is '>=' value 1 and '<=' value two.** @example** 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.*/between: function (items) {- 6
return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]));}}}).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({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");- 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);}};};/*** @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({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"),/*** @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.** @example** sql.a.minus(sql.b) //=> "a" - "b"** @return {patio.sql.NumericExpression}*/minus: numericMethod("minus"),/*** @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.** @example** sql.a.divide(sql.b) //=> "a" / "b"** @return {patio.sql.NumericExpression}*/divide: numericMethod("divide"),/*** @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.** @example** sql.a.multiply(sql.b) //=> "a" * "b"** @return {patio.sql.NumericExpression}*/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({instance: {/**@lends patio.sql.OrderedMethods.prototype*//*** Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).** @example* sql.name.asc(); //=> name ASC* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST** @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}*/asc: function (options) {- 7
return new OrderedExpression(this, false, options);},/*** Mark the receiving SQL column as sorting in a descending fashion.* @example** sql.price.desc(); //=> price DESC* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST** @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}*/desc: function (options) {- 26
return new OrderedExpression(this, true, options);}}}).as(sql, "OrderedMethods");/*** @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({instance: {/**@lends patio.sql.QualifyingMethods.prototype*//*** Qualify the receiver with the given qualifier (table for column/schema for table).** @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.** @return {patio.sql.QualifiedIdentifier}*/qualify: function (qualifier) {- 511
return new QualifiedIdentifier(qualifier, this);},/*** Use to create a .* expression.** @example* sql.table.all() //=> "table".** sql.table.qualify("schema").all() //=> "schema"."table".**** @return {patio.sql.ColumnAll}*/all: function () {- 208
return new ColumnAll(this);}}}).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({instance: {/**@lends patio.sql.StringMethods.prototype*//*** 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%'** @return {patio.sql.BooleanExpression}*/ilike: function (expression) {- 278
expression = argsToArray(arguments);- 278
return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([{caseInsensitive: true}]));},/*** 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}.** @example* sql.a.like(/^a/); //=> "a" ~* '^a'* sql.a.like("A%"); //=> "a" LIKE 'A%'** @param expression*/like: function (expression) {- 13
expression = argsToArray(arguments);- 13
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({instance: {/**@lends patio.sql.StringConcatenationMethods.prototype*//*** Return a {@link patio.sql.StringExpression} representing the concatenation of this expression* with the given argument.** @example** sql.x.sqlString.concat("y"); //=> "x" || "y"** @param expression expression to concatenate this expression with.*/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({instance: {/*** 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) {- 66
var args = argsToArray(arguments);- 66
return new SubScript(this, flatten(args));}}}).as(sql, "SubScriptMethods");/*** @class This is the parent of all expressions.** @name Expression* @memberOf patio.sql*/- 1
Expression = define({instance: {/**@lends patio.sql.Expression.prototype*//*** Returns the string representation of this expression** @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.* @return {String} a string literal version of this expression.*/sqlLiteral: function (ds) {- 0
return this.toString(ds);}},static: {/**@lends patio.sql.Expression*//*** This is a helper method that will take in an array of arguments and return an expression.** @example** QualifiedIdentifier.fromArgs(["table", "column"]);** @param {*[]} args array of arguments to pass into the constructor of the function.** @return {patio.sql.Expression} an expression.*/fromArgs: function (args) {- 2652
var ret, Self = this;- 2652
try {- 2652
ret = new Self();} catch (ignore) {}- 2652
this.apply(ret, args);- 2652
return ret;},/*** 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.** @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.*/isConditionSpecifier: function (obj) {- 21253
return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {- 8797
return isArray(i) && i.length === 2;}));}}}).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
AliasedExpression = Expression.extend({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) {- 1017
this.expression = expression;- 1017
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) {- 949
!Dataset && (Dataset = require("./dataset"));- 949
ds = ds || new Dataset();- 949
return ds.aliasedExpressionSql(this);}}}).as(sql, "AliasedExpression");- 1
CaseExpression = GenericExpression.extend({instance: {/**@lends patio.sql.CaseExpression.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.** @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** @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.*/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);}},/*** 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.** @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
Cast = GenericExpression.extend({instance: {/**@lends patio.sql.Cast*//*** Represents a cast of an SQL expression to a specific type.* @constructs* @augments patio.sql.GenericExpression** @param expr the expression to CAST.* @param type the type to CAST the expression to.** @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 {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 cast expression fragment.*/toString: function (ds) {- 2
!Dataset && (Dataset = require("./dataset"));- 2
ds = ds || new Dataset();- 2
return ds.castSql(this.expr, this.type);}}}).as(sql, "Cast");- 1
ColumnAll = Expression.extend({instance: {/**@lends patio.sql.ColumnAll.prototype*//*** Represents all columns in a given table, table.* in SQL* @constructs** @augments patio.sql.Expression** @param table the table this expression is for.** @property table the table this all column expression represents.*/constructor: function (table) {- 225
this.table = table;},/*** 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 columnAll expression fragment.*/toString: function (ds) {- 224
!Dataset && (Dataset = require("./dataset"));- 224
ds = ds || new Dataset();- 224
return ds.columnAllSql(this);}}}).as(sql, "ColumnAll");- 1
var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {instance: {/**@lends patio.sql.ComplexExpression.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.** @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>*/constructor: function (op) {- 7395
if (op) {- 6665
var args = argsToArray(arguments, 1);//make a copy of the args- 6665
var origArgs = args.slice(0);- 6665
args.forEach(function (a, i) {- 13510
if (Expression.isConditionSpecifier(a)) {- 6
args[i] = BooleanExpression.fromValuePairs(a);}});- 6665
op = op.toUpperCase();- 6665
if (N_ARITY_OPERATORS.hasOwnProperty(op)) {- 1143
if (args.length < 1) {- 0
throw new ExpressionError("The " + op + " operator requires at least 1 argument");}- 1143
var oldArgs = args.slice(0);- 1143
args = [];- 1143
oldArgs.forEach(function (a) {- 2529
a instanceof ComplexExpression && a.op === op ? args = args.concat(a.args) : args.push(a);});- 5522
} else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {- 5459
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.- 5459
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);}- 6665
this.op = op;- 6665
this.args = args;}},/*** 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.ComplexExpression}.*/toString: function (ds) {- 6325
!Dataset && (Dataset = require("./dataset"));- 6325
ds = ds || new Dataset();- 6325
return ds.complexExpressionSql(this.op, this.args);}},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,/*** Default mathematical operators.** @type Object* @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}*/MATHEMATICAL_OPERATORS: MATHEMATICAL_OPERATORS,/*** Default bitwise operators.** @type Object* @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}*/BITWISE_OPERATORS: BITWISE_OPERATORS,/*** Default inequality operators.** @type Object* @default {GT:">",GTE:">=",LT:"<",LTE:"<="}*/INEQUALITY_OPERATORS: INEQUALITY_OPERATORS,/*** Default boolean operators.** @type Object* @default {AND:"AND",OR:"OR"}*/BOOLEAN_OPERATORS: BOOLEAN_OPERATORS,/*** Default IN operators.** @type Object* @default {IN:"IN",NOTIN:'NOT IN'}*/IN_OPERATORS: IN_OPERATORS,/*** Default IS operators.** @type Object* @default {IS:"IS", ISNOT:'IS NOT'}*/IS_OPERATORS: IS_OPERATORS,/*** Default two arity operators.** @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'* }*/TWO_ARITY_OPERATORS: TWO_ARITY_OPERATORS,/*** Default N(multi) arity operators.** @type Object* @default {* "||":"||",* AND:"AND",* OR:"OR",* PLUS:"+",* MINUS:"-",* DIVIDE:"/", MULTIPLY:"*"* }*/N_ARITY_OPERATORS: N_ARITY_OPERATORS,/*** Default ONE operators.** @type Object* @default {* "NOT":"NOT",* "NOOP":"NOOP"* }*/ONE_ARITY_OPERATORS: ONE_ARITY_OPERATORS}}).as(sql, "ComplexExpression");/*** @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
BooleanExpression = define([ComplexExpression, BooleanMethods], {static: {/**@lends patio.sql.BooleanExpression*//*** 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"** @param {patio.sql.BooleanExpression} expression* the expression to invert.** @return {patio.sql.BooleanExpression} the inverted expression.*/invert: function (expression) {- 123
if (isInstanceOf(expression, BooleanExpression)) {- 118
var op = expression.op, newArgs;- 118
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 {- 115
newArgs = [OPERTATOR_INVERSIONS[op]].concat(expression.args);- 115
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);}},/*** 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:** <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>** 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) {- 7185
!Dataset && (Dataset = require("./dataset"));- 7185
op = op || "AND", negate = negate || false;- 7185
var pairArr = [];- 7185
var isArr = isArray(a) && Expression.isConditionSpecifier(a);- 7185
if (isHash(a)) {- 3194
pairArr.push(this.__filterObject(a, null, op));} else {- 3991
for (var k in a) {- 4698
var v = isArr ? a[k][1] : a[k], ret;- 4698
k = isArr ? a[k][0] : k;- 4698
if (isArray(v) || isInstanceOf(v, Dataset)) {- 17
k = isArray(k) ? k.map(sql.stringToIdentifier) : sql.stringToIdentifier(k);- 17
ret = new BooleanExpression("IN", k, v);- 4681
} else if (isInstanceOf(v, NegativeBooleanConstant)) {- 0
ret = new BooleanExpression("ISNOT", k, v.constant);- 4681
} else if (isInstanceOf(v, BooleanConstant)) {- 0
ret = new BooleanExpression("IS", k, v.constant);- 4681
} else if (isNull(v) || isBoolean(v)) {- 254
ret = new BooleanExpression("IS", k, v);- 4427
} else if (isHash(v)) {- 0
ret = BooleanExpression.__filterObject(v, k, op);- 4427
} else if (isRegExp(v)) {- 97
ret = StringExpression.like(sql.stringToIdentifier(k), v);} else {- 4330
ret = new BooleanExpression("EQ", sql.stringToIdentifier(k), v);}- 4698
negate && (ret = BooleanExpression.invert(ret));- 4698
pairArr.push(ret);}}//if We just have one then return the first otherwise create a new Boolean expression- 7185
return pairArr.length === 1 ? pairArr[0] : BooleanExpression.fromArgs([op].concat(pairArr));},/*** @private** This builds an expression from a hash** @example** 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 {Object} expr the expression we need to create an expression out of* @param {String} [key=null] the key that the hash corresponds to** @return {patio.sql.Expression} an expression to use in the filter*/__filterObject: function (expr, key, op) {/*jshint loopfunc:true*/- 3340
var pairs = [], opts, newKey;- 3340
var twoArityOperators = this.TWO_ARITY_OPERATORS;- 3340
for (var k in expr) {- 3384
var v = expr[k];- 3384
if (isHash(v)) { //its a hash too filter it too!- 144
pairs.push(this.__filterObject(v, k, op));- 3240
} else if (key && (twoArityOperators[k.toUpperCase()] || k.match(/between/i))) {//its a two arrity operator (e.g. '=', '>')- 147
newKey = isString(key) ? key.split(",") : [key];- 147
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, op);}, this));} else {- 146
newKey = [sql.stringToIdentifier(newKey[0])];- 146
if (k.match(/^like$/)) {//its a like clause {col : {like : "hello"}}- 3
pairs.push(StringExpression.like.apply(StringExpression, (newKey.concat(isArray(v) ? v : [v]))));- 143
} 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}))));- 141
} 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- 135
pairs.push(new BooleanExpression(k, newKey[0], v));}}} else {//we're not a twoarity operator//so we create a boolean expression out of it- 3093
newKey = k.split(",");- 3093
if (newKey.length === 1) {- 3087
newKey = sql.stringToIdentifier(newKey[0]);}- 3093
opts = [[newKey, v]];- 3093
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- 3340
return pairs.length === 1 ? pairs[0] : BooleanExpression.fromArgs([op || "AND"].concat(pairs));}}}).as(sql, "BooleanExpression");- 1
var Constant = GenericExpression.extend({instance: {/**@lends patio.sql.Constant.prototype*//*** Represents constants or psuedo-constants (e.g.'CURRENT_DATE) in SQL.** @constructs* @augments patio.sql.GenericExpression* @property {String} constant <b>READ ONLY</b> the contant.*/constructor: function (constant) {- 18
this.__constant = constant;},/*** 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.Constant}.*/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;}}}}).as(sql, "Constant");/*** @class Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.* @augments patio.sql.Constant* @name BooleanConstant* @memberOf patio.sql*/- 1
BooleanConstant = Constant.extend({instance: {/**@lends patio.sql.BooleanConstant.prototype*//*** 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}.*/toString: function (ds) {- 10
!Dataset && (Dataset = require("./dataset"));- 10
ds = ds || new Dataset();- 10
return ds.booleanConstantSql(this.__constant);}}}).as(sql, "BooleanConstant");/*** 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
NegativeBooleanConstant = BooleanConstant.extend({instance: {/**@lends patio.sql.NegativeBooleanConstant.prototype*//*** 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.NegativeBooleanConstant}.*/toString: function (ds) {- 2
!Dataset && (Dataset = require("./dataset"));- 2
ds = ds || new Dataset();- 2
return ds.negativeBooleanConstantSql(this.__constant);}}}).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;- 1
Identifier = define([GenericExpression, QualifyingMethods], {instance: {/**@lends patio.sql.Identifier.prototype*//*** 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 {String}value the identifier.** @property {String} value <b>READ ONLY</b> the column or table this identifier represents.*/constructor: function (value) {- 16466
this.__value = value;},/*** 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.Identifier}.*/toString: function (ds) {- 20370
!Dataset && (Dataset = require("./dataset"));- 20370
ds = ds || new Dataset();- 20370
return ds.quoteIdentifier(this);},/**@ignore*/getters: {value: function () {- 23567
return this.__value;}}}}).as(sql, "Identifier");- 1
var JoinClause = Expression.extend({instance: {/**@lends patio.sql.JoinClause.prototype*//*** 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** @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) {- 810
this.__joinType = joinType;- 810
this.__table = table;- 810
this.__tableAlias = tableAlias || null;},/*** Converts the {@link patio.sql.JoinClause} 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.JoinClause}.*/toString: function (ds) {- 17
!Dataset && (Dataset = require("./dataset"));- 17
ds = ds || new Dataset();- 17
return ds.joinClauseSql(this);},/**@ignore*/getters: {joinType: function () {- 953
return this.__joinType;},table: function () {- 954
return this.__table;},tableAlias: function () {- 952
return this.__tableAlias;}}}}).as(sql, "JoinClause");- 1
var JoinOnClause = JoinClause.extend({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** @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.*/constructor: function (on, joinType, table, tableAlias) {- 780
this.__on = on;- 780
this._super(arguments, [joinType, table, tableAlias]);},/*** 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.JoinOnClause}.*/toString: function (ds) {- 832
!Dataset && (Dataset = require("./dataset"));- 832
ds = ds || new Dataset();- 832
return ds.joinOnClauseSql(this);},/**@ignore*/getters: {on: function () {- 832
return this.__on;}}}}).as(sql, "JoinOnClause");- 1
var JoinUsingClause = JoinClause.extend({instance: {/**@lends patio.sql.JoinUsingClause.prototype*//*** 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.JoinClause** @param using the column/s to use when joining.* @property using <b>READ ONLY</b> the column/s to use when joining.*/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) {- 102
!Dataset && (Dataset = require("./dataset"));- 102
ds = ds || new Dataset();- 102
return ds.joinUsingClauseSql(this);},/**@ignore*/getters: {using: function () {- 102
return this.__using;}}}}).as(sql, "JoinUsingClause");- 1
PlaceHolderLiteralString = GenericExpression.extend({instance: {/**@lends patio.sql.PlaceHolderLiteralString.prototype*//*** 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* @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 (str, args, parens) {- 55
parens = parens || false;- 55
var v;- 55
this.__str = str;- 55
this.__args = isArray(args) && args.length === 1 && isHash((v = args[0])) ? v : args;- 55
this.__parens = parens;},/*** 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}.*/toString: function (ds) {- 55
!Dataset && (Dataset = require("./dataset"));- 55
ds = ds || new Dataset();- 55
return ds.placeholderLiteralStringSql(this);},/**@ignore*/getters: {str: function () {- 58
return this.__str;},args: function () {- 58
return this.__args;},parens: function () {- 58
return this.__parens;}}}}).as(sql, "PlaceHolderLiteralString");- 1
SQLFunction = GenericExpression.extend({instance: {/**@lends patio.sql.SQLFunction.prototype*//*** Represents an SQL function call.** @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) {- 1368
var args = argsToArray(arguments).slice(1);- 1368
this.__f = isInstanceOf(f, Identifier) ? f.value : f, this.__args = args.map(function (a) {- 972
return isString(a) ? sql.stringToIdentifier(a) : a;});},/*** Converts the {@link patio.sql.SQLFunction} 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}.*/toString: function (ds) {- 709
!Dataset && (Dataset = require("./dataset"));- 709
ds = ds || new Dataset();- 709
return ds.functionSql(this);},/**@ignore*/getters: {f: function () {- 711
return this.__f;},args: function () {- 711
return this.__args;}}}}).as(sql, "SQLFunction");/*** @class Subclass of {@link patio.sql.ComplexExpression} where the expression results* in a numeric value in SQL.** @name NumericExpression* @memberOf patio.sql* @augments patio.sql.ComplexExpression* @augments patio.sql.BitWiseMethods* @augments patio.sql.NumericMethods* @augments patio.sql.InequalityMethods*/- 1
NumericExpression = define([ComplexExpression, BitWiseMethods, NumericMethods, InequalityMethods]).as(sql, "NumericExpression");- 1
OrderedExpression = Expression.extend({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});},/*** @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);},/**@ignore*/getters: {expression: function () {- 75
return this.__expression;},descending: function () {- 75
return this.__descending;},nulls: function () {- 82
return this.__nulls;}}},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
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) {- 4425
this.__table = table;- 4425
this.__column = column;},/*** 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) {- 4506
!Dataset && (Dataset = require("./dataset"));- 4506
ds = ds || new Dataset();- 4506
return ds.qualifiedIdentifierSql(this);},/**@ignore*/getters: {table: function () {- 4528
return this.__table;},column: function () {- 4533
return this.__column;}}}}).as(sql, "QualifiedIdentifier");- 1
var likeElement = function (re) {- 871
var ret;- 871
if (isRegExp(re)) {- 108
ret = [("" + re).replace(/^\/|\/$|\/[i|m|g]*$/g, ""), true, re.ignoreCase];} else {- 763
ret = [re, false, false];}- 871
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
StringExpression = define([ComplexExpression, StringMethods, StringConcatenationMethods, InequalityMethods, NoBooleanInputMethods], {static: {/**@lends patio.sql.StringExpression*//*** <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) {- 435
var args = argsToArray(arguments, 1);- 435
var params = likeElement(l);- 435
var likeMap = this.likeMap;- 435
var lh = params[0], lre = params[1], lci = params[2];- 435
var last = args[args.length - 1];- 435
lci = (isHash(last) ? args.pop() : {})["caseInsensitive"] ? true : lci;- 435
args = args.map(function (ce) {- 436
var r, rre, rci;- 436
var ceArr = likeElement(ce);- 436
r = ceArr[0], rre = ceArr[1], rci = ceArr[2];- 436
return new BooleanExpression(likeMap["" + (lre || rre) + (lci || rci)], l, r);}, this);- 435
return args.length === 1 ? args[0] : BooleanExpression.fromArgs(["OR"].concat(args));},/*** Like map used to by {@link patio.sql.StringExpression.like} to create the* LIKE expression.* @type Object*/likeMap: {"truetrue": '~*', "truefalse": "~", "falsetrue": "ILIKE", "falsefalse": "LIKE"}}}).as(sql, "StringExpression");- 1
SubScript = GenericExpression.extend({instance: {/**@lends patio.sql.SubScript.prototype*//*** 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- 67
this.__arrCol = arrCol;//The array of subscripts to use (should be an array of numbers)- 67
this.__sub = sub;},/*** 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));},/*** 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) {- 67
!Dataset && (Dataset = require("./dataset"));- 67
ds = ds || new Dataset();- 67
return ds.subscriptSql(this);},/**@ignore*/getters: {f: function () {- 68
return this.__arrCol;},sub: function () {- 68
return this.__sub;}}}}).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"];- 1
var addStringMethod = function (op) {- 24
return function () {- 3524
return this.__str[op].apply(this.__str, arguments);};};- 1
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) {- 293
this.__str = str;}}}).as(sql, "LiteralString");- 1
STRING_METHODS.forEach(function (op) {- 24
LiteralString.prototype[op] = addStringMethod(op);}, this);/*** Represents a json object that should be placed into a SQL query literally.* @constructs** @name Json* @memberOf patio.sql*/- 1
Json = define({instance: {constructor: function (obj) {- 684
merge(this, obj);}}}).as(sql, "Json");/*** Represents a json array that should be placed into a SQL query literally.* @constructs** @name JsonArray* @memberOf patio.sql*/- 1
var id = 0;- 1
function JsonArray(arr) {- 678
if (!this instanceof JsonArray) {- 0
return new JsonArray(arr);}- 678
this.id = id++;- 678
Array.call(this);- 678
var i = -1, l = arr.length;- 678
while (++i < l) {- 678
this.push(arr[i]);}}/*jshint supernew:false*/- 1
JsonArray.prototype = [];- 1
JsonArray.prototype.toJSON = function () {- 251
return this.slice();};- 1
JsonArray.prototype.valueOf = function () {- 0
return this.slice();};//require("util").inherits(JsonArray, Array);- 1
sql.JsonArray = JsonArray;
|
migration.js
|
Coverage92.02
SLOC554
LOC238
Missed19
|
- 1
var comb = require("comb"),Promise = comb.Promise,errors = require("./errors"),MigrationError = errors.MigrationError,NotImplemented = errors.NotImplemented(),format = comb.string.format,define = comb.define,isFunction = comb.isFunction,isNumber = comb.isNumber,when = comb.when,isUndefined = comb.isUndefined,fs = require("fs"),path = require("path"),baseName = path.basename,asyncArray = comb.async.array,IntegerMigrator,TimestampMigrator;- 1
var Migrator = define(null, {instance: {/**@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) {- 31
var self = this;- 31
return this._static.getFileNames(this.directory).chain(function (files) {- 31
self.__files = files;- 31
return files;});} else {- 19
return new Promise().callback(this.__files).promise();}},getMigrationVersionFromFile: function (filename) {- 488
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) {- 62
var ret = new Promise(), self = this, pattern = this.MIGRATION_FILE_PATTERN;- 62
fs.readdir(directory, function (err, files) {- 62
if (err) {- 0
ret.errback(err);} else {- 62
files = files.filter(function (file) {- 302
return file.match(pattern) !== null;}).map(function (file) {- 302
return path.resolve(directory, file);});- 62
files.sort();- 62
ret.callback(files);}});- 62
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").chain(function(){* console.log("done migrating!");* });** patio. migrate(DB, __dirname + "/timestamp_migration", {target : 0}).chain(function(){* console.log("done migrating down!");* });*** @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.** @return {Promise} a promise that is resolved once the migration is complete.*/run: function (db, directory, opts, cb) {- 31
if (isFunction(opts)) {- 0
cb = opts;- 0
opts = {};} else {- 31
opts = opts || {};}- 31
opts = opts || {};- 31
return this.__getMigrator(directory).chain(function (Migrator) {- 31
return new Migrator(db, directory, opts).run();}).classic(cb);},// 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 retClass = IntegerMigrator, MIGRATION_SPLITTER = this.MIGRATION_SPLITTER, MINIMUM_TIMESTAMP = this.MINIMUM_TIMESTAMP;- 31
return this.getFileNames(directory).chain(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(MIGRATION_SPLITTER)[0], 10) > MINIMUM_TIMESTAMP) {- 18
retClass = TimestampMigrator;- 18
break;}}}- 31
return retClass;});}}});/*** @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
IntegerMigrator = define(Migrator, {instance: {/**@lends patio.migrations.IntegerMigrator.prototype*/current: null,direction: null,migrations: null,_migrationFiles: null,run: function () {- 13
var DB = this.db, self = this;- 13
return this._getLatestMigrationVersion().chain(function (target) {- 11
return self._getCurrentMigrationVersion().chain(function (current) {- 11
if (current !== target) {- 11
var direction = self.direction = current < target ? "up" : "down", isUp = direction === "up", version = 0;- 11
return self._getMigrations(current, target, direction).chain(function (migrations) {- 11
return asyncArray(migrations).forEach(function (curr) {- 49
var 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
return DB.transaction(function () {- 49
if (!isFunction(migration[direction])) {- 0
return self._setMigrationVersion(lv);} else {- 49
var nextP = new Promise();- 49
var dirP = migration[direction].apply(DB, [DB, nextP.resolve.bind(nextP)]);- 49
return (comb.isPromiseLike(dirP) ? dirP : nextP).chain(function () {- 49
return self._setMigrationVersion(lv);});}}).chain(function () {- 49
DB.logInfo("Finished applying migration version %d, direction: %s, took % 4dms seconds", lv, direction, new Date() - now);});}, 1).chain(function () {- 11
return version;});});} else {- 0
return target;}});}).chain(function (version) {- 11
return version;});},_getMigrations: function (current, target, direction) {- 11
var isUp = direction === "up", migrations = [];- 11
return when(this._getMigrationFiles()).chain(function (files) {- 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
throw new MigrationError("Invalid target " + target);}- 11
return migrations;});},_getMigrationFiles: function () {- 19
if (!this._migrationFiles) {- 13
var retFiles = [], self = this;- 13
return this.getFileNames().chain(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 = self.getMigrationVersionFromFile(file);- 59
if (isUndefined(retFiles[version])) {- 58
retFiles[version] = file;} else {- 1
throw 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
throw new MigrationError("Missing migration for " + j);}}}- 11
self._migrationFiles = retFiles;- 11
return retFiles;});} else {- 6
return when(this._migrationFiles);}},_getLatestMigrationVersion: function () {- 13
if (!isUndefined(this._opts.target)) {- 5
return when(this._opts.target);} else {- 8
var self = this;- 8
return this._getMigrationFiles().chain(function (files) {- 6
var l = files[files.length - 1];- 6
return l ? self.getMigrationVersionFromFile(path.basename(l)) : null;});}},_getCurrentMigrationVersion: function () {- 11
if (!isUndefined(this._opts.current)) {- 2
return when(this._opts.current);} else {- 9
var column = this.column;- 9
return when(this._getSchemaDataset()).chain(function (ds) {- 9
return ds.get(column);});}},_setMigrationVersion: function (version) {- 49
var c = this.column;- 49
return this._getSchemaDataset().chain(function (ds) {- 49
var item = {};- 49
item[c] = version;- 49
return ds.update(item).chainBoth();});},_getSchemaDataset: function () {- 58
var c = this.column, table = this.table;- 58
if (!this.__schemaDataset) {- 11
var ds = this.db.from(table), self = this;- 11
return this.__createOrAlterMigrationTable().chain(function () {- 11
return ds.isEmpty().chain(function (empty) {- 11
if (empty) {- 0
var item = {};- 0
item[c] = -1;- 0
self.__schemaDataset = ds;- 0
return ds.insert(item).chain(function () {- 0
return ds;});} else {- 11
return ds.count().chain(function (count) {- 11
if (count > 1) {- 0
throw new Error("More than one row in migrator table");} else {- 11
self.__schemaDataset = ds;- 11
return ds;}});}});});} else {- 47
return when(this.__schemaDataset);}},__createOrAlterMigrationTable: function () {- 11
var c = this.column, table = this.table, db = this.db, ds = this.db.from(table), self = this;- 11
return db.tableExists(table).chain(function (exists) {- 11
if (!exists) {- 6
return db.createTable(table, function () {- 6
this.column(c, "integer", {"default": -1, allowNull: false});});} else {- 5
return ds.columns.chain(function (columns) {- 5
if (columns.indexOf(c) === -1) {- 1
db.addColumn(table, c, "integer", {"default": -1, allowNull: false});}});}});}},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
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 DB = this.db, column = this.column, self = this;- 18
return this.__getMigrationFiles().chain(function (migrations) {- 18
return self._getSchemaDataset().chain(function (ds) {- 18
return asyncArray(migrations).forEach(function (curr) {- 70
var 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
return DB.transaction(function () {- 70
var fileLowerCase = file.toLowerCase();- 70
var query = {};- 70
query[column] = fileLowerCase;- 70
if (!isFunction(migration[direction])) {- 0
return (direction === "up" ? ds.insert(query) : ds.filter(query).remove());} else {- 70
var nextP = new Promise();- 70
var dirP = migration[direction].apply(DB, [DB, nextP.resolve.bind(nextP)]);- 68
return (comb.isPromiseLike(dirP) ? dirP : nextP).chain(function () {- 68
return (direction === "up" ? ds.insert(query) : ds.filter(query).remove());});}}).chain(function () {- 68
DB.logInfo("Finished applying migration file %s, direction: %s, took % 4dms seconds", file, direction, new Date() - now);});}, 1);});});},getFileNames: function () {- 37
var self = this;- 37
return asyncArray(this._super(arguments)).sort(function (f1, f2) {- 184
var ret = self.getMigrationVersionFromFile(f1) - self.getMigrationVersionFromFile(f2);- 184
if (ret === 0) {- 4
var b1 = baseName(f1, ".js").split("."),b2 = baseName(f2, ".js").split(".");- 4
b1 = b1[b1.length - 1];- 4
b2 = b2[b2.length - 1];- 4
ret = b1 > b1 ? 1 : b1 < b2 ? -1 : 0;}- 184
return ret;});},__getAppliedMigrations: function () {- 18
if (!this.__appliedMigrations) {- 18
var self = this;- 18
return this._getSchemaDataset().chain(function (ds) {- 18
return when(ds.selectOrderMap(self.column), self.getFileNames()).chain(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
throw new MigrationError("Applied migrations file not found in directory " + appliedMigrations[i]);}}- 9
self.__appliedMigrations = appliedMigrations;- 9
return appliedMigrations;} else {- 9
self.__appliedMigrations = [];- 9
return appliedMigrations;}});});} else {- 0
return when(this.__appliedMigrations);}},__getMigrationFiles: function () {- 18
var upMigrations = [], downMigrations = [], target = this.target;- 18
if (!this.__migrationFiles) {- 18
var self = this;- 18
return when(this.getFileNames(), this.__getAppliedMigrations()).chain(function (res) {- 18
var files = res[0], appliedMigrations = res[1];- 18
var l = files.length;- 18
if (l > 0) {- 18
for (var i = 0; i < l; i++) {- 92
var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase(), index = appliedMigrations.indexOf(fLowerCase);- 92
if (!isUndefined(target)) {- 48
var version = self.getMigrationVersionFromFile(f);- 48
if (version > target || (version === 0 && target === version)) {- 35
if (index !== -1) {- 26
downMigrations.push([f, require(file), "down"]);}- 13
} else if (index === -1) {- 9
upMigrations.push([f, require(file), "up"]);}- 44
} else if (index === -1) {- 35
upMigrations.push([f, require(file), "up"]);}}- 18
self.__migrationFiles = upMigrations.concat(downMigrations.reverse());- 18
return self.__migrationFiles;}});} else {- 0
return when(this.__migrationFiles);}},// Returns the dataset for the schema_migrations table. If no such table// exists, it is automatically created._getSchemaDataset: function () {- 36
if (!this.__schemaDataset) {- 18
var ds = this.db.from(this.table), self = this;- 18
return this.__createTable().chain(function () {- 18
return (self.__schemaDataset = ds);});} else {- 18
return when(this.__schemaDataset);}},__convertSchemaInfo: function () {- 1
var c = this.column, ds = this.db.from(this.table), self = this;- 1
return this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).chain(function (version) {- 1
return self.getFileNames().chain(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 (self.getMigrationVersionFromFile(f) <= version) {- 5
var insert = {};- 5
insert[c] = f;- 5
inserts.push(ds.insert(insert));}}}- 1
return when(inserts);});});},__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), self = this;- 18
return when(db.tableExists(table), db.tableExists(intMigrationTable)).chain(function (res) {- 18
var exists = res[0], intMigratorExists = res[1];- 18
if (!exists) {- 10
return db.createTable(table, function () {- 10
this.column(c, String, {primaryKey: true});}).chain(function () {- 10
if (intMigratorExists) {- 1
return db.from(intMigrationTable).all().chain(function (versions) {- 1
var version;- 1
if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {- 1
return self.__convertSchemaInfo();}});}});} else {- 8
return ds.columns.chain(function (columns) {- 8
if (columns.indexOf(c) === -1) {- 0
throw new MigrationError(format("Migration table %s does not contain column %s", table, c));}});}});}},static: {DEFAULT_SCHEMA_COLUMN: "filename",DEFAULT_SCHEMA_TABLE: "schema_migrations"}}).as(exports, "TimestampMigrator");- 1
exports.run = function () {- 31
return Migrator.run.apply(Migrator, arguments);};
|
dataset/sql.js
|
Coverage92.18
SLOC1714
LOC524
Missed41
|
- 1
var comb = require("comb"),define = comb.define,array = comb.array,toArray = array.toArray,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,hitch = comb.hitch,isUndefinedOrNull = comb.isUndefinedOrNull,isString = comb.isString,sql = require("../sql").sql,Json = sql.Json,JsonArray = sql.JsonArray,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;- 1
var clauseMethods = function (type, clauses) {- 13
if (isString(clauses)) {- 13
clauses = clauses.split(" ");}- 13
return clauses.map(function (clause) {- 84
return ["_", type, clause.charAt(0).toUpperCase(), clause.substr(1), "Sql"].join("");});};- 1
define({instance: {/**@lends patio.Dataset.prototype*//*** Dataset mixin that provides functions to the {@link patio.dataset.Dataset} to* create SELECT, UPDATE, CREATE, and DELETE SQL statements, based off of the the* methods invoked in the {@link patio.dataset._Query}* mixin. This class should not be used directly by** @constructs* @memberOf patio.dataset* @name _Query** @property {String} sql Readonly property that returns a SELECT statement.* @property {String} deleteSql DELETE SQL query string. See {@link patio.dataset._Actions#delete}.** <pre class="code">* dataset.filter(function(){* return this.price.gte(100);* }).deleteSql;* // => "DELETE FROM items WHERE (price >= 100)"* </pre>* @property {String} selectSql Returns a SELECT SQL query string.** <pre class="code">** DB.from("items").selectSql;* //=> "SELECT * FROM items"* </pre>* @property {String} truncateSql Returns a TRUNCATE SQL query string. See {@link patio.dataset._Actions#truncate}** <pre class="code">** DB.from("items").truncateSql();* //=> 'TRUNCATE items'* </pre>* @property {String} exists Returns an EXISTS clause for the dataset as a {@link patio.sql.LiteralString}.** <pre class="code">** var ds = DB.from("test");* ds.filter(ds.filter(sql.price.lt(100))).exists()).sql;* //=> 'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)))'** </pre>**/constructor: function () {//We initialize these here because otherwise//the will be blank because of recursive dependencies.- 27052
!patio && (patio = require("../index"));- 27052
!Dataset && (Dataset = patio.Dataset);- 27052
this.outputIdentifier = hitch(this, this.outputIdentifier);- 27052
this._super(arguments);},/*** Returns an INSERT SQL query string. See {@link patio.dataset._Actions#insert}** @example*** DB.from("items").insertSql({a : 1});* //=> INSERT INTO items (a) VALUES (1)** var ds = DB.from("test");** //default values* ds.insertSql();* //=> INSERT INTO test DEFAULT VALUES** //with hash* ds.insertSql({name:'wxyz', price:342});* //=> INSERT INTO test (name, price) VALUES ('wxyz', 342)* ds.insertSql({});* //=> INSERT INTO test DEFAULT VALUES** //object that has a values property* ds.insertSql({values:{a:1}});* //=> INSERT INTO test (a) VALUES (1)** //arbitrary value* ds.insertSql(123);* //=> INSERT INTO test VALUES (123)** //with dataset* ds.insertSql(DB.from("something").filter({x:2}));* //=> INSERT INTO test SELECT * FROM something WHERE (x = 2)** //with array* ds.insertSql('a', 2, 6.5);* //=> INSERT INTO test VALUES ('a', 2, 6.5)** @throws {patio.QueryError} if there are Different number of values and columns given to insertSql or* if an invalid BooleanExpresion is given.** @param {patio.Dataset|patio.sql.LiteralString|Array|Object|patio.sql.BooleanExpression|...} values values to* insert into the database. The INSERT statement generated depends on the type.* <ul>* <li>Empty object| Or no arugments: then DEFAULT VALUES is used.</li>* <li>Object: the keys will be used as the columns, and values will be the values inserted.</li>* <li>Single {@link patio.Dataset} : an insert with subselect will be performed.</li>* <li>Array with {@link patio.Dataset} : The array will be used for columns and a subselect will performed with the dataset for the values.</li>* <li>{@link patio.sql.LiteralString} : the literal value will be used.</li>* <li>Single Array : the values in the array will be used as the VALUES clause.</li>* <li>Two Arrays: the first array is the columns the second array is the values.</li>* <li>{@link patio.sql.BooleanExpression} : the expression will be used as the values.* <li>An arbitrary number of arguments : the {@link patio.Dataset#literal} version of the values will be used</li>* </ul>*** @return {String} a INSERT SQL query string*/insertSql: function (values) {- 1289
values = argsToArray(arguments);- 1289
var opts = this.__opts;- 1289
if (opts.sql) {- 0
return this._staticSql(opts.sql);}- 1289
this.__checkModificationAllowed();- 1285
var columns = [];- 1285
switch (values.length) {case 0 ://we have no values- 14
return this.insertSql({});case 1 :- 1193
var vals = values[0], v;- 1193
if (isInstanceOf(vals, Dataset, LiteralString) || isArray(vals)) {- 10
values = vals;- 1183
} else if (vals.hasOwnProperty("values") && isObject((v = vals.values))) {- 2
return this.insertSql(v);- 1181
} else if (isHash(vals)) {- 1175
vals = merge({}, opts.defaults || {}, vals);- 1175
vals = merge({}, vals, opts.overrides || {});- 1175
values = [];- 1175
for (var i in vals) {- 6400
columns.push(i);- 6400
values.push(vals[i]);}- 6
} else if (isInstanceOf(vals, BooleanExpression)) {- 1
var op = vals.op;- 1
values = [];- 1
if (!isUndefinedOrNull(this._static.TWO_ARITY_OPERATORS[op])) {- 1
var args = vals.args;- 1
columns.push(args[0]);- 1
values.push(args[1]);} else {- 0
throw new QueryError("Invalid Expression op: " + op);}}- 1191
break;case 2 :- 75
var v0 = values[0], v1 = values[1];- 75
if (isArray(v0) && isArray(v1) || isInstanceOf(v1, Dataset, LiteralString)) {- 70
columns = v0, values = v1;- 70
if (isArray(values) && columns.length !== values.length) {- 0
throw new QueryError("Different number of values and columns given to insertSql");}}- 75
break;}- 1269
columns = columns.map(function (k) {- 6501
return isString(k) ? new Identifier(k) : k;}, this);- 1269
return this.mergeOptions({columns: columns, values: values})._insertSql();},/*** Returns an array of insert statements for inserting multiple records.* This method is used by {@link patio.dataset._Actions#multiInsert} to format insert statements.* <b>This method is not typically used directly.</b>** <p>* <b>Note:</b>This method should be overridden by descendants if there is support for* inserting multiple records in a single SQL statement.* </p>** @param {Array} columns The columns to insert values for.* This array will be used as the base for each values item in the values array.* @param {Array[Array]} values Array of arrays of values to insert into the columns.** @return {String[]} array of insert statements.*/multiInsertSql: function (columns, values) {- 32
return values.map(function (r) {- 56
return this.insertSql(columns, r);}, this);},/***Formats an UPDATE statement using the given values. See {@link patio.dataset._Actions#update}.** @example** DB.from("items").updateSql({price : 100, category : 'software'});* //=> "UPDATE items SET price = 100, category = 'software'** @throw {QueryError} If the dataset is grouped or includes more than one table.** @param {*...} Variable number of values to update the table with.* The UPDATE statement created depends on the values passed in.* <ul>* <li>Object : the keys will be used as the columns and the values will be the values to set to columns to</li>* <li>{@link patio.sql.Expression} : the {@link patio.dataset._Sql#literal} representation of the* {@link patio.sql.Expression} will be used as the value* </li>* </li> Other : the {@link patio.dataset._Sql#literal} value will be used as the value</li>* </ul>** @return {String} the UPDATE statement.*/updateSql: function (values) {- 233
values = argsToArray(arguments);- 233
var update;- 233
if (this.__opts.sql) {- 0
update = this._staticSql(this.__opts.sql);} else {- 233
this.__checkModificationAllowed();- 229
update = this.mergeOptions({values: values})._updateSql();}- 229
return update;},/*** Returns a qualified column name (including a table name) if the column* name isn't already qualified.** @example** dataset.qualifiedColumnName("b1", "items");* //=> items.b1** dataset.qualifiedColumnName("ccc__b"));* //=> 'ccc.b'** dataset.qualifiedColumnName("ccc__b", "items"));* //=> 'ccc.b'** @param {String} column the column to qualify. If the column is already qualified (e.g. ccc__b) then the* table name (e.g. ccc) will override the provided table.** @param {String} table the name of the table to qualify the column to.** @return {String} the qualified column name..**/qualifiedColumnName: function (column, table) {- 1166
if (isString(column)) {- 1163
var parts = this._splitString(column);- 1163
var columnTable = parts[0], alias = parts[2], schema, tableAlias;- 1163
column = parts[1];- 1163
if (!columnTable) {- 1161
if (isInstanceOf(table, Identifier)) {- 1138
table = table.value;}- 1161
if (isInstanceOf(table, AliasedExpression)) {- 0
tableAlias = table.alias;- 1161
} else if (isInstanceOf(table, QualifiedIdentifier)) {- 1
tableAlias = table;} else {- 1160
parts = this._splitString(table);- 1160
schema = parts[0];- 1160
tableAlias = parts[2];- 1160
table = parts[1];- 1160
if (schema) {- 0
tableAlias = new Identifier(tableAlias) || new QualifiedIdentifier(schema, table);}}- 1161
columnTable = tableAlias || table;}- 1163
return new QualifiedIdentifier(columnTable, column);- 3
} else if (isInstanceOf(column, Identifier)) {- 0
return column.qualify(table);} else {- 3
return column;}},/*** Creates a unique table alias that hasn't already been used in this dataset.** @example** DB.from("table").unusedTableAlias("t");* //=> "t"** DB.from("table").unusedTableAlias("table");* //=> "table0"** DB.from("table", "table0"]).unusedTableAlias("table");* //=> "table1"** @param {String|patio.sql.Identifier} tableAlias the table to get an unused alias for.** @return {String} the implicit alias that is in tableAlias with a possible "N"* if the alias has already been used, where N is an integer starting at 0.*/unusedTableAlias: function (tableAlias) {- 11
tableAlias = this._toTableName(tableAlias);- 11
var usedAliases = [], from, join;- 11
if ((from = this.__opts.from) != null) {- 11
usedAliases = usedAliases.concat(from.map(function (n) {- 13
return this._toTableName(n);}, this));}- 11
if ((join = this.__opts.join) != null) {- 1
usedAliases = usedAliases.concat(join.map(function (join) {- 1
if (join.tableAlias) {- 0
return this.__toAliasedTableName(join.tableAlias);} else {- 1
return this._toTableName(join.table);}}, this));}- 11
if (usedAliases.indexOf(tableAlias) !== -1) {- 10
var base = tableAlias, i = 0;- 10
do {- 13
tableAlias = string.format("%s%d", base, i++);} while (usedAliases.indexOf(tableAlias) !== -1);}- 11
return tableAlias;},/*** Returns a literal representation of a value to be used as part* of an SQL expression.** @example** DB.from("items").literal("abc'def\\") //=> "'abc''def\\\\'"* DB.from("items").literal("items__id") //=> "items.id"* DB.from("items").literal([1, 2, 3]) //=> "(1, 2, 3)"* DB.from("items").literal(DB.from("items")) //=> "(SELECT * FROM items)"* DB.from("items").literal(sql.x.plus(1).gt("y")); //=> "((x + 1) > y)"** @throws {patio.QueryError} If an unsupported object is given.* @param {*} v the value to convert the the SQL literal representation** @return {String} a literal representation of the value.*/literal: function (v) {- 42352
if (isInstanceOf(v, Json, JsonArray)) {- 504
return this._literalJson(v);- 41848
} else if (isInstanceOf(v, LiteralString)) {- 193
return "" + v;- 41655
} else if (isString(v)) {- 5606
return this._literalString(v);- 36049
} else if (isNumber(v)) {- 6324
return this._literalNumber(v);- 29725
} else if (isInstanceOf(v, Expression)) {- 27206
return this._literalExpression(v);- 2519
} else if (isInstanceOf(v, Dataset)) {- 104
return this._literalDataset(v);- 2415
} else if (isArray(v)) {- 2019
return this._literalArray(v);- 396
} else if (isInstanceOf(v, sql.Year)) {- 1
return this._literalYear(v);- 395
} else if (isInstanceOf(v, sql.TimeStamp, sql.DateTime)) {- 31
return this._literalTimestamp(v);- 364
} else if (isDate(v)) {- 7
return this._literalDate(v);- 357
} else if (isInstanceOf(v, sql.Time)) {- 2
return this._literalTime(v);- 355
} else if (Buffer.isBuffer(v)) {- 11
return this._literalBuffer(v);- 344
} else if (isNull(v)) {- 227
return this._literalNull();- 117
} else if (isBoolean(v)) {- 107
return this._literalBoolean(v);- 10
} else if (isHash(v)) {- 6
return this._literalObject(v);} else {- 4
return this._literalOther(v);}},//BEGIN PROTECTED/**** Qualify the given expression to the given table.* @param {patio.sql.Expression} column the expression to qualify* @param table the table to qualify the expression to*/_qualifiedExpression: function (e, table) {- 349
var h, i, args;- 349
if (isString(e)) {//this should not be hit but here just for completeness- 0
return this.stringToIdentifier(e);- 349
} else if (isArray(e)) {- 73
return e.map(function (exp) {- 164
return this._qualifiedExpression(exp, table);}, this);- 276
} else if (isInstanceOf(e, Identifier)) {- 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});- 215
} else if (isInstanceOf(e, AliasedExpression)) {- 72
return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias);- 143
} else if (isInstanceOf(e, CaseExpression)) {- 2
args = [this._qualifiedExpression(e.conditions, table), this._qualifiedExpression(e.def, table)];- 2
if (e.hasExpression) {- 1
args.push(this._qualifiedExpression(e.expression, table));}- 2
return CaseExpression.fromArgs(args);- 141
} else if (isInstanceOf(e, Cast)) {- 1
return new Cast(this._qualifiedExpression(e.expr, table), e.type);- 140
} else if (isInstanceOf(e, SQLFunction)) {- 2
return SQLFunction.fromArgs([e.f].concat(this._qualifiedExpression(e.args, table)));- 138
} else if (isInstanceOf(e, ComplexExpression)) {- 30
return ComplexExpression.fromArgs([e.op].concat(this._qualifiedExpression(e.args, table)));- 108
} else if (isInstanceOf(e, SubScript)) {- 1
return new SubScript(this._qualifiedExpression(e.f, table), this._qualifiedExpression(e.sub, table));- 107
} else if (isInstanceOf(e, PlaceHolderLiteralString)) {- 3
args = [];- 3
var eArgs = e.args;- 3
if (isHash(eArgs)) {- 1
h = {};- 1
for (i in eArgs) {- 2
h[i] = this._qualifiedExpression(eArgs[i], table);}- 1
args = h;} else {- 2
args = this._qualifiedExpression(eArgs, table);}- 3
return new PlaceHolderLiteralString(e.str, args, e.parens);- 104
} else if (isHash(e)) {- 0
h = {};- 0
for (i in e) {- 0
h[this._qualifiedExpression(i, table) + ""] = this._qualifiedExpression(e[i], table);}- 0
return h;} else {- 104
return e;}},/*** Returns a string that is the name of the table.** @throws {patio.QueryError} If the name is not a String {@link patio.sql.Identifier},* {@link patio.sql.QualifiedIdentifier} or {@link patio.sql.AliasedExpression}.** @param {String|patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} name* the object to get the table name from.** @return {String} the name of the table.*/_toTableName: function (name) {- 67
var ret;- 67
if (isString(name)) {- 10
var parts = this._splitString(name);- 10
var schema = parts[0], table = parts[1], alias = parts[2];- 10
ret = (schema || alias) ? alias || table : table;- 57
} else if (isInstanceOf(name, Identifier)) {- 54
ret = name.value;- 3
} else if (isInstanceOf(name, QualifiedIdentifier)) {- 1
ret = this._toTableName(name.column);- 2
} else if (isInstanceOf(name, AliasedExpression)) {- 2
ret = this.__toAliasedTableName(name.alias);} else {- 0
throw new QueryError("Invalid object to retrieve the table name from");}- 67
return ret;},/*** Return the unaliased part of the identifier. Handles both* implicit aliases in strings, as well as {@link patio.sql.AliasedExpression}s.* Other objects are returned as is.** @param {String|patio.sql.AliasedExpression|*} tableAlias the object to un alias** @return {patio.sql.QualifiedIdentifier|String|*} the unaliased portion of the identifier*/_unaliasedIdentifier: function (c) {- 35
if (isString(c)) {- 30
var parts = this._splitString(c);- 30
var table = parts[0], column = parts[1];- 30
if (table) {- 5
return new QualifiedIdentifier(table, column);}- 25
return column;- 5
} else if (isInstanceOf(c, AliasedExpression)) {- 3
return c.expression;} else {- 2
return c;}},/*** Return a [@link patio.sql._Query#fromSelf} dataset if an order or limit is specified, so it works as expected* with UNION, EXCEPT, and INTERSECT clauses.*/_compoundFromSelf: function () {- 102
var opts = this.__opts;- 102
return (opts["limit"] || opts["order"]) ? this.fromSelf() : this;},/*** Return true if the dataset has a non-null value for any key in opts.* @param opts the options to compate this datasets options to** @return {Boolean} true if the dataset has a non-null value for any key in opts.*/_optionsOverlap: function (opts) {- 118
var o = [];- 118
for (var i in this.__opts) {- 299
if (!isUndefinedOrNull(this.__opts[i])) {- 175
o.push(i);}}- 118
return intersect(compact(o), opts).length !== 0;},//Formats in INSERT statement using the stored columns and values._insertSql: function () {- 1261
return this._clauseSql("insert");},//Formats an UPDATE statement using the stored values._updateSql: function () {- 229
return this._clauseSql("update");},//Formats the truncate statement. Assumes the table given has already been//literalized._truncateSql: function (table) {- 28
return "TRUNCATE TABLE" + table;},//Prepares an SQL statement by calling all clause methods for the given statement type._clauseSql: function (type) {- 5939
var sql = [("" + type).toUpperCase()];- 5939
try {- 5939
this._static[sql + "_CLAUSE_METHODS"].forEach(function (m) {- 59801
if (m.match("With")) {- 5654
this[m](sql);} else {- 54147
var sqlRet = this[m]();- 54145
if (sqlRet) {- 19157
sql.push(sqlRet);}}}, this);} catch (e) {- 2
throw e;}- 5937
return sql.join("");},//SQL fragment specifying the table to insert INTO_insertIntoSql: function (sql) {- 1270
return string.format(" INTO%s", this._sourceList(this.__opts.from));},//SQL fragment specifying the columns to insert into_insertColumnsSql: function (sql) {- 1260
var columns = this.__opts.columns, ret = "";- 1260
if (columns && columns.length) {- 1206
ret = " (" + columns.map(function (c) {- 6501
return c.toString(this);}, this).join(this._static.COMMA_SEPARATOR) + ")";}- 1260
return ret;},//SQL fragment specifying the values to insert._insertValuesSql: function () {- 1260
var values = this.__opts.values, ret = [];- 1260
if (isArray(values)) {- 1242
ret.push(values.length === 0 ? " DEFAULT VALUES" : " VALUES " + this.literal(values));- 18
} else if (isInstanceOf(values, Dataset)) {- 6
ret.push(" " + this._subselectSql(values));- 12
} else if (isInstanceOf(values, LiteralString)) {- 11
ret.push(" " + values.toString(this));} else {- 1
throw new QueryError("Unsupported INSERT values type, should be an array or dataset");}- 1259
return ret.join("");},//SQL fragment for Array_arraySql: function (a) {- 2020
return !a.length ? '(NULL)' : "(" + this.__expressionList(a) + ")";},//This method quotes the given name with the SQL standard double quote.//should be overridden by subclasses to provide quoting not matching the//SQL standard, such as backtick (used by MySQL and SQLite)._quotedIdentifier: function (name) {- 21
return string.format("\"%s\"", ("" + name).replace('"', '""'));},/*This section is for easier adapter overrides of sql formatting. These metthods are used by patio.sql.* toStringmethods to generate sql.*//*** @private For internal use by patio** SQL fragment for AliasedExpression*/aliasedExpressionSql: function (ae) {- 949
return this.__asSql(this.literal(ae.expression), ae.alias);},/*** @private For internal use by patio* SQL fragment for BooleanConstants* */booleanConstantSql: function (constant) {- 12
return this.literal(constant);},/*** @private For internal use by patio* SQL fragment for CaseExpression*/caseExpressionSql: function (ce) {- 2
var sql = ['(CASE '];- 2
if (ce.expression) {- 1
sql.push(this.literal(ce.expression), " ");}- 2
var conds = ce.conditions;- 2
if (isArray(conds)) {- 2
conds.forEach(function (cond) {- 2
sql.push(format("WHEN %s THEN %s", this.literal(cond[0]), this.literal(cond[1])));}, this);- 0
} else if (isHash(conds)) {- 0
for (var i in conds) {- 0
sql.push(format("WHEN %s THEN %s", this.literal(i), this.literal(conds[i])));}}- 2
return format("%s ELSE %s END)", sql.join(""), this.literal(ce.def));},/*** @private For internal use by patio* SQL fragment for the SQL CAST expression* */castSql: function (expr, type) {- 2
return string.format("CAST(%s AS %s)", this.literal(expr), this.db.castTypeLiteral(type));},/*** @private For internal use by patio* SQL fragment for specifying all columns in a given table**/columnAllSql: function (ca) {- 224
return string.format("%s.*", this.quoteSchemaTable(ca.table));},/*** @private For internal use by patio* SQL fragment for complex expressions**/complexExpressionSql: function (op, args) {- 6310
var newOp;- 6310
var isOperators = this._static.IS_OPERATORS, isLiterals = this._static.IS_LITERALS, l;- 6310
if ((newOp = isOperators[op]) != null) {- 306
var r = args[1], v = isNull(r) ? isLiterals.NULL : isLiterals[r];- 306
if (r == null || this.supportsIsTrue) {- 306
if (isUndefined(v)) {- 0
throw new QueryError(string.format("Invalid argument('%s') used for IS operator", r));}- 306
l = args[0];- 306
return string.format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp, v);- 0
} else if (op === "IS") {- 0
return this.complexExpressionSql("EQ", args);} else {- 0
return this.complexExpressionSql("OR",[BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],null)]);}- 6004
} else if (["IN", "NOTIN"].indexOf(op) !== -1) {- 17
var cols = args[0], vals = args[1], colArray = isArray(cols), valArray = false, emptyValArray = false;- 17
if (isArray(vals)) {- 13
valArray = true;- 13
emptyValArray = vals.length === 0;}- 17
if (colArray) {- 6
if (emptyValArray) {- 2
if (op === "IN") {- 1
return this.literal(BooleanExpression.fromValuePairs(cols.map(function (x) {- 2
return [x, x];}), "AND", true));} else {- 1
return this.literal({1: 1});}- 4
} else if (!this.supportsMultipleColumnIn) {- 0
if (valArray) {- 0
var expr = BooleanExpression.fromArgs(["OR"].concat(vals.map(function (vs) {- 0
return BooleanExpression.fromValuePairs(array.zip(cols, vs));})));- 0
return this.literal(op === "IN" ? expr : expr.invert());}} else {//If the columns and values are both arrays, use _arraySql instead of//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));}}else {- 11
if (emptyValArray) {- 1
if (op === "IN") {- 1
return this.literal(BooleanExpression.fromValuePairs([[cols, cols]], "AND", true));} else {- 0
return this.literal({1: 1});}} else {- 10
return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),ComplexExpression.IN_OPERATORS[op], this.literal(vals));}}- 5987
} else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {- 5038
l = args[0];- 5038
return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,this.literal(args[1]));- 949
} else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {- 915
return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));- 34
} else if (op === "NOT") {- 5
return string.format("NOT %s", this.literal(args[0]));- 29
} else if (op === "NOOP") {- 29
return this.literal(args[0]);} else {- 0
throw new QueryError("Invalid operator " + op);}},/*** @private For internal use by patio** SQL fragment for constants* */constantSql: function (constant) {- 6
return "" + constant;},/*** @private For internal use by patio** SQL fragment specifying an SQL function call* */functionSql: function (f) {- 709
var args = f.args;- 709
return string.format("%s%s", f.f, args.length === 0 ? '()' : this.literal(args));},/*** @private For internal use by patio* SQL fragment specifying a JOIN clause without ON or USING.* */joinClauseSql: function (jc) {- 951
var table = jc.table,tableAlias = jc.tableAlias;- 951
if (table === tableAlias) {- 37
tableAlias = null;}- 951
var tref = this.__tableRef(table);- 951
return string.format(" %s %s", this._joinTypeSql(jc.joinType), tableAlias ? this.__asSql(tref, tableAlias) : tref);},/*** @private For internal use by patio* SQL fragment specifying a JOIN clause with ON.**/joinOnClauseSql: function (jc) {- 832
return string.format("%s ON %s", this.joinClauseSql(jc), this.literal(this._filterExpr(jc.on)));},/*** @private For internal use by patio* SQL fragment specifying a JOIN clause with USING.**/joinUsingClauseSql: function (jc) {- 102
return string.format("%s USING (%s)", this.joinClauseSql(jc), this.__columnList(jc.using));},/*** @private For internal use by patio* SQL fragment for NegativeBooleanConstants.**/negativeBooleanConstantSql: function (constant) {- 2
return string.format("NOT %s", this.booleanConstantSql(constant));},/*** @private For internal use by patio** SQL fragment for the ordered expression, used in the ORDER BY* clause.*/orderedExpressionSql: function (oe) {- 73
var s = string.format("%s %s", this.literal(oe.expression), oe.descending ? "DESC" : "ASC");- 73
if (oe.nulls) {- 7
s = string.format("%s NULLS %s", s, oe.nulls === "first" ? "FIRST" : "LAST");}- 73
return s;},/*** @private For internal use by patio* SQL fragment for a literal string with placeholders* */placeholderLiteralStringSql: function (pls) {- 55
var args = pls.args;- 55
var s;- 55
if (isHash(args)) {- 6
for (var i in args) {- 10
args[i] = this.literal(args[i]);}- 6
s = string.format(pls.str, args);} else {- 49
s = pls.str.replace(this._static.QUESTION_MARK, "%s");- 49
args = toArray(args).map(this.literal, this);- 49
s = string.format(s, args);}- 55
if (pls.parens) {- 30
s = string.format("(%s)", s);}- 55
return s;},/*** @private For internal use by patio* SQL fragment for the qualifed identifier, specifying* a table and a column (or schema and table).*/qualifiedIdentifierSql: function (qcr) {- 4506
return [qcr.table, qcr.column].map(function (x) {- 9012
var isLiteral = [QualifiedIdentifier, Identifier, String].some(function (c) {- 25645
return x instanceof c;}), ret;- 9012
if (isLiteral) {- 1389
ret = this.literal(x);} else {- 7623
ret = this.quoteIdentifier(x);}- 9012
return ret;}, this).join('.');},/*** @private For internal use by patio** Adds quoting to identifiers (columns and tables). If identifiers are not* being quoted, returns name as a string. If identifiers are being quoted* quote the name with {@link patio.dataset._Sql#_quotedIdentifier}.*/quoteIdentifier: function (name) {- 32167
if (isInstanceOf(name, LiteralString)) {- 93
return name;} else {- 32074
if (isInstanceOf(name, Identifier)) {- 20702
name = name.value;}- 32074
name = this.inputIdentifier(name);- 32074
if (this.quoteIdentifiers) {- 25568
name = this._quotedIdentifier(name);}}- 32074
return name;},/*** @private For internal use by patio** Modify the identifier returned from the database based on the* identifierOutputMethod.*/inputIdentifier: function (v) {- 32246
var i = this.__identifierInputMethod;- 32246
v = v.toString(this);- 32246
return !isUndefinedOrNull(i) ?isFunction(v[i]) ?v[i]() :isFunction(comb[i]) ?comb[i](v): v: v;},/*** @private For internal use by patio** Modify the identifier sent to the database based on the* identifierOutputMethod.*/outputIdentifier: function (v) {- 22601
(v === '' && (v = 'untitled'));- 22601
var i = this.__identifierOutputMethod;- 22601
return !isUndefinedOrNull(i) ?isFunction(v[i]) ?v[i]() :isFunction(comb[i]) ?comb[i](v): v: v;},/*** @private For For internal use by patio** Separates the schema from the table and returns a string with them* quoted (if quoting identifiers)*/quoteSchemaTable: function (table) {- 2038
var parts = this.schemaAndTable(table);- 2038
var schema = parts[0];- 2038
table = parts[1];- 2038
return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));},/*** @private For For internal use by patio* Split the schema information from the table*/schemaAndTable: function (tableName) {- 2233
var sch = this.db ? this.db.defaultSchema || null : null;- 2233
if (isString(tableName)) {- 802
var parts = this._splitString(tableName);- 802
var s = parts[0], table = parts[1];- 802
return [s || sch, table];- 1431
} else if (isInstanceOf(tableName, QualifiedIdentifier)) {- 3
return [tableName.table, tableName.column];- 1428
} else if (isInstanceOf(tableName, Identifier)) {- 1428
return [null, tableName.value];} else {- 0
throw new QueryError("table should be a QualifiedIdentifier, Identifier, or String");}},/*** @private For For internal use by patio* SQL fragment for specifying subscripts (SQL array accesses)* */subscriptSql: function (s) {- 67
return string.format("%s[%s]", this.literal(s.f), this.__expressionList(s.sub));},/*** Do a simple join of the arguments (which should be strings) separated by commas* */__argumentList: function (args) {- 2
return args.join(this._static.COMMA_SEPARATOR);},/*** SQL fragment for specifying an alias. expression should already be literalized.*/__asSql: function (expression, alias) {- 1044
return string.format("%s AS %s", expression, this.quoteIdentifier(alias));},/*** Converts an array of column names into a comma seperated string of* column names. If the array is empty, a wildcard (*) is returned.*/__columnList: function (columns) {- 4905
return (!columns || columns.length === 0) ? this._static.WILDCARD : this.__expressionList(columns);},/*** The alias to use for datasets, takes a number to make sure the name is unique.* */_datasetAlias: function (number) {- 96
return this._static.DATASET_ALIAS_BASE_NAME + number;},/*** Converts an array of expressions into a comma separated string of* expressions.*/__expressionList: function (columns) {- 4287
return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);},//Format the timestamp based on the default_timestamp_format, with a couple//of modifiers. First, allow %N to be used for fractions seconds (if the//database supports them), and override %z to always use a numeric offset//of hours and minutes.formatTimestamp: function (v, format) {- 33
return this.literal(patio.dateToString(v, format));},/*** SQL fragment specifying a JOIN type, splits a camelCased join type* and converts to uppercase/*/_joinTypeSql: function (joinType) {- 948
return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g, function (m) {- 1209
return m.toUpperCase() + " ";}).trimRight() + " JOIN";},/*Methods for converting types to a SQL .*//*** @return SQL fragment for a type of object not handled by {@link patio.dataset._Sql#literal}.* If object has a method sqlLiteral then it is called with this dataset as the first argument,* otherwise raises an error. Classes implementing sqlLiteral should call a class-specific method* on the dataset provided and should add that method to {@link patio.dataset.Dataset}, allowing for adapters* to provide customized literalizations.* If a database specific type is allowed, this should be overriden in a subclass.*/_literalOther: function (v) {- 4
if (isFunction(v.sqlLiteral)) {- 1
return v.sqlLiteral(this);} else {- 2
throw new QueryError(string.format("can't express %j as a SQL literal", [v]));}},/***@return SQL fragment for Buffer, treated as an expression* */_literalBuffer: function (b) {- 3
return "X'" + b.toString("hex") + "'";},/***@return SQL fragment for Hash, treated as an expression* */_literalObject: function (v) {- 6
return this._literalExpression(BooleanExpression.fromValuePairs(v));},/*** @return SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.*/_literalArray: function (v) {- 2019
return Expression.isConditionSpecifier(v) ? this._literalExpression(BooleanExpression.fromValuePairs(v)) : this._arraySql(v);},/*** @return SQL fragment for a number.*/_literalNumber: function (num) {- 6324
var ret = "" + num;- 6324
if (isNaN(num) || num === Infinity) {- 0
ret = string.format("'%s'", ret);}- 6324
return ret;},/*** @return SQL fragment for Dataset. Does a subselect inside parantheses.*/_literalDataset: function (dataset) {- 115
return string.format("(%s)", this._subselectSql(dataset));},/*** @return SQL fragment for Date, using the ISO8601 format.*/_literalDate: function (date) {- 7
return (this.requiresSqlStandardDateTimes ? "DATE '" : "'") + patio.dateToString(date) + "'";},/***@return SQL fragment for a year.*/_literalYear: function (o) {- 1
return patio.dateToString(o, this._static.YEAR_FORMAT);},/***@return SQL fragment for a timestamp, using the ISO8601 format.*/_literalTimestamp: function (v) {- 31
return this.formatTimestamp(v, this._static.TIMESTAMP_FORMAT);},/***@return SQL fragment for a timestamp, using the ISO8601 format.*/_literalTime: function (v) {- 2
return this.formatTimestamp(v, this._static.TIME_FORMAT);},/*** @return SQL fragment for a boolean.*/_literalBoolean: function (b) {- 107
return b ? this._static.BOOL_TRUE : this._static.BOOL_FALSE;},/*** @return SQL fragment for SQL::Expression, result depends on the specific type of expression.* */_literalExpression: function (v) {- 27216
return v.toString(this);},/***@return SQL fragment for Hash, treated as an expression* */_literalHash: function (v) {- 0
return this._literalExpression(BooleanExpression.fromValuePairs(v));},/**@return SQL fragment for null*/_literalNull: function () {- 227
return this._static.NULL;},/*** @return SQL fragment for String. Doubles \ and ' by default.* */_literalString: function (v) {- 173
var parts = this._splitString(v);- 173
var table = parts[0], column = parts[1], alias = parts[2], ret;- 173
if (!alias) {- 173
if (column && table) {- 1
ret = this._literalExpression(QualifiedIdentifier.fromArgs([table, column]));} else {- 172
ret = "'" + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'";}} else {- 0
if (column && table) {- 0
ret = new AliasedExpression(QualifiedIdentifier.fromArgs([table, column]), alias);} else {- 0
ret = new AliasedExpression(new Identifier(column), alias);}- 0
ret = this.literal(ret);}- 173
return ret;},/*** @return SQL fragment for json. Doubles ' by default.* */_literalJson: function (v) {- 0
throw new QueryError("Json not supported.");},/*SQL STATEMENT CREATION METHODS*/_selectQualifySql: function () {- 4440
var o = this.__opts;- 4440
var table = this.__opts.alwaysQualify;- 4440
if (table && !o.sql) {- 2
array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) {- 2
o[k] = this._qualifiedExpression(o[k], table);}, this);- 2
if (!o.select || isEmpty(o.select)) {- 2
o.select = [new ColumnAll(table)];}}},_deleteQualifySql: function () {- 750
return this._selectQualifySql.apply(this, arguments);},/*** @return the columns selected* */_selectColumnsSql: function () {- 3690
return " " + this.__columnList(this.__opts.select);},/**@return the DISTINCT clause.*/_selectDistinctSql: function () {- 3690
var distinct = this.__opts.distinct, ret = [];- 3690
if (distinct) {- 10
ret.push(" DISTINCT");- 10
if (distinct.length) {- 4
ret.push(format(" ON (%s)", this.__expressionList(distinct)));}}- 3690
return ret.join("");},/*** @return the EXCEPT, INTERSECT, or UNION clause.* This uses a subselect for the compound datasets used, because using parantheses doesn't* work on all databases.**/_selectCompoundsSql: function () {- 3690
var opts = this.__opts, compounds = opts.compounds, ret = [];- 3690
if (compounds) {- 49
compounds.forEach(function (c) {- 49
var type = c[0], dataset = c[1], all = c[2];- 49
ret.push(string.format(" %s%s %s", type.toUpperCase(), all ? " ALL" : "", this._subselectSql(dataset)));}, this);}- 3690
return ret.join("");},/*** @return the sql to add the list of tables to select FROM**/_selectFromSql: function () {- 3710
var from = this.__opts.from;- 3710
return from ? string.format(" %s%s", this._static.FROM, this._sourceList(from)) : "";},/*** @return the GROUP BY clause**/_selectGroupSql: function () {- 3690
var group = this.__opts.group;- 3690
return group ? string.format(" GROUP BY %s", this.__expressionList(group)) : "";},/***@return the sql to add the filter criteria in the HAVING clause**/_selectHavingSql: function () {- 3690
var having = this.__opts.having;- 3690
return having ? string.format(" HAVING %s", this.literal(having)) : "";},/*** @return the JOIN clause.**/_selectJoinSql: function () {- 3693
var join = this.__opts.join, ret = [];- 3693
if (join) {- 503
join.forEach(function (j) {- 951
ret.push(this.literal(j));}, this);}- 3693
return ret.join("");},/*** @return the LIMIT and OFFSET clauses.* */_selectLimitSql: function () {- 3690
var ret = [], limit = this.__opts.limit, offset = this.__opts.offset;- 3690
!isUndefined(limit) && !isNull(limit) && (ret.push(format(" LIMIT %s", this.literal(limit))));- 3690
!isUndefined(offset) && !isNull(offset) && (ret.push(format(" OFFSET %s", this.literal(offset))));- 3690
return ret.join("");},/*** @return SQL for different locking modes.**/_selectLockSql: function () {- 3689
var lock = this.__opts.lock, ret = [];- 3689
if (lock) {- 3
if (lock === "update") {- 2
ret.push(this._static.FOR_UPDATE);} else {- 1
ret.push(" ", lock);}}- 3689
return ret.join("");},/*** @return the SQL ORDER BY clause fragment.*/_selectOrderSql: function () {- 3704
var order = this.__opts.order;- 3704
return order ? string.format(" ORDER BY %s", this.__expressionList(order)) : "";},/*** @return the SQL WHERE clause fragment.*/_selectWhereSql: function () {- 4669
var where = this.__opts.where;- 4669
return where ? string.format(" WHERE %s", this.literal(where)) : "";},/*** @return SQL WITH clause fragment.* @param sql*/_selectWithSql: function (sql) {- 5654
var wit = this.__opts["with"];- 5654
if (wit && wit.length) {//sql.length = 0;- 8
var base = sql.join("");- 8
sql.length = 0;- 8
sql.push([this._selectWithSqlBase(), wit.map(function (w) {- 11
return [this.quoteIdentifier(w.name),(w.args ? ("(" + this.__argumentList(w.args) + ")") : "")," AS ",this._literalDataset(w.dataset)].join("");}, this).join(this._static.COMMA_SEPARATOR), base].join(" "));}},_deleteWithSql: function () {- 730
return this._selectWithSql.apply(this, arguments);},_insertWithSql: function () {- 1120
return this._selectWithSql.apply(this, arguments);},_updateWithSql: function () {- 202
return this._selectWithSql.apply(this, arguments);},_insertReturningSql: function (sql) {- 2051
var opts = this.__opts, ret = "";- 2051
if (opts.hasOwnProperty("returning")) {- 1113
return [this._static.RETURNING, this.__columnList(array.toArray(opts.returning))].join("");}- 938
return ret;},_deleteReturningSql: function () {- 730
return this._insertReturningSql.apply(this, arguments);},_updateReturningSql: function () {- 202
return this._insertReturningSql.apply(this, arguments);},/*** @return The base keyword to use for the SQL WITH clause**/_selectWithSqlBase: function () {- 8
return this._static.SQL_WITH;},/*** @see patio.dataset._Sql#_selectFromSql*/_deleteFromSql: function () {- 20
return this._selectFromSql();},/*** @see patio.dataset._Sql#_selectOrderSql*/_deleteOrderSql: function () {- 12
return this._selectOrderSql();},/*** @see patio.dataset._Sql#_selectWhereSql*/_deleteWhereSql: function () {- 750
return this._selectWhereSql();},/*** @see patio.dataset._Sql#_selectOrderSql*/_updateOrderSql: function () {- 2
return this._selectOrderSql();},/*** @see patio.dataset._Sql#_selectWhereSql*/_updateWhereSql: function () {- 229
return this._selectWhereSql();},/*** @return SQL fragment specifying the tables to delete from.* Includes join table if modifying joins is allowed.*/_updateTableSql: function (sql) {- 27
var ret = [this._sourceList(this.__opts.from)];- 27
if (this.supportsModifyingJoins) {- 3
ret.push(this._selectJoinSql());}- 27
return ret.join("");},/*** @returns The SQL fragment specifying the columns and values to SET.* */_updateSetSql: function () {- 229
var values = this.__opts.values, defs = this.__opts.defaults, overrides = this.__opts.overrides;- 229
var st = [" SET "];- 229
if (isArray(values)) {- 229
var v = [], mergedDefsAndOverrides = false, length = values.length, ident, val;- 229
for (var i = 0; i < length; i++) {- 225
val = values[i];- 225
if (isHash(val)) {- 221
mergedDefsAndOverrides = true;- 221
val = merge({}, defs || {}, val);- 221
val = merge({}, val, overrides || {});- 221
for (var j in val) {- 240
ident = this.stringToIdentifier(j);- 240
v.push(this.quoteIdentifier(ident) + " = " + this.literal(val[j]));}- 4
} else if (isInstanceOf(val, Expression)) {- 2
v.push(this._literalExpression(val).replace(/^\(|\)$/g, ""));} else {- 2
v.push(val);}}- 229
if (!mergedDefsAndOverrides) {- 8
val = merge({}, defs || {});- 8
val = merge({}, val, overrides || {});- 8
for (i in val) {- 8
ident = this.stringToIdentifier(i);- 8
v.push(this.quoteIdentifier(ident) + " = " + this.literal(val[i]));}}- 229
st.push(v.join(this._static.COMMA_SEPARATOR));} else {- 0
st.push(values);}- 229
return st.join("");},/*** Converts an array of source names into into a comma separated list.**/_sourceList: function (source) {- 5933
if (!Array.isArray(source)) {- 731
source = [source];}- 5933
if (!source || !source.length) {- 0
throw new QueryError("No source specified for the query");}- 5933
return " " + source.map(function (s) {- 6121
return this.__tableRef(s);}, this).join(this._static.COMMA_SEPARATOR);},/*** @return SQL to use if this dataset uses static SQL. Since static SQL* can be a PlaceholderLiteralString in addition to a String,* we literalize nonstrings.**/_staticSql: function (sql) {- 51
return isString(sql) ? sql : this.literal(sql);},/*** @return SQL fragment for a subselect using the given database's SQL.**/_subselectSql: function (ds) {- 170
return ds.sql;},/*** @returns SQL fragment specifying a table name.**/__tableRef: function (t) {- 7072
return isString(t) ? this._quotedIdentifier(t) : this.literal(t);},//Raise an InvalidOperation exception if deletion is not allowed//for this dataset__checkModificationAllowed: function () {- 2308
if (this.__opts.group) {- 8
throw new QueryError("Grouped datasets cannot be modified");}- 2300
if (!this.supportsModifyingJoins && this._joinedDataset) {- 8
throw new QueryError("Joined datasets cannot be modified");}},__toAliasedTableName: function (alias) {- 2
var ret;- 2
if (isString(alias)) {- 1
ret = alias;- 1
} else if (isInstanceOf(alias, Identifier)) {- 1
ret = alias.value;} else {- 0
throw new QueryError("Invalid table alias");}- 2
return ret;},getters: {//Same as selectS, not aliased directly to make subclassing simpler.sql: function () {- 665
return this.selectSql;},selectSql: function () {- 3741
var selectSql;- 3741
if (this.__opts.sql) {- 51
selectSql = this._staticSql(this.__opts.sql);} else {- 3690
selectSql = this._clauseSql("select");}- 3741
return selectSql;},deleteSql: function () {- 754
var opts = this.__opts;- 754
if (opts.sql) {- 0
return this._staticSql(this.sql);} else {- 754
this.__checkModificationAllowed();- 750
return this._clauseSql("delete");}},truncateSql: function () {- 32
if (this.__opts.sql) {- 0
return this._staticSql(this.__opts.sql);} else {- 32
this.__checkModificationAllowed();- 28
if (this.__opts.where) {- 0
throw new QueryError("cant truncate filtered datasets");}- 28
return this._truncateSql(this._sourceList(this.__opts.from));}},exists: function () {- 6
return new LiteralString("EXISTS (" + this.selectSql + ")");},//Whether this dataset is a joined dataset_joinedDataset: function () {- 158
var from = this.__opts.from;- 158
return (isArray(from) && from.length > 1) || this.__opts.join;}}},static: {/**@lends patio.Dataset*//*** Default FROM clause*/FROM: "FROM",/*** Default SQL AND separator.*/AND_SEPARATOR: " AND ",/*** Default SQL boolean false operator.*/BOOL_FALSE: "'f'",/*** Default SQL boolean true operator.*/BOOL_TRUE: "'t'",/*** Default SQL comma sperator.*/COMMA_SEPARATOR: ', ',/*** Default COUNT expression.*/COUNT_OF_ALL_AS_COUNT: sql.count(sql.literal('*')).as("count"),/*** Default alias for datasets.*/DATASET_ALIAS_BASE_NAME: 't',/*** Default FOR UPDATE SQL fragment.*/FOR_UPDATE: ' FOR UPDATE',/*** Hash of IS literals*/IS_LITERALS: {NULL: 'NULL', true: 'TRUE', false: 'FALSE'},/*** Defaults IS OPERATORS. See {@link patio.sql.ComplexExpression.IS_OPERATORS}.*/IS_OPERATORS: ComplexExpression.IS_OPERATORS,/*** Defaults N(Multi arity) OPERATORS. See {@link patio.sql.ComplexExpression.N_ARITY_OPERATORS}.*/N_ARITY_OPERATORS: ComplexExpression.N_ARITY_OPERATORS,/*** Defaults TWO OPERATORS. See {@link patio.sql.ComplexExpression.TWO_ARITY_OPERATORS}.*/TWO_ARITY_OPERATORS: ComplexExpression.TWO_ARITY_OPERATORS,/*** Defaults SQL NULL.*/NULL: "NULL",/*** Default SQL clauses that need qualifying. This may be overrode by adapters.*/QUALIFY_KEYS: ["select", "where", "having", "order", "group"],/*** Regexp used to replace '?' in {@link patio.sql.PlaceHolderLiteralString}*/QUESTION_MARK: /\?/g,/*** Default SQL DELETE clause methods. This may be overrode by adapters.*/DELETE_CLAUSE_METHODS: clauseMethods("delete", "qualify from where"),/*** Default SQL INSERT clause. This may be overrode by adapters.*/INSERT_CLAUSE_METHODS: clauseMethods("insert", "into columns values"),/*** Default SQL SELECT clause. This may be overrode by adapters.*/SELECT_CLAUSE_METHODS: clauseMethods("select", "qualify with distinct columns from join where group having compounds order limit lock"),/*** Default SQL UPDATE clause. This may be overrode by adapters.*/UPDATE_CLAUSE_METHODS: clauseMethods("update", "table set where"),/*** Default SQL '*' literal string.*/WILDCARD: new LiteralString('*'),/*** Default SQL 'RETURNING' literal string*/RETURNING: " RETURNING ",/*** Default SQL WITH base. This may be overrode by adapters.*/SQL_WITH: "WITH",/*** Default space to use when building SQL queries*/SPACE: " ",clauseMethods: clauseMethods}}).as(module);
|
database/query.js
|
Coverage93.19
SLOC1100
LOC323
Missed22
|
- 1
var comb = require("comb"),define = comb.define,merge = comb.merge,hitch = comb.hitch,when = comb.when,isBoolean = comb.isBoolean,isEmpty = comb.isEmpty,isArray = comb.isArray,isUndefined = comb.isUndefined,isPromiseLike = comb.isPromiseLike,isUndefinedOrNull = comb.isUndefinedOrNull,argsToArray = comb.argsToArray,isFunction = comb.isFunction,format = comb.string.format,Promise = comb.Promise,isNull = comb.isNull,Queue = comb.collections.Queue,sql = require("../sql").sql,PromiseList = comb.PromiseList,errors = require("../errors"),NotImplemented = errors.NotImplemented,stream = require("stream"),PassThroughStream = stream.PassThrough,utils = require("../utils"),pipeAll = utils.pipeAll,resolveOrPromisfyFunction = utils.resolveOrPromisfyFunction;- 1
var Database = define(null, {instance: {/**@lends patio.Database.prototype*//*** The method name to invoke on a connection. The method name* should be overrode by an adapter if the method to execute* a query is different for the adapter specific connection class.*/connectionExecuteMethod: "execute",/*** The <b>BEGIN</b> SQL fragment used to signify the start of a transaciton.*/SQL_BEGIN: 'BEGIN',/*** The <b>COMMIT</b> SQL fragment used to signify the end of a transaction and the final commit.*/SQL_COMMIT: 'COMMIT',/*** The <b>RELEASE SAVEPOINT</b> SQL fragment used by trasactions when using save points.* The adapter should override this SQL fragment if the adapters SQL is different.* <p>* <b>This fragment will not be used if {@link patio.Database#supportsSavepoints} is false.</b>* </p>*/SQL_RELEASE_SAVEPOINT: 'RELEASE SAVEPOINT autopoint_%d',/*** The <b>ROLLBACK</b> SQL fragment used to rollback a database transaction.* This should be overrode by adapters if the SQL for the adapters* database is different.*/SQL_ROLLBACK: 'ROLLBACK',/*** The <b>ROLLBACK TO SAVEPOINT</b> SQL fragment used to rollback a database transaction* to a particular save point.* This should be overrode by adapters if the SQL for the adapters* database is different.** <p>* <b>This fragment will not be used if {@link patio.Database#supportsSavepoints} is false.</b>* </p>*/SQL_ROLLBACK_TO_SAVEPOINT: 'ROLLBACK TO SAVEPOINT autopoint_%d',/*** The <b>SAVEPOINT</b> SQL fragment used for creating a save point in a* database transaction.* <p>* <b>This fragment will not be used if {@link patio.Database#supportsSavepoints} is false.</b>* </p>*/SQL_SAVEPOINT: 'SAVEPOINT autopoint_%d',/*** Object containing different database transaction isolation levels.* This object is used to look up the proper SQL when starting a new transaction* and setting the isolation level in the options.* @field*/TRANSACTION_ISOLATION_LEVELS: {uncommitted: 'READ UNCOMMITTED',committed: 'READ COMMITTED',repeatable: 'REPEATABLE READ',serializable: 'SERIALIZABLE'},/*** @ignore*/POSTGRES_DEFAULT_RE: /^(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))$/,/*** @ignore*/MSSQL_DEFAULT_RE: /^(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))$/,/*** @ignore*/MYSQL_TIMESTAMP_RE: /^CURRENT_(?:DATE|TIMESTAMP)?$/,/*** @ignore*/STRING_DEFAULT_RE: /^'(.*)'$/,/*** @ignore*/POSTGRES_TIME_PATTERN: "HH:mm:ss",/*** @ignore*/POSTGRES_DATE_TIME_PATTERN: "yyyy-MM-dd HH:mm:ss.SSZ",__transactions: null,/*** @ignore*/constructor: function () {- 120
this._super(arguments);- 120
this.__transactions = [];- 120
this.__transactionQueue = new Queue();},/*** Executes the given SQL on the database. This method should be implemented by adapters.* <b>This method should not be called directly by user code.</b>*/execute: function (sql, opts, conn) {- 5288
var ret;- 5288
if (opts.stream) {- 5
ret = this.__executeStreamed(sql, opts, conn);} else {- 5282
ret = this.__executePromised(sql, opts, conn);}- 5287
return ret;},__executeStreamed: function (sql, opts, conn) {- 10
var self = this, ret;- 10
if (conn) {- 5
var cleanUp = function cleanUp(err) {- 5
if (err) {- 2
conn.errored = true;}- 5
self._returnConnection(conn);- 5
ret.removeListener("end", cleanUp);- 5
ret.removeListener("error", cleanUp);- 5
self = conn = sql = ret = null;};- 5
ret = this.__logAndExecute(sql, opts, function () {- 5
return conn.stream(sql, opts);});- 5
ret.on("end", cleanUp);- 5
ret.on("error", cleanUp);} else {- 5
ret = new PassThroughStream({objectMode: true});- 5
this._getConnection().chain(function (conn) {- 5
function fieldHandler(fields) {- 3
ret.emit("fields", fields);- 3
queryStream.removeListener("fields", fieldHandler);- 3
queryStream = self = ret = null;}- 5
var queryStream = self.__executeStreamed(sql, opts, conn);- 5
queryStream.on("fields", fieldHandler);- 5
pipeAll(queryStream, ret);}, function (err) {- 0
ret.emit("error", err);});}- 10
return ret;},__executePromised: function (sql, opts, conn) {- 6746
var self = this, ret;- 6746
if (conn) {- 5282
ret = this.__logAndExecute(sql, opts, function () {- 5277
return conn[opts.stream ? "stream" : "query"](sql, opts);});- 5282
ret.both(function () {- 5282
var ret = self._returnConnection(conn);- 5282
self = conn = sql = null;- 5282
return ret;});} else {- 1464
ret = this._getConnection().chain(function (conn) {- 1464
return self.__executePromised(sql, opts, conn);});}- 6746
return ret;},/*** Return a Promise that is resolved with an object containing index information.* <p>* The keys are index names. Values are objects with two keys, columns and unique. The value of columns* is an array of column names. The value of unique is true or false* depending on if the index is unique.* </p>** <b>Should not include the primary key index, functional indexes, or partial indexes.</b>** @example* DB.indexes("artists").chain(function(indexes){* //e.g. indexes === {artists_name_ukey : {columns : [name], unique : true}};* })**/indexes: function (table, opts) {- 1
throw new NotImplemented("indexes should be overridden by adapters");},/*** Proxy for {@link patio.Dataset#get}.*/get: function () {- 35
return this.dataset.get.apply(this.dataset, arguments);},/*** @ignore* //todo implement prepared statements** Call the prepared statement with the given name with the given object* of arguments.** DB.from("items").filter({id : 1}).prepare("first", "sa");* DB.call("sa") //=> SELECT * FROM items WHERE id = 1* */call: function (psName, hash) {- 0
hash = hash || {};- 0
this.preparedStatements[psName](hash);},/*** Method that should be used when submitting any DDL (Data DefinitionLanguage) SQL,* such as {@link patio.Database#createTable}. By default, calls {@link patio.Database#executeDui}.* <b>This method should not be called directly by user code.</b>*/executeDdl: function (sql, opts) {- 629
opts = opts || {};- 629
return this.executeDui(sql, opts);},/*** Method that should be used when issuing a DELETE, UPDATE, or INSERT* statement. By default, calls {@link patio.Database#execute}.* <b>This method should not be called directly by user code.</b>**/executeDui: function (sql, opts) {- 2760
opts = opts || {};- 2760
return this.execute(sql, opts);},/*** Method that should be used when issuing a INSERT* statement. By default, calls {@link patio.Database#executeDui}.* <b>This method should not be called directly by user code.</b>**/executeInsert: function (sql, opts) {- 1117
opts = opts || {};- 1117
return this.executeDui(sql, opts);},/*** Runs the supplied SQL statement string on the database server..** @example* DB.run("SET some_server_variable = 42")** @param {String} sql the SQL to run.* @return {Promise} a promise that is resolved with the result of the query.**/run: function (sql, opts) {- 53
opts = opts || {};- 53
return this.executeDdl(sql, opts);},/*** Parse the schema from the database.** @example** DB.schema("artists").chain(function(schema){* //example schema* {* id : {* type : "integer",* primaryKey : true,* "default" : "nextval('artist_id_seq'::regclass)",* jsDefault : null,* dbType : "integer",* allowNull : false* },* name : {* type : "string",* primaryKey : false,* "default" : null,* jsDefault : null,* dbType : "text",* allowNull : false* }* }* })** @param {String|patio.sql.Identifier|patio.sql.QualifiedIdentifier} table the table to get the schema for.* @param {Object} [opts=null] Additinal options.* @param {boolean} [opts.reload=false] Set to true to ignore any cached results.* @param {String|patio.sql.Identifier} [opts.schema] An explicit schema to use. It may also be implicitly provided* via the table name.** @return {Promise} Returns a Promise that is resolved with the schema for the given table as an object* where the key is the column name and the value is and object containg column information. The default* column information returned.* <ul>* <li>allowNull : Whether NULL is an allowed value for the column.</li>* <li>dbType : The database type for the column, as a database specific string.</li>* <li>"default" : The database default for the column, as a database specific string.</li>* <li>primaryKey : Whether the columns is a primary key column. If this column is not present,* it means that primary key information is unavailable, not that the column* is not a primary key.</li>* <li>jsDefault : The database default for the column, as a javascript object. In many cases, complex* database defaults cannot be parsed into javascript objects.</li>* <li>type : A string specifying the type, such as "integer" or "string".</li>* <ul>**/schema: function (table, opts) {- 108
if (!isFunction(this.schemaParseTable)) {- 0
throw new Error("Schema parsing is not implemented on this database");}- 108
opts = opts || {};- 108
var schemaParts = this.__schemaAndTable(table);- 108
var sch = schemaParts[0], tableName = schemaParts[1];- 108
var quotedName = this.__quoteSchemaTable(table);- 108
opts = sch && !opts.schema ? merge({schema: sch}, opts) : opts;- 108
if (opts.reload) {- 1
delete this.schemas[quotedName];}- 108
var self = this;- 108
return this.schemaParseTable(tableName, opts).chain(function (cols) {- 108
if (!cols || cols.length === 0) {- 0
throw new Error("Error parsing schema, " + table + " no columns returns, table probably doesnt exist");} else {- 108
var schema = {};- 108
cols.forEach(function (c) {- 807
var name = c[0];- 807
c = c[1];- 807
c.jsDefault = self.__columnSchemaToJsDefault(c["default"], c.type);- 807
schema[name] = c;});- 108
return schema;}});},/*** Remove the cached schema for the given table name* @example* DB.schema("artists").chain(function(){* DB.removeCachedSchema("artists");* });* @param {String|patio.sql.Identifier|patio.sql.QualifiedIdentifier} the table to remove from this* databases cached schemas.*/removeCachedSchema: function (table) {- 496
if (this.schemas && !isEmpty(this.schemas)) {- 0
delete this.schemas[this.__quoteSchemaTable(table)];}},/*** Determine if a table exists.* @example* comb.executeInOrder(DB, function(DB){* return {* table1Exists : DB.tableExists("table1"),* table2Exists : DB.tableExists("table2")* };* }).chain(function(ret){* //ret.table1Exists === true* //ret.table2Exists === false* });* @param {String|patio.sql.Identifier|patio.sql.QualifiedIdentifier} the table to remove from this** @return {Promise} a promise resolved with a boolean indicating if the table exists.*/tableExists: function (table, cb) {- 2
return this.from(table).first().chain(function () {- 1
return true;}, function () {- 1
return false;}).classic(cb).promise();},/*** Returns a promise with a list of tables names in this database. This method* should be implemented by the adapter.** @example* DB.tables().chain(function(tables){* //e.g. tables === ["table1", "table2", "table3"];* });** @return {Promise} a promise that is resolved with a list of tablenames.*/tables: function () {- 1
throw new NotImplemented("tables should be implemented by the adapter");},/*** Starts a database transaction. When a database transaction is used,* either all statements are successful or none of the statements are* successful.* <p>* <b>Note</b> that MySQL MyISAM tables do not support transactions.</p>* </p>** ```* //normal transaction* DB.transaction(function() {* return comb.when(* this.execute('DROP TABLE test;'),* this.execute('DROP TABLE test2;')* );* });** //transaction with a save point.* DB.transaction(function() {* return this.transaction({savepoint : true}, function() {* return comb.when(* this.execute('DROP TABLE test;'),* this.execute('DROP TABLE test2;')* );* });*});* ```** Using a promise.** ```* var ds = db.from("user");* db.transaction(function(){* return ds.insert({* firstName:"Jane",* lastName:"Gorgenson",* password:"password",* dateOfBirth:new Date(1956, 1, 3)* }).chain(function(){* return ds.forEach(function(user){* return ds.where({id:user.id}).update({firstName:user.firstName + 1});* });* });* });* ```** Using the done method* ```* var ds = db.from("user");* db.transaction(function(db, done){* ds.insert({* firstName:"Jane",* lastName:"Gorgenson",* password:"password",* dateOfBirth:new Date(1956, 1, 3)* }).chain(function(){* ds.forEach(function(user){* return ds.where({id:user.id}).update({firstName:user.firstName + 1});* }).classic(done)* });* });* ```** ```* //WITH ISOLATION LEVELS** db.supportsTransactionIsolationLevels = true;* //BEGIN* //SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED* //DROP TABLE test1'* //COMMIT* DB.transaction({isolation:"uncommited"}, function(d) {* return d.run("DROP TABLE test1");* });** //BEGIN* //SET TRANSACTION ISOLATION LEVEL READ COMMITTED* //DROP TABLE test1* //COMMIT* DB.transaction({isolation:"committed"}, function(d) {* return d.run("DROP TABLE test1");* });** //BEGIN* //SET TRANSACTION ISOLATION LEVEL REPEATABLE READ'* //DROP TABLE test1* //COMMIT* DB.transaction({isolation:"repeatable"}, function(d) {* return d.run("DROP TABLE test1");* });** //BEGIN* //SET TRANSACTION ISOLATION LEVEL SERIALIZABLE* //DROP TABLE test1* //COMMIT* DB.transaction({isolation:"serializable"}, function(d) {* return d.run("DROP TABLE test1");* });** //With an Error* //BEGIN* //DROP TABLE test* //ROLLBACK* DB.transaction(function(d) {* d.execute('DROP TABLE test');* throw "Error";* });* ```** @param {Object} [opts={}] options to use when performing the transaction.* @param {String} [opts.isolated] This will ensure that the transaction will be run on its own connection and* not part of another transaction if one is already in progress.* @param {String} [opts.isolation] The transaction isolation level to use for this transaction,* should be "uncommitted", "committed", "repeatable", or "serializable",* used if given and the database/adapter supports customizable* transaction isolation levels.* @param {String} [opts.prepare] A string to use as the transaction identifier for a* prepared transaction (two-phase commit), if the database/adapter* supports prepared transactions.* @param {Boolean} [opts.savepoint] Whether to create a new savepoint for this transaction,* only respected if the database/adapter supports savepoints. By* default patio will reuse an existing transaction, so if you want to* use a savepoint you must use this option.* @param {Function} cb a function used to perform the transaction. This function is* called in the scope of the database by default so one can use this. The funciton is also* called with the database as the first argument, and a function to be called when the tranaction is complete.* If you return a promise from the transaction block then you do not need to call the done cb.*** @return {Promise} a promise that is resolved once the transaction is complete.**/transaction: function (opts, cb) {- 904
if (isFunction(opts)) {- 150
cb = opts;- 150
opts = {};} else {- 754
opts = opts || {};}- 904
var ret;- 904
if (!this.__alreadyInTransaction) {- 835
this.__alreadyInTransaction = true;- 835
ret = this.__transaction(null, opts, cb);} else {- 69
ret = this.__enqueueTransaction(opts, cb);}- 904
return ret.promise();},__enqueueTransaction: function (opts, cb) {- 73
var ret = new Promise(), self = this;- 73
var transaction = function () {- 73
self.__transaction(null, opts, cb).chain(ret.callback, ret.errback);};- 73
this.__transactionQueue.enqueue(transaction);- 73
return ret.promise();},__transactionProxy: function (cb, conn) {- 915
var promises = [];- 915
var repl = [];//set up our proxy methos- 915
["transaction", "execute"].forEach(function (n) {- 1830
var orig = this[n];- 1830
repl.push({name: n, orig: orig});- 1830
this[n] = function (arg1, arg2) {- 5710
var ret;- 5710
try {- 5710
if (n === "transaction") {- 1726
if (comb.isHash(arg1) && arg1.isolated) {- 4
return this.__enqueueTransaction(arg1, arg2);} else {//if its a transaction with no options then we just create a promise from what ever is returned- 1722
if (isFunction(arg1)) {- 1
ret = resolveOrPromisfyFunction(arg1, this, this);} else {- 1721
if (this.supportsSavepoints && arg1.savepoint) {//if we support save points there is a save point option then we//use __transaction again with the previous connection- 7
ret = this.__transaction(conn, arg1, arg2);} else {//other wise use the function passed in to get the returned promise- 1714
ret = resolveOrPromisfyFunction(arg2, this, this);}}}} else {- 3984
ret = orig.apply(this, argsToArray(arguments).concat(conn));}} catch (e) {- 0
ret = new Promise().errback(e);}- 5706
if (comb.isInstanceOf(ret, stream.Stream)) {- 0
promises.push(comb.promisfyStream(ret));} else {- 5706
promises.push((ret = when(ret)));- 5706
ret = ret.promise();}- 5706
return ret;};}, this);- 915
try {- 915
promises.push(resolveOrPromisfyFunction(cb, this, this));} catch (e) {- 8
promises.push(new Promise().errback(e));}- 915
if (promises.length === 0) {- 0
promises.push(new Promise().callback());}- 915
var self = this;- 915
return new PromiseList(promises).both(function () {- 915
repl.forEach(function (o) {- 1830
self[o.name] = o.orig;});}).promise();},_getConnection: function () {- 2380
return this.pool.getConnection();},_returnConnection: function (conn) {- 5289
if (!this.alreadyInTransaction(conn)) {- 1471
this.pool.returnConnection(conn);}},__getTransactionConnection: function (conn) {- 915
var self = this;- 915
return conn ? when(conn) : this._getConnection().chain(function (conn) {//add the connection to the transactions- 908
self.__transactions.push(conn);//reset transaction depth to 0, this is used for keeping track of save points.- 908
conn.__transactionDepth = 0;- 908
return conn;});},__transaction: function (conn, opts, cb) {- 915
var promise = new Promise();- 915
try {- 915
var self = this;- 915
return this.__getTransactionConnection(conn).chain(function (conn) {- 915
return self.__beginTransaction(conn, opts).chain(function () {- 915
return self.__transactionProxy(cb, conn).chain(function () {- 899
return self.__commitTransaction(conn).chain(function (res) {- 896
self.__finishTransactionAndCheckForMore(conn);- 896
return res;},function errorHandler(err) {- 3
self.__finishTransactionAndCheckForMore(conn);- 3
throw err;});}, function (err) {- 19
return self.__rollback(conn, err);});});});} catch (e) {- 0
this.logError(e);- 0
promise.errback(e);}- 0
return promise.promise();},__transactionComplete: function (promise, type, conn) {- 0
this.__finishTransactionAndCheckForMore(conn);- 0
promise[type].apply(promise, argsToArray(arguments).slice(3));},__rollback: function (conn, err) {- 19
return this.__rollbackTransaction(conn, null, err).both(hitch(this, "__finishTransactionAndCheckForMore", conn)).chain(hitch(this, function () {- 19
if (conn.__transactionDepth <= 1) {- 19
this.__transactionError(err);} else {- 0
throw err;}}));},__transactionError: function (err) {- 19
if (isArray(err)) {- 16
for (var i in err) {- 16
if (i in err) {- 16
var e = err[i];- 16
if (isArray(e) && e.length === 2) {- 16
var realE = e[1];- 16
if (realE !== "ROLLBACK") {- 15
throw realE;}- 1
break;} else {- 0
throw e;}}}} else {- 3
if (err !== "ROLLBACK") {- 3
throw err;}}},__finishTransactionAndCheckForMore: function (conn) {- 918
if (this.alreadyInTransaction(conn)) {- 915
if (!this.supportsSavepoints || ((conn.__transactionDepth -= 1) <= 0)) {- 908
if (conn) {- 908
this.pool.returnConnection(conn);}- 908
var index, transactions = this.__transactions;- 908
if ((index = transactions.indexOf(conn)) > -1) {- 908
transactions.splice(index, 1);}- 908
if (!this.__transactionQueue.isEmpty) {- 73
this.__transactionQueue.dequeue()();} else {- 835
this.__alreadyInTransaction = false;}}}},//SQL to start a new savepoint__beginSavepointSql: function (depth) {- 7
return format(this._static.SQL_SAVEPOINT, depth);},// Start a new database connection on the given connection__beginNewTransaction: function (conn, opts) {- 899
var self = this;- 899
return this.__logConnectionExecute(conn, this.beginTransactionSql).chain(function () {- 899
return self.__setTransactionIsolation(conn, opts);});},//Start a new database transaction or a new savepoint on the given connection.__beginTransaction: function (conn, opts) {- 915
var ret;- 915
if (this.supportsSavepoints) {- 772
if (conn.__transactionDepth > 0) {- 7
ret = this.__logConnectionExecute(conn, this.__beginSavepointSql(conn.__transactionDepth));} else {- 765
ret = this.__beginNewTransaction(conn, opts);}- 772
conn.__transactionDepth += 1;} else {- 143
ret = this.__beginNewTransaction(conn, opts);}- 915
return ret;},// SQL to commit a savepoint__commitSavepointSql: function (depth) {- 2
return format(this.SQL_RELEASE_SAVEPOINT, depth);},//Commit the active transaction on the connection__commitTransaction: function (conn, opts) {- 896
opts = opts || {};- 896
if (this.supportsSavepoints) {- 762
var depth = conn.__transactionDepth;- 762
var sql = null;- 762
if (depth > 1) {- 2
sql = this.__commitSavepointSql(depth - 1);} else {- 760
this.__commiting = true;- 760
sql = this.commitTransactionSql;}- 762
return this.__logConnectionExecute(conn, (sql));} else {- 134
this.__commiting = true;- 134
return this.__logConnectionExecute(conn, this.commitTransactionSql);}},//SQL to rollback to a savepoint__rollbackSavepointSql: function (depth) {- 4
return format(this.SQL_ROLLBACK_TO_SAVEPOINT, depth);},//Rollback the active transaction on the connection__rollbackTransaction: function (conn, opts) {- 19
opts = opts || {};- 19
if (this.supportsSavepoints) {- 10
var sql, depth = conn.__transactionDepth;- 10
if (depth > 1) {- 4
sql = this.__rollbackSavepointSql(depth - 1);} else {- 6
this.__commiting = true;- 6
sql = this.rollbackTransactionSql;}- 10
return this.__logConnectionExecute(conn, sql);} else {- 9
this.__commiting = false;- 9
return this.__logConnectionExecute(conn, this.rollbackTransactionSql);}},// Set the transaction isolation level on the given connection__setTransactionIsolation: function (conn, opts) {- 908
var level;- 908
var ret;- 908
if (this.supportsTransactionIsolationLevels && !isUndefinedOrNull(level = isUndefinedOrNull(opts.isolation) ? this.transactionIsolationLevel : opts.isolation)) {- 8
ret = this.__logConnectionExecute(conn, this.__setTransactionIsolationSql(level));} else {- 900
ret = new Promise().callback();}- 908
return ret.promise();},// SQL to set the transaction isolation level__setTransactionIsolationSql: function (level) {- 8
return format("SET TRANSACTION ISOLATION LEVEL %s", this.TRANSACTION_ISOLATION_LEVELS[level]);},//Convert the given default, which should be a database specific string, into//a javascript object.__columnSchemaToJsDefault: function (def, type) {- 859
if (isNull(def) || isUndefined(def)) {- 714
return null;}- 145
var origDefault = def, m, datePattern, dateTimePattern, timeStampPattern, timePattern;- 145
if (this.type === "postgres" && (m = def.match(this.POSTGRES_DEFAULT_RE)) !== null) {- 9
def = m[1] || m[2];- 9
dateTimePattern = this.POSTGRES_DATE_TIME_PATTERN;- 9
timePattern = this.POSTGRES_TIME_PATTERN;}- 145
if (this.type === "mssql" && (m = def.match(this.MSSQL_DEFAULT_RE)) !== null) {- 4
def = m[1] || m[2];}- 145
if (["string", "blob", "date", "datetime", "year", "timestamp", "time", "enum"].indexOf(type) !== -1) {- 32
if (this.type === "mysql") {- 12
if (["date", "datetime", "time", "timestamp"].indexOf(type) !== -1 && def.match(this.MYSQL_TIMESTAMP_RE)) {- 4
return null;}- 8
origDefault = def = "'" + def + "'".replace("\\", "\\\\");}- 28
if (!(m = def.match(this.STRING_DEFAULT_RE))) {- 2
return null;}- 26
def = m[1].replace("''", "'");}- 139
var ret = null;- 139
try {- 139
switch (type) {case "boolean":- 6
if (def.match(/[f0]/i)) {- 3
ret = false;- 3
} else if (def.match(/[t1]/i)) {- 3
ret = true;- 0
} else if (isBoolean(def)) {- 0
ret = def;}- 6
break;case "blob":- 1
ret = new Buffer(def);- 1
break;case "string":case "enum":- 13
ret = def;- 13
break;case "integer":- 92
ret = parseInt(def, 10);- 92
if (isNaN(ret)) {- 85
ret = null;}- 92
break;case "float":case "decimal":- 11
ret = parseFloat(def, 10);- 11
if (isNaN(ret)) {- 1
ret = null;}- 11
break;case "year" :- 1
ret = this.patio.stringToYear(def);- 1
break;case "date":- 3
ret = this.patio.stringToDate(def, datePattern);- 3
break;case "timestamp":- 1
ret = this.patio.stringToTimeStamp(def, timeStampPattern);- 1
break;case "datetime":- 4
ret = this.patio.stringToDateTime(def, dateTimePattern);- 4
break;case "time":- 3
ret = this.patio.stringToTime(def, timePattern);- 3
break;}} catch (e) {}- 139
return ret;},/*** Match the database's column type to a javascript type via a* regular expression, and return the javascript type as a string* such as "integer" or "string".* @private*/schemaColumnType: function (dbType) {- 806
var ret = dbType, m;- 806
if (dbType.match(/^interval$/i)) {- 0
ret = "interval";- 806
} else if (dbType.match(/^(character( varying)?|n?(var)?char)/i)) {- 191
ret = "string";- 615
} else if (dbType.match(/^int(eger)?|(big|small|tiny)int/i)) {- 156
ret = "integer";- 459
} else if (dbType.match(/^date$/i)) {- 34
ret = "date";- 425
} else if (dbType.match(/^year/i)) {- 0
ret = "year";- 425
} else if (dbType.match(/^((small)?datetime|timestamp( with(out)? time zone)?)$/i)) {- 8
ret = "datetime";- 417
} else if (dbType.match(/^time( with(out)? time zone)?$/i)) {- 1
ret = "time";- 416
} else if (dbType.match(/^(bit|boolean)$/i)) {- 0
ret = "boolean";- 416
} else if (dbType.match(/^(real|float|double( precision)?)$/i)) {- 12
ret = "float";- 404
} else if ((m = dbType.match(/^(?:(?:(?:num(?:ber|eric)?|decimal|double)(?:\(\d+,\s*(\d+)\))?)|(?:small)?money)/i))) {- 68
ret = m[1] && m[1] === '0' ? "integer" : "decimal";- 336
} else if (dbType.match(/n?text/i)) {- 305
ret = "text";- 31
} else if (dbType.match(/bytea|[bc]lob|image|(var)?binary/i)) {- 17
ret = "blob";- 14
} else if (dbType.match(/^enum/i)) {- 2
ret = "enum";- 12
} else if (dbType.match(/^set/i)) {- 0
ret = "set";- 12
} else if (dbType.match(/^json/i)) {- 12
ret = "json";}- 806
return ret;},/*** Returns true if this DATABASE is currently in a transaction.** @param opts* @return {Boolean} true if this dabase is currently in a transaction.*/alreadyInTransaction: function (conn, opts) {- 6207
opts = opts || {};- 6207
return this.__transactions.indexOf(conn) !== -1 && (!this.supportsSavepoints || !opts.savepoint);},/**@ignore*/getters: {/**@lends patio.Database.prototype*//*** SQL to BEGIN a transaction.* See {@link patio.Database#SQL_BEGIN} for default,* @field* @type String*/beginTransactionSql: function () {- 908
return this.SQL_BEGIN;},/*** SQL to COMMIT a transaction.* See {@link patio.Database#SQL_COMMIT} for default,* @field* @type String*/commitTransactionSql: function () {- 894
return this.SQL_COMMIT;},/*** SQL to ROLLBACK a transaction.* See {@link patio.Database#SQL_ROLLBACK} for default,* @field* @type String*/rollbackTransactionSql: function () {- 15
return this.SQL_ROLLBACK;},/*** Return a function for the dataset's {@link patio.Dataset#outputIdentifierMethod}.* Used in metadata parsing to make sure the returned information is in the* correct format.** @field* @type Function*/outputIdentifierFunc: function () {- 111
var ds = this.dataset;- 111
return function (ident) {- 1053
return ds.outputIdentifier(ident);};},/*** Return a function for the dataset's {@link patio.Dataset#inputIdentifierMethod}.* Used in metadata parsing to make sure the returned information is in the* correct format.** @field* @type Function*/inputIdentifierFunc: function () {- 172
var ds = this.dataset;- 172
return function (ident) {- 172
return ds.inputIdentifier(ident);};},/*** Return a dataset that uses the default identifier input and output methods* for this database. Used when parsing metadata so that column are* returned as expected.** @field* @type patio.Dataset*/metadataDataset: function () {- 111
if (this.__metadataDataset) {- 80
return this.__metadataDataset;}- 31
var ds = this.dataset;- 31
ds.identifierInputMethod = this.identifierInputMethod;- 31
ds.identifierOutputMethod = this.identifierOutputMethod;- 31
this.__metadataDataset = ds;- 31
return ds;}}},"static": {SQL_BEGIN: 'BEGIN',SQL_COMMIT: 'COMMIT',SQL_RELEASE_SAVEPOINT: 'RELEASE SAVEPOINT autopoint_%d',SQL_ROLLBACK: 'ROLLBACK',SQL_ROLLBACK_TO_SAVEPOINT: 'ROLLBACK TO SAVEPOINT autopoint_%d',SQL_SAVEPOINT: 'SAVEPOINT autopoint_%d',TRANSACTION_BEGIN: 'Transaction.begin',TRANSACTION_COMMIT: 'Transaction.commit',TRANSACTION_ROLLBACK: 'Transaction.rollback',TRANSACTION_ISOLATION_LEVELS: {uncommitted: 'READ UNCOMMITTED',committed: 'READ COMMITTED',repeatable: 'REPEATABLE READ',serializable: 'SERIALIZABLE'},POSTGRES_DEFAULT_RE: /^(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))$/,MSSQL_DEFAULT_RE: /^(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))$/,MYSQL_TIMESTAMP_RE: /^CURRENT_(?:DATE|TIMESTAMP)?$/,STRING_DEFAULT_RE: /^'(.*)'$/}}).as(module);
|
database/defaults.js
|
Coverage93.55
SLOC244
LOC31
Missed2
|
- 1
var comb = require("comb"),isUndefined = comb.isUndefined,isUndefinedOrNull = comb.isUndefinedOrNull,define = comb.define;- 1
define(null, {instance:{__supportsTransactionIsolationLevels:false,__supportsSavePoints:false,__supportsPreparedTransactions:false,constructor:function (opts) {- 120
this._super(arguments);- 120
var statics = this._static;- 120
this.__identifierInputMethod = isUndefined(opts.identifierInputMethod) ? isUndefined(statics.identifierInputMethod) ? this.identifierInputMethodDefault : statics.identifierInputMethod : opts.identifierInputMethod;- 120
this.__identifierOutputMethod = isUndefined(opts.identifierOutputMethod) ? isUndefined(statics.identifierOutputMethod) ? this.identifierOutputMethodDefault : statics.identifierOutputMethod : opts.identifierOutputMethod;- 120
this.__quoteIdentifiers = isUndefined(opts.quoteIdentifiers) ? isUndefinedOrNull(statics.quoteIdentifiers) ? this.quoteIdentifiersDefault : statics.quoteIdentifiers : opts.quoteIdentifiers;},getters:{/**@lends patio.Database.prototype*//*** The default options for the connection pool.* @field* @type Object*/connectionPoolDefaultOptions:function () {- 120
return {};},/*** Default schema to use. This is generally null but may be overridden by an adapter.* @field* @type {String|patio.sql.Identifier}*/defaultSchemaDefault:function () {- 120
return null;},/*** The default String or comb method to use transform identifiers with when* sending identifiers to the database.* @field* @type String* @default toUpperCase*/identifierInputMethodDefault:function () {- 20
return "toUpperCase";},/*** The default String or comb method to use transform identifiers with when* they are retrieved from the database.* @field* @type String* @default toLowerCase*/identifierOutputMethodDefault:function () {- 17
return "toLowerCase";},/*** Default boolean of whether or not to quote identifiers before sending* then to the database.* @field* @type Boolean* @default true*/quoteIdentifiersDefault:function () {- 10
return true;},/*** Default serial primary key options, used by the table creation* code.* @field* @type Object* @default {primaryKey : true, type : "integer", autoIncrement : true}* */serialPrimaryKeyOptions:function () {- 9
return {primaryKey:true, type:"integer", autoIncrement:true};},/*** Whether the database and adapter support prepared transactions* (two-phase commit)* @field* @type Boolean* @default false*/supportsPreparedTransactions:function () {- 0
return this.__supportsPreparedTransactions;},/*** Whether the database and adapter support savepoints.* @field* @type Boolean* @default false*/supportsSavepoints:function () {- 702
return this.__supportsSavePoints;},/*** Whether the database and adapter support transaction isolation levels.** @field* @type Boolean* @default false* */supportsTransactionIsolationLevels:function () {- 164
return this.__supportsTransactionIsolationLevels;},/*** The String or comb method to use transform identifiers with when* sending identifiers to the database. If this property is undefined then* {@link patio.Database#identifierInputMethodDefault} will be used.** @field* @type String*/identifierInputMethod:function () {- 13664
return this.__identifierInputMethod;},/*** The String or comb method to use transform identifiers with when* they are retrieved from database. If this property is undefined then* {@link patio.Database#identifierOutputMethodDefault} will be used.** @field* @type String*/identifierOutputMethod:function () {- 13664
return this.__identifierOutputMethod;},/*** Boolean of whether or not to quote identifiers before sending* then to the database. If this property is undefined then* then {@link patio.Database#quoteIdentifiersDefault} will be used.** @field* @type Boolean* @default true*/quoteIdentifiers:function () {- 13637
return this.__quoteIdentifiers;}},setters:{identifierInputMethod:function (identifierInputMethod) {- 51
this.__identifierInputMethod = identifierInputMethod;},identifierOutputMethod:function (identifierOutputMethod) {- 51
this.__identifierOutputMethod = identifierOutputMethod;},quoteIdentifiers:function (quoteIdentifiers) {- 20
this.__quoteIdentifiers = quoteIdentifiers;},supportsTransactionIsolationLevels:function (supports) {- 3
this.__supportsTransactionIsolationLevels = supports;},supportsPreparedTransactions:function (supports) {- 0
this.__supportsPreparedTransactions = supports;},supportsSavepoints:function (supports) {- 1
this.__supportsSavePoints = supports;}}},"static":{__identifierInputMethod:undefined,__identifierOutputMethod:undefined,__quoteIdentifiers:null,getters:{/**@lends patio.Database*//*** The String or comb method to use transform identifiers with when* they are sent to database. See {@link patio#identifierInputMethod}** @field* @type String*/identifierInputMethod:function () {- 218
return this.__identifierInputMethod;},/*** The String or comb method to use transform identifiers with when* they are retrieved from database. See {@link patio#identifierOutputMethod}** @field* @type String*/identifierOutputMethod:function () {- 221
return this.__identifierOutputMethod;},/*** Boolean of whether or not to quote identifiers before sending* then to the database. See {@link patio#quoteIdentifiers}** @field* @type Boolean*/quoteIdentifiers:function () {- 225
return this.__quoteIdentifiers;}},setters:{identifierInputMethod:function (identifierInputMethod) {- 77
this.__identifierInputMethod = identifierInputMethod;},identifierOutputMethod:function (identifierOutputMethod) {- 77
this.__identifierOutputMethod = identifierOutputMethod;},quoteIdentifiers:function (quoteIdentifiers) {- 82
this.__quoteIdentifiers = quoteIdentifiers;}}}}).as(module);
|
errors.js
|
Coverage94.74
SLOC81
LOC19
Missed1
|
- 1
var patio = exports;/*** @class Thrown if a function is not impltemened** @param {String} message the message to show.*/- 1
patio.NotImplemented = function (message) {- 3
return new Error("Not Implemented : " + message);};/*** @class Thrown if there is an Expression Error.** @param {String} message the message to show.*/- 1
patio.ExpressionError = function (message) {- 1
return new Error("Expression Error :" + message);};/*** @class Thrown if there is a Query Error.** @param {String} message the message to show.*/- 1
patio.QueryError = function (message) {- 131
return new Error("QueryError : " + message);};/*** @class Thrown if there is a Dataset Error.** @param {String} message the message to show.*/- 1
patio.DatasetError = function (message) {- 7
return new Error("DatasetError : " + message);};/*** @class Thrown if there is a Database Error.** @param {String} message the message to show.*/- 1
patio.DatabaseError = function (message) {- 7
return new Error("Database error : " + message);};/*** @class Thrown if there is a unexpected Error.** @param {String} message the message to show.*/- 1
patio.PatioError = function (message) {- 31
return new Error("Patio error : " + message);};/*** @class Thrown if there is a error thrown within a model.* @param {String} message the message to show.*/- 1
patio.ModelError = function (message) {- 3
return new Error("Model error : " + message);};/*** @class Thrown if there is an error when loading/creating/deleteing an association.* @param {String} message the message to show.*/- 1
patio.AssociationError = function (message) {- 0
return new Error("Association error : " + message);};/*** Thrown if there is an error when performing a migration.* @param {String} message the message to show.*/- 1
patio.MigrationError = function (message) {- 2
return new Error("Migration error : " + message);};
|
dataset/actions.js
|
Coverage96.77
SLOC1291
LOC217
Missed7
|
- 1
var comb = require("comb"),errors = require("../errors"),asyncArray = comb.async.array,NotImplemented = errors.NotImplemented,QueryError = errors.QueryError,sql = require("../sql").sql,Identifier = sql.Identifier,isUndefinedOrNull = comb.isUndefinedOrNull,argsToArray = comb.argsToArray,isFunction = comb.isFunction,isNumber = comb.isNumber,QualifiedIdentifier = sql.QualifiedIdentifier,AliasedExpression = sql.AliasedExpression,define = comb.define,isInstanceOf = comb.isInstanceOf,merge = comb.merge,isBoolean = comb.isBoolean,isString = comb.isString,flatten = comb.array.flatten,when = comb.when,logging = comb.logging,Logger = logging.Logger,Promise = comb.Promise,PromiseList = comb.PromiseList,TransformStream = require("stream").Transform,pipeAll = require("../utils").pipeAll;- 1
var Dataset;- 1
var LOGGER = Logger.getLogger("patio.Dataset");- 1
function partition(arr, sliceSize) {- 10
var output = [], j = 0;- 10
for (var i = 0, l = arr.length; i < l; i += sliceSize) {- 24
output[j++] = arr.slice(i, i + sliceSize);}- 10
return output;}- 1
define({instance: {/**@lends patio.Dataset.prototype*//**@ignore*/constructor: function () {- 27052
if (!Dataset) {- 1
Dataset = require("../index").Dataset;}- 27052
this._super(arguments);},/*** Returns a [Stream](http://nodejs.org/api/stream.html) for streaming data from the database.**```* User* .stream()* .on("data", function(record){* console.log(record);* })* .on("error", errorHandler)* .on("end", function(){* console.log("all done")* });** //postgres options* User* .stream({batchSize: 100, highWaterMark: 1000})* .on("data", function(record){* console.log(record);* })* .on("error", errorHandler)* .on("end", function(){* console.log("all done")* });*```* @param {Object} opts an object to pass to the adapters connection stream implementation* @return {Stream}*/stream: function (opts) {- 5
var queryStream = this.fetchRows(this.selectSql, merge(opts || {}, {stream: true})), rowCb, ret;- 5
if ((rowCb = this.rowCb)) {- 1
ret = new TransformStream({objectMode: true});- 1
ret._transform = function (data, encoding, done) {- 20
when(rowCb(data)).chain(function (data) {- 20
ret.push(data);- 20
done();}, done);};- 1
pipeAll(queryStream, ret);} else {- 4
ret = queryStream;}- 5
return ret;},/*** Returns a Promise that is resolved with an array with all records in the dataset.* If a block is given, the array is iterated over after all items have been loaded.** @example** // SELECT * FROM table* DB.from("table").all().chain(function(res){* //res === [{id : 1, ...}, {id : 2, ...}, ...];* });* // Iterate over all rows in the table* var myArr = [];* var rowPromise = DB.from("table").all(function(row){ myArr.push(row);});* rowPromise.chain(function(rows){* //=> rows == myArr;* });** @param {Function} block a block to be called with each item. The return value of the block is ignored.* @param {Function} [cb] a block to invoke when the action is done** @return {comb.Promise} a promise that is resolved with an array of rows.*/all: function (block, cb) {- 2429
var self = this;- 2429
var ret = asyncArray(this.forEach().chain(function (records) {- 2425
return self.postLoad(records);}));- 2429
if (block) {- 2
ret = ret.forEach(block);}- 2429
return ret.classic(cb).promise();},/*** Returns a promise that is resolved with the average value for the given column.** @example** // SELECT avg(number) FROM table LIMIT 1* DB.from("table").avg("number").chain(function(avg){* // avg === 3* });** @param {String|patio.sql.Identifier} column the column to average* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is resolved with the average value of the column.*/avg: function (column, cb) {- 6
return this.__aggregateDataset().get(sql.avg(this.stringToIdentifier(column)), cb);},/*** Returns a promise that is resolved with the number of records in the dataset.** @example** // SELECT COUNT(*) AS count FROM table LIMIT 1* DB.from("table").count().chain(function(count){* //count === 3;* });** @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is resolved with the the number of records in the dataset.*/count: function (cb) {- 84
return this.__aggregateDataset().get(sql.COUNT(sql.literal("*")).as("count")).chain(function (res) {- 84
return parseInt(res, 10);}).classic(cb);},/**@ignore*/"delete": function () {- 0
return this.remove();},/*** Deletes the records in the dataset. The returned Promise should be resolved with the* number of records deleted, but that is adapter dependent.** @example** // DELETE * FROM table* DB.from("table").remove().chain(function(numDeleted){* //numDeleted === 3* });** @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise resolved with the* number of records deleted, but that is adapter dependent.*/remove: function (cb) {- 732
return this.executeDui(this.deleteSql).classic(cb).promise();},/*** Iterates over the records in the dataset as they are returned from the* database adapter.** @example** // SELECT * FROM table* DB.from("table").forEach(function(row){* //....do something* });** @param {Function} [block] the block to invoke for each row.* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is resolved when the action has completed.*/forEach: function (block, cb) {- 2976
var rowCb, ret;- 2976
if (this.__opts.graph) {- 12
ret = this.graphEach(block);} else {- 2964
ret = this.fetchRows(this.selectSql);- 2964
if ((rowCb = this.rowCb)) {- 1209
ret = ret.map(function (r) {- 1533
return rowCb(r);});}- 2964
if (block) {- 108
ret = ret.forEach(block);}}- 2976
return ret.classic(cb);},/*** Returns a promise that is resolved with true if no records exist in the dataset,* false otherwise.* @example** // SELECT 1 FROM table LIMIT 1* DB.from("table").isEmpty().chain(function(isEmpty){* // isEmpty === false* });** @param {Function} [cb] a function to callback when action is done** @return {comb.Promise} a promise that is resolved with a boolean indicating if the table is empty.*/isEmpty: function (cb) {- 15
return this.get(1).chain(function (res) {- 15
return isUndefinedOrNull(res) || res.length === 0;}.bind(this)).classic(cb);},__processFields: function (fields) {- 0
throw new Error("Not Implemented");},__processRow: function (row, cols) {- 3806
var h = {}, i = -1, l = cols.length, col;- 3806
while (++i < l) {- 30109
col = cols[i];- 30109
h[col[0]] = col[1](row[col[2]]);}- 3803
return h;},__processRows: function (rows, cols) {//dp this so the callbacks are called in appropriate order also.- 2774
var ret = [], i = -1, l = rows.length, processRow = this.__processRow;- 2774
while (++i < l) {- 3782
ret[i] = processRow.call(this, rows[i], cols);}- 2771
cols = null;- 2771
rows.length = 0;- 2771
return ret;},fetchStreamedRows: function (sql, opts) {- 5
var ret = new TransformStream({objectMode: true}), cols, self = this;- 5
ret._transform = function (row, encoding, callback) {- 24
ret.push(self.__processRow(row, cols));- 24
callback();};- 5
var queryStream = this.execute(sql, opts);- 5
queryStream.on("fields", function (fields) {- 3
cols = self.__processFields(fields);});- 5
pipeAll(queryStream, ret);- 5
return ret;},fetchPromisedRows: function (sql, opts) {- 2775
var self = this;- 2775
return asyncArray(this.execute(sql, opts).chain(function (rows, fields) {- 2774
return self.__processRows(rows, self.__processFields(fields));}));},/*** @private* Executes a select query and fetches records, passing each record to the* supplied cb. This method should not be called by user code, use {@link patio.Dataset#forEach}* instead.*/fetchRows: function (sql, opts) {- 2780
opts = opts || {};- 2780
var ret;- 2780
if (opts.stream) {- 5
ret = this.fetchStreamedRows(sql, opts);} else {- 2775
ret = this.fetchPromisedRows(sql, opts);}- 2780
return ret;},/*** If a integer argument is given, it is interpreted as a limit, and then returns all* matching records up to that limit.** If no arguments are passed, it returns the first matching record.** If a function taking no arguments is passed in as the last parameter then it* is assumed to be a filter block. If the a funciton is passed in that takes arguments* then it is assumed to be a callback. You may also pass in both the second to last argument* being a filter function, and the last being a callback.** If any other type of argument(s) is passed, it is given to {@link patio.Dataset#filter} and the* first matching record is returned. Examples:** @example** comb.executeInOrder(DB.from("table"), function(ds){* // SELECT * FROM table LIMIT 1* ds.first(); // => {id : 7}** // SELECT * FROM table LIMIT 2* ds.first(2); // => [{id : 6}, {id : 4}]** // SELECT * FROM table WHERE (id = 2) LIMIT 1* ds.first({id : 2}) // => {id : 2}*** // SELECT * FROM table WHERE (id = 3) LIMIT 1* ds.first("id = 3"); // => {id : 3}** // SELECT * FROM table WHERE (id = 4) LIMIT 1* ds.first("id = ?", 4); // => {id : 4}** // SELECT * FROM table WHERE (id > 2) LIMIT 1* ds.first(function(){return this.id.gt(2);}); // => {id : 5}*** // SELECT * FROM table WHERE ((id > 4) AND (id < 6)) LIMIT 1* ds.first("id > ?", 4, function(){* return this.id.lt(6);* }); // => {id : 5}** // SELECT * FROM table WHERE (id < 2) LIMIT 2* ds.first(2, function(){* return this.id.lt(2)* }); // => [{id:1}]* });** @param {*} args varargs to be used to limit/filter the result set.** @return {comb.Promise} a promise that is resolved with the either the first matching record.* Or an array of items if a limit was provided as the first argument.*/first: function (args) {- 73
args = comb(arguments).toArray();- 73
var cb,block = isFunction(args[args.length - 1]) ? args.pop() : null;- 73
if (block && block.length > 0) {- 18
cb = block;- 18
block = isFunction(args[args.length - 1]) ? args.pop() : null;}- 73
var ds = block ? this.filter(block) : this;- 73
if (!args.length) {- 50
return ds.singleRecord(cb);} else {- 23
args = (args.length === 1) ? args[0] : args;- 23
if (isNumber(args)) {- 9
return ds.limit(args).all(null, cb);} else {- 14
return ds.filter(args).singleRecord(cb);}}},/*** Return the column value for the first matching record in the dataset.** @example* // SELECT id FROM table LIMIT 1* DB.from("table").get("id").chain(function(val){* // val === 3* });*** // SELECT sum(id) FROM table LIMIT 1* ds.get(sql.sum("id")).chain(function(val){* // val === 6;* });** // SELECT sum(id) FROM table LIMIT 1* ds.get(function(){* return this.sum("id");* }).chain(function(val){* // val === 6;* });** @param {*} column the column to filter on can be anything that* {@link patio.Dataset#select} accepts.* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that will be resolved will the value requested.*/get: function (column, cb) {- 178
return this.select(column).singleValue(cb);},/*** Inserts multiple records into the associated table. This method can be* used to efficiently insert a large number of records into a table in a* single query if the database supports it. Inserts* are automatically wrapped in a transaction.** This method is called with a columns array and an array of value arrays:* <pre class="code">* // INSERT INTO table (x, y) VALUES (1, 2)* // INSERT INTO table (x, y) VALUES (3, 4)* DB.from("table").import(["x", "y"], [[1, 2], [3, 4]]).* </pre>** This method also accepts a dataset instead of an array of value arrays:** <pre class="code">* // INSERT INTO table (x, y) SELECT a, b FROM table2* DB.from("table").import(["x", "y"], DB.from("table2").select("a", "b"));* </pre>** The method also accepts a commitEvery option that specifies* the number of records to insert per transaction. This is useful especially* when inserting a large number of records, e.g.:** <pre class="code">* // this will commit every 50 records* DB.from("table").import(["x", "y"], [[1, 2], [3, 4], ...], {commitEvery : 50});* </pre>** @param {Array} columns The columns to insert values for.* This array will be used as the base for each values item in the values array.* @param {Array[Array]} values Array of arrays of values to insert into the columns.* @param {Object} [opts] options* @param {Number} [opts.commitEvery] the number of records to insert per transaction.* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is resolved once all records have been inserted.*/"import": function (columns, values, opts, cb) {- 29
if (isFunction(opts)) {- 3
cb = opts;- 3
opts = null;}- 29
opts = opts || {};- 29
var ret, self = this;- 29
if (isInstanceOf(values, Dataset)) {- 2
ret = this.db.transaction(function () {- 2
return self.insert(columns, values);});} else {- 27
if (!values.length) {- 0
ret = new Promise().callback();- 27
} else if (!columns.length) {- 0
throw new QueryError("Invalid columns in import");}- 27
var sliceSize = opts.commitEvery || opts.slice, result = [];- 27
if (sliceSize) {- 10
ret = asyncArray(partition(values, sliceSize)).forEach(function (entries, offset) {- 24
offset = (offset * sliceSize);- 24
return self.db.transaction(opts, function () {- 24
return when(self.multiInsertSql(columns, entries).map(function (st, index) {- 28
return self.executeDui(st).chain(function (res) {- 28
result[offset + index] = res;});}));});}, 1);} else {- 17
var statements = this.multiInsertSql(columns, values);- 17
ret = this.db.transaction(function () {- 17
return when(statements.map(function (st, index) {- 37
return self.executeDui(st).chain(function (res) {- 37
result[index] = res;});}));});}}- 29
return ret.chain(function () {- 29
return flatten(result);}).classic(cb).promise();},/*** This is the recommended function to do the insert of multiple items into the* database. This acts as a proxy to the {@link patio.Dataset#import} method so* one can use an array of hashes rather than an array of columns and an array of values.* See {@link patio.Dataset#import} for more information regarding the method of inserting.* <p>* <b>NOTE:</b>All hashes should have the same keys other wise some values could be missed</b>* </p>** @example** // INSERT INTO table (x) VALUES (1)* // INSERT INTO table (x) VALUES (2)* DB.from("table").multiInsert([{x : 1}, {x : 2}]).chain(function(){* //...do something* })** //commit every 50 inserts* DB.from("table").multiInsert([{x : 1}, {x : 2},....], {commitEvery : 50}).chain(function(){* //...do something* });** @param {Object[]} hashes an array of objects to insert into the database. The keys of* the first item in the array will be used to look up columns in all subsequent objects. If the* array is empty then the promise is resolved immediatly.** @param {Object} opts See {@link patio.Dataset#import}.* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} See {@link patio.Dataset#import} for return functionality.*/multiInsert: function (hashes, opts, cb) {- 18
if (isFunction(opts)) {- 5
cb = opts;- 5
opts = null;}- 18
opts = opts || {};- 18
hashes = hashes || [];- 18
var ret = new Promise();- 18
if (!hashes.length) {- 2
ret.callback();} else {- 16
var columns = Object.keys(hashes[0]);- 16
ret = this["import"](columns, hashes.map(function (h) {- 48
return columns.map(function (c) {- 48
return h[c];});}), opts, cb);}- 18
return ret.classic(cb).promise();},/*** Inserts values into the associated table. The returned value is generally* the value of the primary key for the inserted row, but that is adapter dependent.** @example** // INSERT INTO items DEFAULT VALUES* DB.from("items").insert()** // INSERT INTO items DEFAULT VALUES* DB.from("items").insert({});** // INSERT INTO items VALUES (1, 2, 3)* DB.from("items").insert([1,2,3]);** // INSERT INTO items (a, b) VALUES (1, 2)* DB.from("items").insert(["a", "b"], [1,2]);** // INSERT INTO items (a, b) VALUES (1, 2)* DB.from("items").insert({a : 1, b : 2});** // INSERT INTO items SELECT * FROM old_items* DB.from("items").insert(DB.from("old_items"));** // INSERT INTO items (a, b) SELECT * FROM old_items* DB.from("items").insert(["a", "b"], DB.from("old_items"));**** @param {patio.Dataset|patio.sql.LiteralString|Array|Object|patio.sql.BooleanExpression|...} values values to* insert into the database. The INSERT statement generated depends on the type.* <ul>* <li>Empty object| Or no arugments: then DEFAULT VALUES is used.</li>* <li>Object: the keys will be used as the columns, and values will be the values inserted.</li>* <li>Single {@link patio.Dataset} : an insert with subselect will be performed.</li>* <li>Array with {@link patio.Dataset} : The array will be used for columns and a subselect will performed with the dataset for the values.</li>* <li>{@link patio.sql.LiteralString} : the literal value will be used.</li>* <li>Single Array : the values in the array will be used as the VALUES clause.</li>* <li>Two Arrays: the first array is the columns the second array is the values.</li>* <li>{@link patio.sql.BooleanExpression} : the expression will be used as the values.* <li>An arbitrary number of arguments : the {@link patio.Dataset#literal} version of the values will be used</li>* </ul>* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is typically resolved with the ID of the inserted row.*/insert: function () {- 1117
var args = argsToArray(arguments);- 1117
var cb = isFunction(args[args.length - 1]) ? args.pop() : null;- 1117
return this.executeInsert(this.insertSql.apply(this, args)).classic(cb);},/*** @see patio.Dataset#insert*/save: function () {- 0
return this.insert.apply(this, arguments);},/*** Inserts multiple values. If a block is given it is invoked for each* item in the given array before inserting it. See {@link patio.Dataset#multiInsert} as* a possible faster version that inserts multiple records in one SQL statement.** <b> Params see @link patio.Dataset#insert</b>** @example** DB.from("table").insertMultiple([{x : 1}, {x : 2}]);* //=> INSERT INTO table (x) VALUES (1)* //=> INSERT INTO table (x) VALUES (2)** DB.from("table").insertMultiple([{x : 1}, {x : 2}], function(row){* row.y = row.x * 2;* });* //=> INSERT INTO table (x, y) VALUES (1, 2)* //=> INSERT INTO table (x, y) VALUES (2, 4)** @param array See {@link patio.Dataset#insert} for possible values.* @param {Function} [block] a function to be called before each item is inserted.* @param {Function} [cb] a function to be called when the aciton is complete** @return {comb.PromiseList} a promiseList that should be resolved with the id of each item inserted* in the order that was in the array.*/insertMultiple: function (array, block, cb) {- 4
var promises, ret;- 4
if (block) {- 2
ret = when(array.map(function (i) {- 6
return this.insert(block(i));}, this));} else {- 2
ret = when(array.map(function (i) {- 8
return this.insert(i);}, this));}- 4
return ret.classic(cb).promise();},/*** @see patio.Dataset#insertMultiple*/saveMultiple: function () {- 0
return this.insertMultiple.apply(this, arguments);},/*** Returns a promise that is resolved with the interval between minimum and maximum values* for the given column.** @example* // SELECT (max(id) - min(id)) FROM table LIMIT 1* DB.from("table").interval("id").chain(function(interval){* //(e.g) interval === 6* });** @param {String|patio.sql.Identifier} column to find the interval of.* @param {Function} [cb] a function to be called when the aciton is complete** @return {comb.Promise} a promise that will be resolved with the interval between the min and max values* of the column.*/interval: function (column, cb) {- 8
return this.__aggregateDataset().get(sql.max(column).minus(sql.min(column)), cb);},/*** Reverses the order and then runs first. Note that this* will not necessarily give you the last record in the dataset,* unless you have an unambiguous order.** @example** // SELECT * FROM table ORDER BY id DESC LIMIT 1* DB.from("table").order("id").last().chain(function(lastItem){* //...(e.g lastItem === {id : 10})* });** // SELECT * FROM table ORDER BY id ASC LIMIT 2* DB.from("table").order(sql.id.desc()).last(2).chain(function(lastItems){* //...(e.g lastItems === [{id : 1}, {id : 2});* });** @throws {patio.error.QueryError} If there is not currently an order for this dataset.** @param {*} args See {@link patio.Dataset#first} for argument types.** @return {comb.Promise} a promise that will be resolved with a single object or array depending on the* arguments provided.*/last: function (args) {- 32
if (!this.__opts.order) {- 4
throw new QueryError("No order specified");}- 28
var ds = this.reverse();- 28
return ds.first.apply(ds, arguments);},/*** Maps column values for each record in the dataset (if a column name is* given).** @example** // SELECT * FROM table* DB.from("table").map("id").chain(function(ids){* // e.g. ids === [1, 2, 3, ...]* });** // SELECT * FROM table* DB.from("table").map(function(r){* return r.id * 2;* }).chain(function(ids){* // e.g. ids === [2, 4, 6, ...]* });** @param {Function|String} column if a string is provided then then it is assumed* to be the name of a column in that table and the value of the column for each row* will be returned. If column is a function then the return value of the function will* be used.* @param {Function} [cb] a function to be called when the aciton is complete** @return {comb.Promise} a promise resolved with the array of mapped values.*/map: function (column, cb) {- 466
var ret = this.forEach();- 466
column && (ret = ret[isFunction(column) ? "map" : "pluck"](column));- 466
return ret.classic(cb).promise();},/*** Returns a promise resolved with the maximum value for the given column.** @example** // SELECT max(id) FROM table LIMIT 1* DB.from("table").max("id").chain(function(max){* // e.g. max === 10.* });*** @param {String|patio.sql.Identifier} column the column to find the maximum value for.* @param {Function} [cb] callback to invoke when action is done** @return {*} the maximum value for the column.*/max: function (column, cb) {- 4
return this.__aggregateDataset().get(sql.max(this.stringToIdentifier(column)), cb);},/*** Returns a promise resolved with the minimum value for the given column.** @example** // SELECT min(id) FROM table LIMIT 1* DB.from("table").min("id").chain(function(min){* // e.g. max === 0.* });*** @param {String|patio.sql.Identifier} column the column to find the minimum value for.* @param {Function} [cb] callback to invoke when action is done** @return {*} the minimum value for the column.*/min: function (column, cb) {- 4
return this.__aggregateDataset().get(sql.min(this.stringToIdentifier(column)), cb);},/*** Returns a promise resolved with a range from the minimum and maximum values for the* given column.** @example* // SELECT max(id) AS v1, min(id) AS v2 FROM table LIMIT 1* DB.from("table").range("id").chain(function(min, max){* //e.g min === 1 AND max === 10* });** @param {String|patio.sql.Identifier} column the column to find the min and max value for.* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is resolved with the min and max value, as the first* and second args respectively.*/range: function (column, cb) {- 8
var ret = new Promise();- 8
this.__aggregateDataset().select(sql.min(this.stringToIdentifier(column)).as("v1"), sql.max(this.stringToIdentifier(column)).as("v2")).first().chain(function (r) {- 8
ret.callback(r.v1, r.v2);}, ret.errback);- 8
return ret.classic(cb).promise();},/*** Selects the column given (either as an argument or as a callback), and* returns an array of all values of that column in the dataset. If you* give a block argument that returns an array with multiple entries,* the contents of the resulting array are undefined.*** @example* // SELECT id FROM table* DB.from("table").selectMap("id").chain(function(selectMap){* // e,g. selectMap === [3, 5, 8, 1, ...]* });** // SELECT abs(id) FROM table* DB.from("table").selectMap(function(){* return this.abs("id");* }).chain(function(selectMap){* //e.g selectMap === [3, 5, 8, 1, ...]* });** @param {*} column The column to return the values for.* See {@link patio.Dataset#select} for valid column values.* @param {Function} [cb] a function to be called when the aciton is complete** @return {comb.Promise} a promise resolved with the array of mapped values.*/selectMap: function (column, cb) {- 13
var ds = this.naked().ungraphed().select(column), col;- 13
return ds.map(function (r) {- 29
return r[col || (col = Object.keys(r)[0])];}, cb);},/*** The same as {@link patio.Dataset#selectMap}, but in addition orders the array by the column.** @example* // SELECT id FROM table ORDER BY id* DB.from("table").selectOrderMap("id").chain(function(mappedIds){* //e.g. [1, 2, 3, 4, ...]* });** // SELECT abs(id) FROM table ORDER BY abs(id)* DB.from("table").selectOrderMap(function(){* return this.abs("id");* }).chain(function(mappedIds){* //e.g. [1, 2, 3, 4, ...]* });** @param {*} column The column to return the values for.* See {@link patio.Dataset#select} for valid column values.** @return {comb.Promise} a promise resolved with the array of mapped values.*/selectOrderMap: function (column, cb) {- 28
var col, ds = this.naked().ungraphed().select(column).order(isFunction(column) ? column : this._unaliasedIdentifier(column));- 28
return ds.map(function (r) {- 59
return r[col || (col = Object.keys(r)[0])];}, cb);},/*** Same as {@link patio.Dataset#singleRecord} but accepts arguments* to filter the dataset. See {@link patio.Dataset#filter} for argument types.** <b>NOTE</b> If the last argument is a function that accepts arguments it is not assumed to* be a filter function but instead a callback.** @return {comb.Promise} a promise resolved with a single row from the database that matched the filter.*/one: function () {- 1439
var args = comb(arguments).toArray(), cb;- 1439
var last = args[args.length - 1];- 1439
if (isFunction(last) && last.length > 0) {- 0
cb = args.pop();}- 1439
var ret = this;- 1439
if (args.length) {- 2
ret = ret.filter.apply(ret, args);}- 1439
return ret.singleRecord(cb);},/*** Returns a promise resolved with the first record in the dataset, or null if the dataset* has no records. Users should probably use {@link patio.Dataset#first} instead of* this method.** @example** //'SELECT * FROM test LIMIT 1'* DB.from("test").singleRecord().chain(function(r) {* //e.g r === {id : 1, name : "firstName"}* });** @param {Function} [cb] a function to be called when the aciton is complete** @return {comb.Promise} a promise resolved with the first record returned from the query.*/singleRecord: function (cb) {- 1706
return this.mergeOptions({limit: 1}).all().chain(function (r) {- 1703
return r && r.length ? r[0] : null;}).classic(cb).promise();},/*** Returns a promise resolved with the first value of the first record in the dataset.* Returns null if dataset is empty. Users should generally use* {@link patio.Dataset#get} instead of this method.** @example** //'SELECT * FROM test LIMIT 1'* DB.from("test").singleValue().chain(function(r) {* //e.g r === 1* });** @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that will be resolved with the first value of the first row returned* from the dataset.**/singleValue: function (cb) {- 199
return this.naked().ungraphed().singleRecord().chain(function (r) {- 196
return r ? r[Object.keys(r)[0]] : null;}).classic(cb).promise();},/*** Returns a promise resolved the sum for the given column.** @example** // SELECT sum(id) FROM table LIMIT 1* DB.from("table").sum("id").chain(function(sum){* // e.g sum === 55* });** @param {String|patio.sql.Identifier\patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} column* the column to find the sum of.* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise resolved with the sum of the column.*/sum: function (column, cb) {- 4
return this.__aggregateDataset().get(sql.sum(this.stringToIdentifier(column)), cb);},/*** Returns a promise resolved with a string in CSV format containing the dataset records. By* default the CSV representation includes the column titles in the* first line. You can turn that off by passing false as the* includeColumnTitles argument.** <p>* <b>NOTE:</b> This does not use a CSV library or handle quoting of values in* any way. If any values in any of the rows could include commas or line* endings, you shouldn't use this.* </p>** @example* // SELECT * FROM table* DB.from("table").toCsv().chain(function(csv){* console.log(csv);* //outputs* id,name* 1,Jim* 2,Bob* });** // SELECT * FROM table* DB.from("table").toCsv(false).chain(function(csv){* console.log(csv);* //outputs* 1,Jim* 2,Bob* });** @param {Boolean} [includeColumnTitles=true] Set to false to prevent the printing of the column* titles as the first line.** @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that will be resolved with the CSV string of the results of the* query.*/toCsv: function (includeColumnTitles, cb) {- 4
var n = this.naked();- 4
if (isFunction(includeColumnTitles)) {- 1
cb = includeColumnTitles;- 1
includeColumnTitles = true;}- 4
includeColumnTitles = isBoolean(includeColumnTitles) ? includeColumnTitles : true;- 4
return n.columns.chain(function (cols) {- 4
var vals = [];- 4
if (includeColumnTitles) {- 2
vals.push(cols.join(", "));}- 4
return n.forEach(function (r) {- 12
vals.push(cols.map(function (c) {- 36
return r[c] || "";}).join(", "));}).chain(function () {- 4
return vals.join("\r\n") + "\r\n";});}.bind(this)).classic(cb).promise();},/*** Returns a promise resolved with a hash with keyColumn values as keys and valueColumn values as* values. Similar to {@link patio.Dataset#toHash}, but only selects the two columns.** @example* // SELECT id, name FROM table* DB.from("table").selectHash("id", "name").chain(function(hash){* // e.g {1 : 'a', 2 : 'b', ...}* });** @param {String|patio.sql.Identifier\patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} keyColumn the column* to use as the key in the hash.** @param {String|patio.sql.Identifier\patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} valueColumn the column* to use as the value in the hash.** @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is resolved with an array of hashes, that have the keyColumn* as the key and the valueColumn as the value.*/selectHash: function (keyColumn, valueColumn, cb) {- 8
var map = {}, args = comb.argsToArray(arguments);- 8
cb = isFunction(args[args.length - 1]) ? args.pop() : null;- 8
var k = this.__hashIdentifierToName(keyColumn),v = this.__hashIdentifierToName(valueColumn);- 8
return this.select.apply(this, args).map(function (r) {- 16
map[r[k]] = v ? r[v] : r;}).chain(function () {- 8
return map;}).classic(cb).promise();},/*** Returns a promise resolved with a hash with one column used as key and another used as value.* If rows have duplicate values for the key column, the latter row(s)* will overwrite the value of the previous row(s). If the valueColumn* is not given or null, uses the entire hash as the value.** @example* // SELECT * FROM table* DB.from("table").toHash("id", "name").chain(function(hash){* // {1 : 'Jim', 2 : 'Bob', ...}* });** // SELECT * FROM table* DB.from("table").toHash("id").chain(function(hash){* // {1 : {id : 1, name : 'Jim'}, 2 : {id : 2, name : 'Bob'}, ...}* });** @param {String|patio.sql.Identifier\patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} keyColumn the column* to use as the key in the returned hash.** @param {String|patio.sql.Identifier\patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} [keyValue=null] the* key of the column to use as the value in the hash** @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that will be resolved with the resulting hash.*/toHash: function (keyColumn, valueColumn, cb) {- 8
var ret = new Promise(), map = {};- 8
if (isFunction(valueColumn)) {- 2
cb = valueColumn;- 2
valueColumn = null;}- 8
var k = this.__hashIdentifierToName(keyColumn), v = this.__hashIdentifierToName(valueColumn);- 8
return this.map(function (r) {- 24
map[r[k]] = v ? r[v] : r;}).chain(function () {- 8
return map;}).classic(cb).promise();},/*** Truncates the dataset. Returns a promise that is resolved once truncation is complete.** @example** // TRUNCATE table* DB.from("table").truncate().chain(function(){* //...do something* });** @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is resolved once truncation is complete.*/truncate: function (cb) {- 27
return this.executeDdl(this.truncateSql).classic(cb);},/*** Updates values for the dataset. The returned promise is resolved with a value that is generally the* number of rows updated, but that is adapter dependent.** @example* // UPDATE table SET x = NULL* DB.from("table").update({x : null}).chain(function(numRowsUpdated){* //e.g. numRowsUpdated === 10* });** // UPDATE table SET x = (x + 1), y = 0* DB.from("table").update({ x : sql.x.plus(1), y : 0}).chain(function(numRowsUpdated){* // e.g. numRowsUpdated === 10* });** @param {Object} values See {@link patio.Dataset#updateSql} for parameter types.* @param {Function} [cb] the callback to invoke when the action is done.** @return {comb.Promise} a promise that is generally resolved with the* number of rows updated, but that is adapter dependent.*/update: function (values, cb) {- 200
return this.executeDui(this.updateSql(values)).classic(cb);},/*** @see patio.Dataset#set*/set: function () {- 1
this.update.apply(this, arguments);},/*** @private* Execute the given select SQL on the database using execute. Use the* readOnly server unless a specific server is set.*/execute: function (sql, opts) {- 2781
return this.db.execute(sql, merge({server: this.__opts.server || "readOnly"}, opts || {}));},/*** @private* Execute the given SQL on the database using {@link patio.Database#executeDdl}.*/executeDdl: function (sql, opts) {- 27
return this.db.executeDdl(sql, this.__defaultServerOpts(opts || {}));},/*** @private* Execute the given SQL on the database using {@link patio.Database#executeDui}.*/executeDui: function (sql, opts) {- 1054
return this.db.executeDui(sql, this.__defaultServerOpts(opts || {}));},/*** @private* Execute the given SQL on the database using {@link patio.Database#executeInsert}.*/executeInsert: function (sql, opts) {- 1117
return this.db.executeInsert(sql, this.__defaultServerOpts(opts || {}));},/*** This is run inside {@link patio.Dataset#all}, after all of the records have been loaded* via {@link patio.Dataset#forEach}, but before any block passed to all is called. It is called with* a single argument, an array of all returned records. Does nothing by* default.*/postLoad: function (allRecords) {- 2425
return allRecords;},/*** @private** Clone of this dataset usable in aggregate operations. Does* a {@link patio.Dataset#fromSelf} if dataset contains any parameters that would* affect normal aggregation, or just removes an existing* order if not.*/__aggregateDataset: function () {- 118
return this._optionsOverlap(this._static.COUNT_FROM_SELF_OPTS) ? this.fromSelf() : this.unordered();},/*** @private* Set the server to use to "default" unless it is already set in the passed opts*/__defaultServerOpts: function (opts) {- 2198
return merge({server: this.__opts.server || "default"}, opts || {});},/*** @private** Returns the string version of the identifier.** @param {String|patio.sql.Identifier\patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} identifier* identifier to resolve to a string.* @return {String} the string version of the identifier.*/__hashIdentifierToName: function (identifier) {- 60
return isString(identifier) ? this.__hashIdentifierToName(this.stringToIdentifier(identifier)) :isInstanceOf(identifier, Identifier) ? identifier.value :isInstanceOf(identifier, QualifiedIdentifier) ? identifier.column :isInstanceOf(identifier, AliasedExpression) ? identifier.alias : identifier;},/**@ignore*/getters: {/**@lends patio.Dataset.prototype*//*** @field* @type {comb.Promise}** Returns a promise that is resolved with the columns in the result set in order as an array of strings.* If the columns are currently cached, then the promise is immediately resolved with the cached value. Otherwise,* a SELECT query is performed to retrieve a single row in order to get the columns.** If you are looking for all columns for a single table and maybe some information about* each column (e.g. database type), see {@link patio.Database#schema}.** <pre class="code">* DB.from("table").columns.chain(function(columns){* // => ["id", "name"]* });* </pre>**/columns: function () {- 21
if (this.__columns) {- 8
return asyncArray(this.__columns);} else {- 13
var ds = this.unfiltered().unordered().mergeOptions({distinct: null, limit: 1});- 13
return asyncArray(ds.forEach().chain(function () {- 13
var columns = this.__columns = ds.__columns || [];- 13
return columns;}.bind(this)));}}}},"static": {/**@lends patio.Dataset*//*** List of action methods avaiable on the dataset.** @type String[]* @default ['all', 'one', 'avg', 'count', 'columns', 'remove', 'forEach', 'isEmpty', 'first',* 'get', 'import', 'insert', 'save', 'insertMultiple', 'saveMultiple', 'interval', 'last',* 'map', 'max', 'min', 'multiInsert', 'range', 'selectHash', 'selectMap', 'selectOrderMap', 'set',* 'singleRecord', 'singleValue', 'sum', 'toCsv', 'toHash', 'truncate', 'update', 'stream']*/ACTION_METHODS: ['all', 'one', 'avg', 'count', 'columns', 'remove', 'forEach', 'isEmpty', 'first','get', 'import', 'insert', 'save', 'insertMultiple', 'saveMultiple', 'interval', 'last','map', 'max', 'min', 'multiInsert', 'range', 'selectHash', 'selectMap', 'selectOrderMap', 'set','singleRecord', 'singleValue', 'sum', 'toCsv', 'toHash', 'truncate', 'update', 'stream'],/*** List of options that can interfere with the aggregation of a {@link patio.Dataset}* @type String[]* @default ["distinct", "group", "sql", "limit", "compounds"]*/COUNT_FROM_SELF_OPTS: ["distinct", "group", "sql", "limit", "compounds"]}}).as(module);
|
plugins/association.js
|
Coverage97.10
SLOC448
LOC69
Missed2
|
- 1
var associations = require("../associations"),oneToMany = associations.oneToMany,manyToOne = associations.manyToOne,oneToOne = associations.oneToOne,manyToMany = associations.manyToMany,fetch = associations.fetch,comb = require("comb"),asyncArray = comb.async.array,Promise = comb.Promise,PromiseList = comb.PromiseList;- 1
var RECIPROCAL_ASSOC = {"oneToOne": ["manyToOne"],"manyToOne": ["oneToMany", "oneToOne"],"oneToMany": ["manyToOne"],"manyToMany": ["manyToMany"]};- 1
exports.AssociationPlugin = comb.define(null, {instance: {/**@lends patio.Model.prototype*//*** @ignore* <p>Plugin to expose association capability.</p>** The associations exposed include** <ul>* <li><b>oneToMany</b> - Foreign key in associated model's table points to this* model's primary key. Each current model object can be associated with* more than one associated model objects. Each associated model object* can be associated with only one current model object.</li>* <li><b>manyToOne</b> - Foreign key in current model's table points to* associated model's primary key. Each associated model object can* be associated with more than one current model objects. Each current* model object can be associated with only one associated model object.</li>* <li><b>oneToOne</b> - Similar to one_to_many in terms of foreign keys, but* only one object is associated to the current object through the* association. The methods created are similar to many_to_one, except* that the one_to_one setter method saves the passed object./li>* <li><b>manyToMany</b> - A join table is used that has a foreign key that points* to this model's primary key and a foreign key that points to the* associated model's primary key. Each current model object can be* associated with many associated model objects, and each associated* model object can be associated with many current model objects./li>* </ul>** @constructs**/constructor: function () {- 2793
if (comb.isUndefinedOrNull(this.__associations)) {- 2685
this.__associations = {};}- 2793
this._super(arguments);},reload: function () {- 11
this.__associations = {};- 11
return this._super(arguments);},/**@ignore*/getters: {/**@lends patio.Model.prototype*//*** List of associations on the {@link patio.Model}* @field* @ignoreCode*/associations: function () {- 34
return this._static.associations;},/*** Returns true if this {@link patio.Model} has associations.* @field* @ignoreCode*/hasAssociations: function () {- 6
return this._static.hasAssociations;}}},static: {/**@lends patio.Model*/__associations: null,/*** Set to false to prevent an event from being emitted when an association is added to the model* @default true*/emitOnAssociationAdd: true,/*** @borrows _Association.fetch as fetch*/fetchType: fetch,/*** String for to signify an association as one to one.* @const*/ONE_TO_ONE: "oneToOne",/*** String for to signify an association as one to many.* @const*/ONE_TO_MANY: "oneToMany",/*** String for to signify an association as many to one.* @const*/MANY_TO_ONE: "manyToOne",/*** String for to signify an association as many to many.* @const*/MANY_TO_MANY: "manyToMany",/***Creates a ONE_TO_MANY association.* See {@link patio.plugins.AssociationPlugin.associate} for options.** @example*** //define the BiologicalFather model* patio.addModel("biologicalFather", {* static:{* init:function () {* this._super("arguments");* this.oneToMany("children");* }* }* });*** //define Child model* patio.addModel("child", {* static:{* init:function () {* this._super("arguments");* this.manyToOne("biologicalFather");* }* }* });**/oneToMany: function (name, options, filter) {- 13
return this.associate(this.ONE_TO_MANY, name, options, filter);},/*** Creates a MANY_TO_ONE association.* See {@link patio.plugins.AssociationPlugin.oneToMany}.* See {@link patio.plugins.AssociationPlugin.associate}*/manyToOne: function (name, options, filter) {- 16
return this.associate(this.MANY_TO_ONE, name, options, filter);},/*** Creates a ONE_TO_ONE relationship between models.* See {@link patio.plugins.AssociationPlugin.associate} for options.** @example** patio.addModel("state", {* static:{* init:function () {* this._super("arguments");* this.oneToOne("capital");* }* }* });** patio.addModel("capital", {* static:{* init:function () {* this._super("arguments");* this.manyToOne("state");* }* }* });*/oneToOne: function (name, options, filter) {- 8
return this.associate(this.ONE_TO_ONE, name, options, filter);},/*** Creates a MANY_TO_MANY relationship between models.* See {@link patio.plugins.AssociationPlugin.associate} for options.** @example** patio.addModel("class", {* static:{* init:function(){* this._super("arguments");* this.manyToMany("students", {fetchType:this.fetchType.EAGER, order : [sql.firstName.desc(), sql.lastName.desc()]});* }* }* });* patio.addModel("student", {* instance:{* enroll:function(clas){* if (comb.isArray(clas)) {* return this.addClasses(clas);* } else {* return this.addClass(clas);* }* }* },* static:{* init:function(){* this._super("arguments");* this.manyToMany("classes", {fetchType:this.fetchType.EAGER, order : sql.name.desc()});* }* }});**/manyToMany: function (name, options, filter) {- 12
return this.associate(this.MANY_TO_MANY, name, options, filter);},/*** Associates a related model with the current model. The following types are* supported:** <ul>* <li><b>oneToMany</b> - Foreign key in associated model's table points to this* model's primary key. Each current model object can be associated with* more than one associated model objects. Each associated model object* can be associated with only one current model object.</li>* <li><b>manyToOne</b> - Foreign key in current model's table points to* associated model's primary key. Each associated model object can* be associated with more than one current model objects. Each current* model object can be associated with only one associated model object.</li>* <li><b>oneToOne</b> - Similar to one_to_many in terms of foreign keys, but* only one object is associated to the current object through the* association. The methods created are similar to many_to_one, except* that the one_to_one setter method saves the passed object./li>* <li><b>manyToMany</b> - A join table is used that has a foreign key that points* to this model's primary key and a foreign key that points to the* associated model's primary key. Each current model object can be* associated with many associated model objects, and each associated* model object can be associated with many current model objects.</li>* </ul>** @param {patio.Model.ONE_TO_ONE|patio.Model.ONE_TO_MANY|patio.Model.MANY_TO_ONE|patio.Model.MANY_TO_MANY} type the* type of association that is to be created.* @param {String} name the name of the association, the name specified will be exposed as a property on instances* of the model.@param {Object} [options] additional options.* The following options can be supplied:* <ul>* <li><b>model</b> - The associated class or its name. If not given, uses the association's name,* which is singularized unless the type is MANY_TO_ONE or ONE_TO_ONE</li>* <li><b>query</b> - The conditions to use to filter the association, can be any argument passed* to {@link patio.Dataset#filter}.</li>* <li><b>dataset</b> - A function that is called in the scope of the model and called with the model as the* first argument. The function must return a dataset that can be used as the base for all dataset* operations.<b>NOTE:</b>The dataset returned will have all options applied to it.</li>* <li><b>distinct</b> Use the DISTINCT clause when selecting associated objects.* See {@link patio.Dataset#distinct}.</li>* <li><b>limit</b> : Limit the number of records to the provided value. Use* an array with two elements for the value to specify a limit (first element) and an* offset (second element). See {@link patio.Dataset#limit}.</li>* <li><b>order</b> : the column/s order the association dataset by. Can be a* one or more columns.* See {@link patio.Dataset#order}.</li>* <li><b>readOnly</b> : Do not add a setter method (for MANY_TO_ONE or ONE_TO_ONE associations),* or add/remove/removeAll methods (for ONE_TO_MANY and MANY_TO_MANY associations).</li>* <li><b>select</b> : the columns to select. Defaults to the associated class's* tableName.* in a MANY_TO_MANY association, which means it doesn't include the attributes from the* join table. If you want to include the join table attributes, you can* use this option, but beware that the join table attributes can clash with* attributes from the model table, so you should alias any attributes that have* the same name in both the join table and the associated table.</li>* </ul>* ManyToOne additional options:* <ul>* <li><b>key</b> : foreignKey in current model's table that references* associated model's primary key. Defaults to : "{tableName}Id". Can use an* array of strings for a composite key association.</li>* <li><b>primaryKey</b> : column in the associated table that the <b>key</b> option references.* Defaults to the primary key of the associated table. Can use an* array of strings for a composite key association.</li>* </ul>* OneToMany and OneToOne additional options:* <ul>* <li><b>key</b> : foreign key in associated model's table that references* current model's primary key, as a string. Defaults to* "{thisTableName}Id". Can use an array of columns for a composite key association.</li>* <li><b>primaryKey</b> : column in the current table that <b>key</b> option references.* Defaults to primary key of the current table. Can use an array of strings for a* composite key association.</li>* </ul>** ManyToMany additional options:* <ul>* <li><b>joinTable</b> : name of table that includes the foreign keys to both* the current model and the associated model. Defaults to the name* of current model and name of associated model, pluralized,* sorted, and joined with '' and camelized.* <li><b>leftKey</b> : foreign key in join table that points to current model's* primary key. Defaults to :"{tableName}Id".* Can use an array of strings for a composite key association.* <li><b>leftPrimaryKey</b> - column in current table that <b>leftKey</b> points to.* Defaults to primary key of current table. Can use an array of strings for a* composite key association.* <li><b>rightKey</b> : foreign key in join table that points to associated* model's primary key. Defaults to Defaults to :"{associatedTableName}Id".* Can use an array of strings for a composite key association.* <li><b>rightPrimaryKey</b> : column in associated table that <b>rightKey</b> points to.* Defaults to primary key of the associated table. Can use an* array of strings for a composite key association.* </ul>* @param {Function} [filter] optional function to filter the dataset after all other options have been applied.**/associate: function (type, name, options, filter) {- 49
if (comb.isFunction(options)) {- 1
filter = options;- 1
options = {};}- 49
this.__associations = this.__associations || {manyToMany: {}, oneToMany: {}, manyToOne: {}, oneToOne: {}};- 49
var assoc = new associations[type](comb.merge({model: name}, options), this.patio, filter);- 49
assoc.inject(this, name);- 49
this.__associations[type][name] = assoc;- 49
this.emit("associate", type, this);- 49
return this;},sync: function (cb) {- 2594
if (!this.synced && this.hasAssociations) {- 39
var self = this;- 39
return this._super().chain(function () {- 39
var associations = self.__associations;- 39
return asyncArray(Object.keys(associations)).map(function (type) {- 156
var types = associations[type];- 156
return asyncArray(Object.keys(types)).map(function (name) {- 49
return types[name].model.sync();}, 1);}, 1);});} else {- 2555
return this._super(arguments);}},__isReciprocalAssociation: function (assoc, pAssoc) {- 1012
var keys = assoc._getAssociationKey(), leftKey = keys[0], rightKey = keys[1];- 1012
var pKeys = pAssoc._getAssociationKey(), pLeftKey = pKeys[0], pRightKey = pKeys[1];- 1012
return leftKey.every(function (k, i) {- 1012
return pRightKey[i] === k;}) && rightKey.every(function (k, i) {- 1002
return pLeftKey[i] === k;}) && assoc.parent === pAssoc.model;},_findAssociation: function (assoc) {- 1002
var ret = null;- 1002
if (!comb.isEmpty(this.__associations)) {- 1002
var type = assoc.type, recipTypes = RECIPROCAL_ASSOC[type];- 1002
for (var i in recipTypes) {- 1019
var recipType = recipTypes[i];- 1019
var potentialAssociations = this.__associations[recipType];- 1019
var found = false;- 1019
for (var j in potentialAssociations) {- 1012
var pAssoc = potentialAssociations[j];- 1012
if (this.__isReciprocalAssociation(assoc, pAssoc)) {- 990
ret = [i, pAssoc], found = true;- 990
break;}}- 1019
if (found) {- 990
break;}}}- 1002
return ret;},_clearAssociationsForType: function (type, clazz, model) {- 0
this._findAssociationsForType(type, clazz).forEach(function (assoc) {- 0
assoc._clearAssociations(model);});},_reloadAssociationsForType: function (type, clazz, model) {- 99
var pl = this._findAssociationsForType(type, clazz).map(function (assoc) {- 217
return assoc._forceReloadAssociations(model);});- 99
return (pl.length ? new PromiseList(pl) : new Promise().callback()).promise();},_findAssociationsForType: function (type, clazz) {- 99
var associations = this.__associations[type], ret = [];- 99
for (var i in associations) {- 233
var assoc = associations[i];- 233
if (assoc.model === clazz) {- 217
ret.push(assoc);}}- 99
return ret;},/**@ignore*/getters: {/**@lends patio.plugins.AssociationPlugin*//*** A list of associated model names.* @field* @type String[]*/associations: function () {- 160
var ret = [], assoc = this.__associations;- 160
if (assoc != null) {- 112
Object.keys(assoc).forEach(function (k) {- 448
ret = ret.concat(Object.keys(assoc[k]));});}- 160
return ret;},/*** Returns true if this model has associations.* @field* @type Boolean*/hasAssociations: function () {- 92
return this.associations.length > 0;}}}});
|
plugins/validation.js
|
Coverage97.64
SLOC530
LOC127
Missed3
|
- 1
var comb = require("comb"),array = comb.array,compact = array.compact,flatten = array.flatten,toArray = array.toArray,net = require("net"),isIP = net.isIP,isIPv4 = net.isIPv4,isIPv6 = net.isIPv6,validator = require("validator"),validatorCheck = validator.check,dateCmp = comb.date.compare,isArray = comb.isArray,combDeepEqual = comb.deepEqual,combIsBoolean = comb.isBoolean,isString = comb.isString,combIsDefined = comb.isDefined,combIsNull = comb.isNull,ModelError = require("../errors.js").ModelError,isFunction = comb.isFunction,format = comb.string.format,Promise = comb.Promise,when = comb.when,merge = comb.merge,define = comb.define;- 1
var Validator = define(null, {instance:{constructor:function validator(col) {- 44
this.col = col;- 44
this.__actions = [];},__addAction:function __addAction(action, opts) {- 46
this.__actions.push({action:action,opts:merge({onlyDefined:true, onlyNotNull:false}, opts)});- 46
return this;},isAfter:function (date, opts) {- 1
opts = opts || {};- 1
var cmpDate = combIsBoolean(opts.date) ? opts.date : true;- 1
return this.__addAction(function (col) {- 3
return dateCmp(col, date, cmpDate ? "date" : "datetime") > 0;}, merge({message:"{col} must be after " + date + " got {val}."}, opts));},isBefore:function (date, opts) {- 1
opts = opts || {};- 1
var cmpDate = combIsBoolean(opts.date) ? opts.date : true;- 1
return this.__addAction(function (col) {- 3
return dateCmp(col, date, cmpDate ? "date" : "datetime") === -1;}, merge({message:"{col} must be before " + date + " got {val}."}, opts));},isDefined:function isDefined(opts) {- 1
return this.__addAction(function (col) {- 3
return combIsDefined(col);}, merge({message:"{col} must be defined.", onlyDefined:false, onlyNotNull:false}, opts));},isNotDefined:function isNotDefined(opts) {- 1
return this.__addAction(function (col) {- 3
return !combIsDefined(col);}, merge({message:"{col} cannot be defined.", onlyDefined:false, onlyNotNull:false}, opts));},isNotNull:function isNotNull(opts) {- 3
return this.__addAction(function (col) {- 21
return combIsDefined(col) && !combIsNull(col);}, merge({message:"{col} cannot be null.", onlyDefined:false, onlyNotNull:false}, opts));},isNull:function isNull(opts) {- 1
return this.__addAction(function (col) {- 3
return !combIsDefined(col) || combIsNull(col);}, merge({message:"{col} must be null got {val}.", onlyDefined:false, onlyNotNull:false}, opts));},isEq:function eq(val, opts) {- 4
return this.__addAction(function (col) {- 15
return combDeepEqual(col, val);}, merge({message:"{col} must === " + val + " got {val}."}, opts));},isNeq:function neq(val, opts) {- 2
return this.__addAction(function (col) {- 8
return !combDeepEqual(col, val);}, merge({message:"{col} must !== " + val + "."}, opts));},isLike:function like(val, opts) {- 3
return this.__addAction(function (col) {- 14
return !!col.match(val);}, merge({message:"{col} must be like " + val + " got {val}."}, opts));},isNotLike:function notLike(val, opts) {- 2
return this.__addAction(function (col) {- 6
return !(!!col.match(val));}, merge({message:"{col} must not be like " + val + "."}, opts));},isLt:function lt(num, opts) {- 1
return this.__addAction(function (col) {- 3
return col < num;}, merge({message:"{col} must be < " + num + " got {val}."}, opts));},isGt:function gt(num, opts) {- 1
return this.__addAction(function (col) {- 3
return col > num;}, merge({message:"{col} must be > " + num + " got {val}."}, opts));},isLte:function lte(num, opts) {- 1
return this.__addAction(function (col) {- 3
return col <= num;}, merge({message:"{col} must be <= " + num + " got {val}."}, opts));},isGte:function gte(num, opts) {- 1
return this.__addAction(function (col) {- 3
return col >= num;}, merge({message:"{col} must be >= " + num + " got {val}."}, opts));},isIn:function isIn(arr, opts) {- 2
if (!isArray(arr)) {- 1
throw new Error("isIn requires an array of values");}- 1
return this.__addAction(function (col) {- 3
return arr.indexOf(col) !== -1;}, merge({message:"{col} must be in " + arr.join(",") + " got {val}."}, opts));},isNotIn:function notIn(arr, opts) {- 2
if (!isArray(arr)) {- 1
throw new Error("notIn requires an array of values");}- 1
return this.__addAction(function (col) {- 3
return arr.indexOf(col) === -1;}, merge({message:"{col} cannot be in " + arr.join(",") + " got {val}."}, opts));},isMacAddress:function isMaxAddress(opts) {- 1
return this.__addAction(function (col) {- 4
return !!col.match(/^([0-9A-F]{2}[:\-]){5}([0-9A-F]{2})$/);}, merge({message:"{col} must be a valid MAC address got {val}."}, opts));},isIPAddress:function isIpAddress(opts) {- 1
return this.__addAction(function (col) {- 4
return !!isIP(col);}, merge({message:"{col} must be a valid IPv4 or IPv6 address got {val}."}, opts));},isIPv4Address:function isIpAddress(opts) {- 1
return this.__addAction(function (col) {- 3
return isIPv4(col);}, merge({message:"{col} must be a valid IPv4 address got {val}."}, opts));},isIPv6Address:function isIpAddress(opts) {- 1
return this.__addAction(function (col) {- 3
return isIPv6(col);}, merge({message:"{col} must be a valid IPv6 address got {val}."}, opts));},isUUID:function isUUID(opts) {- 1
return this.__addAction(function (col) {- 3
return !!col.match(/^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/);}, merge({message:"{col} must be a valid UUID got {val}"}, opts));},isEmail:function isEmail(opts) {- 1
return this.__addAction(function (col) {- 3
return validatorCheck(col).isEmail();}, merge({message:"{col} must be a valid Email Address got {val}"}, opts));},isUrl:function isUrl(opts) {- 1
return this.__addAction(function (col) {- 3
return validatorCheck(col).isUrl();}, merge({message:"{col} must be a valid url got {val}"}, opts));},isAlpha:function isAlpha(opts) {- 2
return this.__addAction(function (col) {- 11
return validatorCheck(col).isAlpha();}, merge({message:"{col} must be a only letters got {val}"}, opts));},isAlphaNumeric:function isAlphaNumeric(opts) {- 1
return this.__addAction(function (col) {- 3
return validatorCheck(col).isAlphanumeric();}, merge({message:"{col} must be a alphanumeric got {val}"}, opts));},hasLength:function hasLength(min, max, opts) {- 2
return this.__addAction(function (col) {- 6
return validatorCheck(col).len(min, max);}, merge({message:"{col} must have a length between " + min + (max ? " and " + max : "") + "."}, opts));},isLowercase:function isLowercase(opts) {- 1
return this.__addAction(function (col) {- 3
return validatorCheck(col).isLowercase();}, merge({message:"{col} must be lowercase got {val}."}, opts));},isUppercase:function isUppercase(opts) {- 1
return this.__addAction(function (col) {- 3
return validatorCheck(col).isUppercase();}, merge({message:"{col} must be uppercase got {val}."}, opts));},isEmpty:function isEmpty(opts) {- 1
return this.__addAction(function (col) {- 3
try {- 3
validatorCheck(col).notEmpty();- 2
return false;} catch (e) {- 1
return true;}}, merge({message:"{col} must be empty got {val}."}, opts));},isNotEmpty:function isNotEmpty(opts) {- 2
return this.__addAction(function (col) {- 11
return validatorCheck(col).notEmpty();}, merge({message:"{col} must not be empty."}, opts));},isCreditCard:function isCreditCard(opts) {- 0
return this.__addAction(function (col) {- 0
return validatorCheck(col).isCreditCard();}, merge({message:"{col} is an invalid credit card"}, opts));},check:function (fun, opts) {- 4
return this.__addAction(fun, opts);},validate:function validate(value) {- 218
var errOpts = {col:this.col, val:value};- 218
return compact(this.__actions.map(function (action) {- 248
var actionOpts = action.opts;- 248
if (!actionOpts.onlyDefined || (combIsDefined(value) &&(!actionOpts.onlyNotNull || !combIsNull(value)) )) {- 186
var ret = null;- 186
try {- 186
if (!action.action(value)) {- 71
ret = format(actionOpts.message, errOpts);}} catch (e) {- 26
ret = format(actionOpts.message, errOpts);}- 186
return ret;}}, this));}}});- 1
function shouldValidate(opts, def) {- 115
opts = opts || {};- 115
return combIsBoolean(opts.validate) ? opts.validate : def;}- 1
function validateHook(prop, next, opts) {- 115
if (shouldValidate(opts, prop) && !this.isValid()) {- 45
next(flatten(toArray(this.errors).map(function (entry) {- 64
return entry[1].map(function (err) {- 48
return new Error(err);});})));} else {- 70
next();}}- 1
define(null, {instance:{/*** A validation plugin for patio models. This plugin adds a <code>validate</code> method to each {@link patio.Model}* class that adds it as a plugin. This plugin does not include most typecast checks as <code>patio</code> already checks* types upon column assignment.** To do single col validation* {@code** var Model = patio.addModel("validator", {* plugins:[ValidatorPlugin]* });* //this ensures column assignment* Model.validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);* //col2 does not have to be assigned but if it is it must match /hello/ig.* Model.validate("col2").like(/hello/ig);* //Ensures that the emailAddress column is a valid email address.* Model.validate("emailAddress").isEmailAddress();* }** Or you can do a mass validation through a callback.* {@code** var Model = patio.addModel("validator", {* plugins:[ValidatorPlugin]* });* Model.validate(function(validate){* //this ensures column assignment* validate("col1").isNotNull().isAlphaNumeric().hasLength(1, 10);* //col2 does not have to be assigned but if it is it must match /hello/ig.* validate("col2").isLike(/hello/ig);* //Ensures that the emailAddress column is a valid email address.* validate("emailAddress").isEmail();* });* }** To check if a {@link patio.Model} is valid you can run the <code>isValid</code> method.** {@code* var model1 = new Model({col2 : 'grape', emailAddress : "test"}),* model2 = new Model({col1 : "grape", col2 : "hello", emailAddress : "test@test.com"});** model1.isValid() //false* model2.isValid() //true* }** To get the errors associated with an invalid model you can access the errors property** {@code* model1.errors; //{ col1: [ 'col1 must be defined.' ],* // col2: [ 'col2 must be like /hello/gi got grape.' ],* // emailAddress: [ 'emailAddress must be a valid Email Address got test' ] }* }** Validation is also run pre save and pre update. To prevent this you can specify the <code>validate</code> option** {@code* model1.save(null, {validate : false});* model2.save(null, {validate : false});* }** Or you can specify the class level properties <code>validateOnSave</code> and <code>validateOnUpdate</code>* to false respectively* {@code* Model.validateOnSave = false;* Model.validateOnUpdate = false;* }** Avaiable validation methods are.** <ul>* <li><code>isAfter</code> : check that a date is after a specified date</li>* <li><code>isBefore</code> : check that a date is after before a specified date </li>* <li><code>isDefined</code> : ensure that a column is defined</li>* <li><code>isNotDefined</code> : ensure that a column is not defined</li>* <li><code>isNotNull</code> : ensure that a column is defined and not null</li>* <li><code>isNull</code> : ensure that a column is not defined or null</li>* <li><code>isEq</code> : ensure that a column equals a value <b>this uses deep equal</b></li>* <li><code>isNeq</code> : ensure that a column does not equal a value <b>this uses deep equal</b></li>* <li><code>isLike</code> : ensure that a column is like a value, can be a regexp or string</li>* <li><code>isNotLike</code> : ensure that a column is not like a value, can be a regexp or string</li>* <li><code>isLt</code> : ensure that a column is less than a value</li>* <li><code>isGt</code> : ensure that a column is greater than a value</li>* <li><code>isLte</code> : ensure that a column is less than or equal to a value</li>* <li><code>isGte</code> : ensure that a column is greater than or equal to a value</li>* <li><code>isIn</code> : ensure that a column is contained in an array of values</li>* <li><code>isNotIn</code> : ensure that a column is not contained in an array of values</li>* <li><code>isMacAddress</code> : ensure that a column is a valid MAC address</li>* <li><code>isIPAddress</code> : ensure that a column is a valid IPv4 or IPv6 address</li>* <li><code>isIPv4Address</code> : ensure that a column is a valid IPv4 address</li>* <li><code>isIPv6Address</code> : ensure that a column is a valid IPv6 address</li>* <li><code>isUUID</code> : ensure that a column is a valid UUID</li>* <li><code>isEmail</code> : ensure that a column is a valid email address</li>* <li><code>isUrl</code> : ensure than a column is a valid URL</li>* <li><code>isAlpha</code> : ensure than a column is all letters</li>* <li><code>isAlphaNumeric</code> : ensure than a column is all letters or numbers</li>* <li><code>hasLength</code> : ensure than a column is fits within the specified length accepts a min and optional max value</li>* <li><code>isLowercase</code> : ensure than a column is lowercase</li>* <li><code>isUppercase</code> : ensure than a column is uppercase</li>* <li><code>isEmpty</code> : ensure than a column empty (i.e. a blank string)</li>* <li><code>isNotEmpty</code> : ensure than a column not empty (i.e. not a blank string)</li>* <li><code>isCreditCard</code> : ensure than a is a valid credit card number</li>* <li><code>check</code> : accepts a function to perform validation</li>* </ul>** All of the validation methods are chainable, and accept an options argument.** The options include* <ul>* <li><code>message</code> : a message to return if a column fails validation. The message can include <code>{val}</code> and <code>{col}</code>* replacements which will insert the invalid value and the column name.* </li>* <li><code>[onlyDefined=true]</code> : set to false to run the method even if the column value is not defined.</li>* <li><code>[onlyNotNull=true]</code> : set to false to run the method even if the column value is null.</li>* </ul>*** @constructs* @name ValidatorPlugin* @memberOf patio.plugins* @property {Object} [errors={}] the validation errors for this model.**/constructor:function () {- 114
this._super(arguments);- 114
this.errors = {};},/*** Validates a model, returning an array of error messages for each invalid property.* @return {String[]} an array of error messages for each invalid property.*/validate:function () {- 159
this.errors = {};- 159
return flatten(this._static.validators.map(function runValidator(validator) {- 218
var col = validator.col, val = this.__values[validator.col], ret = validator.validate(val);- 218
this.errors[col] = ret;- 218
return ret;}, this));},/*** Returns if this model passes validation.** @return {Boolean}*/isValid:function () {- 159
return this.validate().length === 0;}},"static":{/**@lends patio.plugins.ValidatorPlugin*//*** Set to false to prevent model validation when saving.* @default true*/validateOnSave:true,/*** Set to false to prevent model validation when updating.* @default true*/validateOnUpdate:true,__initValidation:function () {- 43
if (!this.__isValidationInited) {- 34
this.validators = [];- 34
this.pre("save", function preSaveValidate(next, opts) {- 114
validateHook.call(this, this._static.validateOnSave, next, opts);});- 34
this.pre("update", function preUpdateValidate(next, opts) {- 1
validateHook.call(this, this._static.validateOnSave, next, opts);});- 34
this.__isValidationInited = true;}},__getValidator:function validator(name) {- 44
var ret = new Validator(name);- 44
this.validators.push(ret);- 44
return ret;},/*** Sets up validation for a model.** To do single col validation* {@code** var Model = patio.addModel("validator", {* plugins:[ValidatorPlugin]* });* //this ensures column assignment* Model.validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);* //col2 does not have to be assigned but if it is it must match /hello/ig.* Model.validate("col2").like(/hello/ig);* //Ensures that the emailAddress column is a valid email address.* Model.validate("emailAddress").isEmailAddress();* }** Or you can do a mass validation through a callback.* {@code** var Model = patio.addModel("validator", {* plugins:[ValidatorPlugin]* });* Model.validate(function(validate){* //this ensures column assignment* validate("col1").isDefined().isAlphaNumeric().hasLength(1, 10);* //col2 does not have to be assigned but if it is it must match /hello/ig.* validate("col2").isLike(/hello/ig);* //Ensures that the emailAddress column is a valid email address.* validate("emailAddress").isEmail();* });* }*** @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) {- 43
this.__initValidation();- 43
var ret;- 43
if (isFunction(name)) {- 1
name.call(this, this.__getValidator.bind(this));- 1
ret = this;- 42
} else if (isString(name)) {- 42
ret = this.__getValidator(name);} else {- 0
throw new ModelError("name is must be a string or function when validating");}- 43
return ret;}}}).as(module);
|
dataset/query.js
|
Coverage97.82
SLOC2356
LOC458
Missed10
|
- 1
var comb = require("comb"),hitch = comb.hitch,array = comb.array,flatten = array.flatten,compact = array.compact,define = comb.define,argsToArray = comb.argsToArray,isString = comb.isString,isEmpty = comb.isEmpty,isNull = comb.isNull,isBoolean = comb.isBoolean,isNumber = comb.isNumber,merge = comb.merge,isArray = comb.isArray,isObject = comb.isObject,isFunction = comb.isFunction,isUndefined = comb.isUndefined,isHash = comb.isHash,isInstanceOf = comb.isInstanceOf,sql = require("../sql").sql,LiteralString = sql.LiteralString,Expression = sql.Expression,ComplexExpression = sql.ComplexExpression,BooleanExpression = sql.BooleanExpression,PlaceHolderLiteralString = sql.PlaceHolderLiteralString,Identifier = sql.Identifier,QualifiedIdentifier = sql.QualifiedIdentifier,AliasedExpression = sql.AliasedExpression,StringExpression = sql.StringExpression,NumericExpression = sql.NumericExpression,OrderedExpression = sql.OrderedExpression,JoinClause = sql.JoinClause,JoinOnClause = sql.JoinOnClause,JoinUsingClause = sql.JoinUsingClause,ColumnAll = sql.ColumnAll,QueryError = require("../errors").QueryError;- 1
var Dataset;- 1
function conditionedJoin(type) {- 719
var args = argsToArray(arguments, 1);- 719
return this.joinTable.apply(this, [type].concat(args));}- 1
function unConditionJoin(type, table) {- 6
var args = argsToArray(arguments, 1);- 6
return this.joinTable.apply(this, [type, table]);}- 1
define({/**@ignore*/instance: {/**@lends patio.Dataset.prototype*//*** @ignore*/constructor: function () {- 27052
!Dataset && (Dataset = require("../index").Dataset);- 27052
this._super(arguments);- 27052
this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {- 189364
if (!this[type + "Join"]) {- 189364
this[type + "Join"] = hitch(this, conditionedJoin, type);}}, this);- 27052
this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {- 135260
if (!this[type + "Join"]) {- 135260
this[type + "Join"] = hitch(this, unConditionJoin, type);}}, this);},/*** Adds a RETURNING clause, which is not supported by all databases. If returning is* used instead of returning the autogenerated primary key or update/delete returning the number of rows modified.** @example** ds.from("items").returning() //"RETURNING *"* ds.from("items").returning(null) //"RETURNING NULL"* ds.from("items").returning("id", "name") //"RETURNING id, name"* ds.from("items").returning(["id", "name"]) //"RETURNING id, name"** @param values columns to return. If values is an array then the array is assumed to contain the columns to* return. Otherwise the arguments will be used.* @return {patio.Dataset} a new dataset with the retuning option added.*/returning: function (values) {- 1118
var args;- 1118
if (Array.isArray(values)) {- 0
args = values;} else {- 1118
args = argsToArray(arguments);}- 1118
return this.mergeOptions({returning: args.map(function (v) {- 1029
return isString(v) ? sql.stringToIdentifier(v) : v;})});},/*** Adds a further filter to an existing filter using AND. This method is identical to {@link patio.Dataset#filter}* except it expects an existing filter.** <p>* <b>For parameter types see {@link patio.Dataset#filter}.</b>* </p>** @example* DB.from("table").filter("a").and("b").sql;* //=>SELECT * FROM table WHERE a AND b** @throws {patio.QueryError} If no WHERE?HAVING clause exists.** @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.*/and: function () {- 7
var tOpts = this.__opts, clauseObj = tOpts[tOpts.having ? "having" : "where"];- 7
if (clauseObj) {- 6
return this.filter.apply(this, arguments);} else {- 1
throw new QueryError("No existing filter found");}},as: function (alias) {- 8
return new AliasedExpression(this, alias);},/*** Adds an alternate filter to an existing WHERE/HAVING using OR.** <p>* <b>For parameter types see {@link patio.Dataset#filter}.</b>* </p>** @example** DB.from("items").filter("a").or("b")* //=> SELECT * FROM items WHERE a OR b** @throws {patio.QueryError} If no WHERE?HAVING clause exists.* @return {patio.Dataset} a cloned dataset with the condition added to the WHERE/HAVING clause added.*/or: function () {- 12
var tOpts = this.__opts;- 12
var clause = (tOpts.having ? "having" : "where"), clauseObj = tOpts[clause];- 12
if (clauseObj) {- 11
var args = argsToArray(arguments);- 11
args = args.length === 1 ? args[0] : args;- 11
var opts = {};- 11
opts[clause] = new BooleanExpression("OR", clauseObj, this._filterExpr(args));- 11
return this.mergeOptions(opts);} else {- 1
throw new QueryError("No existing filter found");}},/*** Adds a group of ORed conditions wrapped in parens, connected to an existing where/having clause by an AND.* If the where/having clause doesn't yet exist, a where clause is created with the ORed group.** <p>* <b>For parameter types see {@link patio.Dataset#filter}.</b>* </p>** @example** DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))** DB.from("items").andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((price < 0) OR (price > 10))** DB.from("items").filter({x:1}).andGroupedOr([{a:1, b:2}, {c:3, d:4}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((x = 1) AND (((a = 1) AND (b = 2)) OR ((c = 3) AND (d = 4)))** @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.*/andGroupedOr: function (filterExp) {- 5
return this._addGroupedCondition("AND", "OR", filterExp);},/*** Adds a group of ANDed conditions wrapped in parens to an existing where/having clause by an AND. If there isn't* yet a clause, a where clause is created with the ANDed conditions** <p>* <b>For parameter types see {@link patio.Dataset#filter}.</b>* </p>** @example** DB.from("items").filter({id, [1,2,3]}).andGroupedAnd([{price: {gt : 0}}, {price: {lt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((id IN (1, 2, 3)) AND ((price > 0) AND (price < 10)))** DB.from("items").andGroupedAnd([{price: {gt : 0}}, {price: {lt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((price > 0) AND (price < 10))** @return {patio.Dataset} a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.*/andGroupedAnd: function (filterExp) {- 5
return this._addGroupedCondition("AND", "AND", filterExp);},/*** Adds a group of ANDed conditions wrapped in parens to an existing where/having clause by an OR. If there isn't* a where/having clause, a where clause is created with the ANDed conditions.** <p>* <b>For parameter types see {@link patio.Dataset#filter}.</b>* </p>** @example** DB.from("items").filter({id, [1,2,3]}).orGroupedAnd([{price: {lt : 0}}, {price: {gt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((id IN (1, 2, 3)) OR ((price > 0) AND (price < 10)))** DB.from("items").orGroupedAnd([{price: {gt : 0}}, {price: {gt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((price > 0) AND (price < 10))** @return {patio.Dataset} a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.*/orGroupedAnd: function () {- 5
var tOpts = this.__opts,clause = (tOpts.having ? "having" : "where"),clauseObj = tOpts[clause];- 5
if (clauseObj) {- 3
return this.or.apply(this, arguments);} else {- 2
var args = argsToArray(arguments);- 2
args = args.length === 1 ? args[0] : args;- 2
var opts = {};- 2
opts[clause] = this._filterExpr(args, null, "AND");- 2
return this.mergeOptions(opts);}},/*** Adds a group of ORed conditions wrapped in parens to an existing having/where clause with an OR. If there isn't* already a clause, a where clause is created with the ORed group.** <p>* <b>For parameter types see {@link patio.Dataset#filter}.</b>* </p>** @example** DB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))** DB.from("items").orGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((price < 0) OR (price > 10))** @return {patio.Dataset} a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.*/orGroupedOr: function (filterExp) {- 5
return this._addGroupedCondition("OR", "OR", filterExp);},/*** Returns a copy of the dataset with the SQL DISTINCT clause.* The DISTINCT clause is used to remove duplicate rows from the* output. If arguments are provided, uses a DISTINCT ON clause,* in which case it will only be distinct on those columns, instead* of all returned columns.** @example** DB.from("items").distinct().sqll* //=> SELECT DISTINCT * FROM items* DB.from("items").order("id").distinct("id").sql;* //=> SELECT DISTINCT ON (id) * FROM items ORDER BY id** @throws {patio.QueryError} If arguments are given and DISTINCT ON is not supported.* @param {...String|...patio.sql.Identifier} args variable number of arguments used to create* the DISTINCT ON clause.* @return {patio.Dataset} a cloned dataset with the DISTINCT/DISTINCT ON clause added.*/distinct: function (args) {- 13
args = argsToArray(arguments);- 13
if (args.length && !this.supportsDistinctOn) {- 1
throw new QueryError("DISTICT ON is not supported");}- 12
args = args.map(function (a) {- 6
return isString(a) ? new Identifier(a) : a;});- 12
return this.mergeOptions({distinct: args});},/*** Adds an EXCEPT clause using a second dataset object.* An EXCEPT compound dataset returns all rows in the current dataset* that are not in the given dataset.** @example** DB.from("items").except(DB.from("other_items")).sql;* //=> SELECT * FROM items EXCEPT SELECT * FROM other_items** DB.from("items").except(DB.from("other_items"),* {all : true, fromSelf : false}).sql;* //=> SELECT * FROM items EXCEPT ALL SELECT * FROM other_items** DB.from("items").except(DB.from("other_items"),* {alias : "i"}).sql;* //=>SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS i** @throws {patio.QueryError} if the operation is not supported.* @param {patio.Dataset} dataset the dataset to use to create the EXCEPT clause.* @param {Object} [opts] options to use when creating the EXCEPT clause* @param {String|patio.sql.Identifier} [opt.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias.* @param {Boolean} [opts.all] Set to true to use EXCEPT ALL instead of EXCEPT, so duplicate rows can occur* @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}, use with care.** @return {patio.Dataset} a cloned dataset with the EXCEPT clause added.*/except: function (dataset, opts) {- 18
opts = isUndefined(opts) ? {} : opts;- 18
if (!isHash(opts)) {- 5
opts = {all: true};}- 18
if (!this.supportsIntersectExcept) {- 2
throw new QueryError("EXCEPT not supoorted");- 16
} else if (opts.hasOwnProperty("all") && !this.supportsIntersectExceptAll) {- 1
throw new QueryError("EXCEPT ALL not supported");}- 15
return this.compoundClone("except", dataset, opts);},/*** Performs the inverse of {@link patio.Dataset#filter}. Note that if you have multiple filter* conditions, this is not the same as a negation of all conditions. For argument types see* {@link patio.Dataset#filter}** @example** DB.from("items").exclude({category : "software").sql;* //=> SELECT * FROM items WHERE (category != 'software')** DB.from("items").exclude({category : 'software', id : 3}).sql;* //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))* @return {patio.Dataset} a cloned dataset with the excluded conditions applied to the HAVING/WHERE clause.*/exclude: function () {- 105
var cond = argsToArray(arguments), tOpts = this.__opts;- 105
var clause = (tOpts["having"] ? "having" : "where"), clauseObj = tOpts[clause];- 105
cond = cond.length > 1 ? cond : cond[0];- 105
cond = this._filterExpr.call(this, cond);- 105
cond = BooleanExpression.invert(cond);- 105
if (clauseObj) {- 94
cond = new BooleanExpression("AND", clauseObj, cond);}- 105
var opts = {};- 105
opts[clause] = cond;- 105
return this.mergeOptions(opts);},/*** Returns a copy of the dataset with the given conditions applied to it.* If the query already has a HAVING clause, then the conditions are applied to the* HAVING clause otherwise they are applied to the WHERE clause.** @example** DB.from("items").filter({id : 3}).sql;* //=> SELECT * FROM items WHERE (id = 3)** DB.from("items").filter('price < ?', 100)* //=> SELECT * FROM items WHERE price < 100** DB.from("items").filter({id, [1,2,3]}).filter({id : {between : [0, 10]}}).sql;* //=> SELECT* ** FROM* items* WHERE* ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))** DB.from("items").filter('price < 100');* //=> SELECT * FROM items WHERE price < 100** DB.from("items").filter("active").sql;* //=> SELECT * FROM items WHERE active** DB.from("items").filter(function(){* return this.price.lt(100);* });* //=> SELECT * FROM items WHERE (price < 100)** //Multiple filter calls can be chained for scoping:* DB.from("items").filter(:category => 'software').filter{price < 100}* //=> SELECT * FROM items WHERE ((category = 'software') AND (price < 100))*** @param {Object|Array|String|patio.sql.Identifier|patio.sql.BooleanExpression} args filters to apply to the* WHERE/HAVING clause. Description of each:* <ul>* <li>Hash - list of equality/inclusion expressions</li>* <li>Array - depends:* <ul>* <li>If first member is a string, assumes the rest of the arguments* are parameters and interpolates them into the string.</li>* <li>If all members are arrays of length two, treats the same way* as a hash, except it allows for duplicate keys to be* specified.</li>* <li>Otherwise, treats each argument as a separate condition.</li>* </ul>* </li>* <li>String - taken literally</li>* <li>{@link patio.sql.Identifier} - taken as a boolean column argument (e.g. WHERE active)</li>* <li>{@link patio.sql.BooleanExpression} - an existing condition expression,* probably created using the patio.sql methods.* </li>** @param {Function} [cb] filter also takes a cb, which should return one of the above argument* types, and is treated the same way. This block is called with an {@link patio.sql} object which can be used to dynaically create expression. For more details* on the sql object see {@link patio.sql}** <p>* <b>NOTE:</b>If both a cb and regular arguments are provided, they get ANDed together.* </p>** @return {patio.Dataset} a cloned dataset with the filter arumgents applied to the WHERE/HAVING clause.**/filter: function (args, cb) {- 3465
args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));- 3465
return this._filter.apply(this, args);},/*** @see patio.Dataset#filter*/find: function () {- 30
var args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments));- 30
return this._filter.apply(this, args);},/*** @example* DB.from("table").forUpdate()* //=> SELECT * FROM table FOR UPDATE* @return {patio.Dataset} a cloned dataset with a "update" lock style.*/forUpdate: function () {- 1
return this.lockStyle("update");},/*** Returns a copy of the dataset with the source changed. If no* source is given, removes all tables. If multiple sources* are given, it is the same as using a CROSS JOIN (cartesian product) between all tables.** @example* var dataset = DB.from("items");** dataset.from().sql;* //=> SELECT *** dataset.from("blah").sql* //=> SELECT * FROM blah** dataset.from("blah", "foo")* //=> SELECT * FROM blah, foo** dataset.from({a:"b"}).sql;* //=> SELECT * FROM a AS b** dataset.from(dataset.from("a").group("b").as("c")).sql;* //=> "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"** @param {...String|...patio.sql.Identifier|...patio.Dataset|...Object} [source] tables to select from** @return {patio.Dataset} a cloned dataset with the FROM clause overridden.*/from: function (source) {- 876
source = argsToArray(arguments);- 876
var tableAliasNum = 0, sources = [];- 876
source.forEach(function (s) {- 1064
if (isInstanceOf(s, Dataset)) {- 86
sources.push(new AliasedExpression(s, this._datasetAlias(++tableAliasNum)));- 978
} else if (isHash(s)) {- 3
for (var i in s) {- 3
sources.push(new AliasedExpression(new Identifier(i), s[i]));}- 975
} else if (isString(s)) {- 950
sources.push(this.stringToIdentifier(s));} else {- 25
sources.push(s);}}, this);- 876
var o = {from: sources.length ? sources : null};- 876
if (tableAliasNum) {- 84
o.numDatasetSources = tableAliasNum;}- 876
return this.mergeOptions(o);},/*** Returns a dataset selecting from the current dataset.* Supplying the alias option controls the alias of the result.** @example** ds = DB.from("items").order("name").select("id", "name")* //=> SELECT id,name FROM items ORDER BY name** ds.fromSelf().sql;* //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1** ds.fromSelf({alias : "foo"}).sql;* //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo** @param {Object} [opts] options* @param {String|patio.sql.Identifier} [opts.alias] alias to use** @return {patio.Dataset} a cloned dataset with the FROM clause set as the current dataset.*/fromSelf: function (opts) {- 84
opts = isUndefined(opts) ? {} : opts;- 84
var fs = {};- 84
var nonSqlOptions = this._static.NON_SQL_OPTIONS;- 84
Object.keys(this.__opts).forEach(function (k) {- 302
if (nonSqlOptions.indexOf(k) === -1) {- 302
fs[k] = null;}});- 84
return this.mergeOptions(fs).from(opts["alias"] ? this.as(opts["alias"]) : this);},/*** Match any of the columns to any of the patterns. The terms can be* strings (which use LIKE) or regular expressions (which are only* supported on MySQL and PostgreSQL). Note that the total number of* pattern matches will be columns[].length * terms[].length,* which could cause performance issues.** @example** DB.from("items").grep("a", "%test%").sql;* //=> SELECT * FROM items WHERE (a LIKE '%test%');** DB.from("items").grep(["a", "b"], ["%test%" "foo"]).sql;* //=> SELECT * FROM items WHERE ((a LIKE '%test%') OR (a LIKE 'foo') OR (b LIKE '%test%') OR (b LIKE 'foo'))** DB.from("items").grep(['a', 'b'], ["%foo%", "%bar%"], {allPatterns : true}).sql;* //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (b LIKE '%foo%')) AND ((a LIKE '%bar%') OR (b LIKE '%bar%')))** DB.from("items").grep(["a", "b"], ['%foo%", "%bar%", {allColumns : true})sql;* //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (a LIKE '%bar%')) AND ((b LIKE '%foo%') OR (b LIKE '%bar%')))** DB.from("items").grep(["a", "b"], ["%foo%", "%bar%"], {allPatterns : true, allColumns : true}).sql;* //=> SELECT * FROM a WHERE ((a LIKE '%foo%') AND (b LIKE '%foo%') AND (a LIKE '%bar%') AND (b LIKE '%bar%'))** @param {String[]|patio.sql.Identifier[]} columns columns to search* @param {String|RegExp} patterns patters to search with* @param {Object} [opts] options to use when searching. NOTE If both allColumns and allPatterns are true, all columns must match all patterns* @param {Boolean} [opts.allColumns] All columns must be matched to any of the given patterns.* @param {Boolean} [opts.allPatterns] All patterns must match at least one of the columns.* @param {Boolean} [opts.caseInsensitive] Use a case insensitive pattern match (the default is* case sensitive if the database supports it).* @return {patio.Dataset} a dataset with the LIKE clauses added*/grep: function (columns, patterns, opts) {- 15
opts = isUndefined(opts) ? {} : opts;- 15
var conds;- 15
if (opts.hasOwnProperty("allPatterns")) {- 4
conds = array.toArray(patterns).map(function (pat) {- 8
return BooleanExpression.fromArgs([(opts.allColumns ? "AND" : "OR")].concat(array.toArray(columns).map(function (c) {- 16
return StringExpression.like(c, pat, opts);})));});- 4
return this.filter(BooleanExpression.fromArgs([opts.allPatterns ? "AND" : "OR"].concat(conds)));} else {- 11
conds = array.toArray(columns).map(function (c) {- 16
return BooleanExpression.fromArgs(["OR"].concat(array.toArray(patterns).map(function (pat) {- 26
return StringExpression.like(c, pat, opts);})));});- 11
return this.filter(BooleanExpression.fromArgs([opts.allColumns ? "AND" : "OR"].concat(conds)));}},/*** @see patio.Dataset#grep*/like: function () {- 1
return this.grep.apply(this, arguments);},/*** Returns a copy of the dataset with the results grouped by the value of* the given columns.* @example* DB.from("items").group("id")* //=>SELECT * FROM items GROUP BY id* DB.from("items").group("id", "name")* //=> SELECT * FROM items GROUP BY id, name* @param {String...|patio.sql.Identifier...} columns columns to group by.** @return {patio.Dataset} a cloned dataset with the GROUP BY clause added.**/group: function (columns) {- 32
columns = argsToArray(arguments);- 32
var self = this;- 32
return this.mergeOptions({group: (array.compact(columns).length === 0 ? null : columns.map(function (c) {- 32
return isString(c) ? self.stringToIdentifier(c) : c;}))});},/*** @see patio.Dataset#group*/groupBy: function () {- 10
return this.group.apply(this, arguments);},/*** Returns a dataset grouped by the given column with count by group.* Column aliases may be supplied, and will be included in the select clause.** @example** DB.from("items").groupAndCount("name").all()* //=> SELECT name, count(*) AS count FROM items GROUP BY name* //=> [{name : 'a', count : 1}, ...]** DB.from("items").groupAndCount("first_name", "last_name").all()* //SELECT first_name, last_name, count(*) AS count FROM items GROUP BY first_name, last_name* //=> [{first_name : 'a', last_name : 'b', count : 1}, ...]** DB.from("items").groupAndCount("first_name___name").all()* //=> SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name* //=> [{name : 'a', count:1}, ...]* @param {String...|patio.sql.Identifier...} columns columns to croup and count on.** @return {patio.Dataset} a cloned dataset with the GROUP clause and count added.*/groupAndCount: function (columns) {- 8
columns = argsToArray(arguments);- 8
var group = this.group.apply(this, columns.map(function (c) {- 9
return this._unaliasedIdentifier(c);}, this));- 8
return group.select.apply(group, columns.concat([this._static.COUNT_OF_ALL_AS_COUNT]));},/*** Returns a copy of the dataset with the HAVING conditions changed. See {@link patio.Dataset#filter} for argument types.** @example* DB.from("items").group("sum").having({sum : 10}).sql;* //=> SELECT * FROM items GROUP BY sum HAVING (sum = 10)** @return {patio.Dataset} a cloned dataset with HAVING clause changed or added.**/having: function () {- 12
var cond = argsToArray(arguments).map(function (s) {- 12
return isString(s) && s !== '' ? this.stringToIdentifier(s) : s;}, this);- 12
return this._filter.apply(this, ["having"].concat(cond));},/*** Adds an INTERSECT clause using a second dataset object.* An INTERSECT compound dataset returns all rows in both the current dataset* and the given dataset.** @example** DB.from("items").intersect(DB.from("other_items")).sql;* //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS t1** DB.from("items").intersect(DB.from("other_items"), {all : true, fromSelf : false}).sql;* //=> SELECT * FROM items INTERSECT ALL SELECT * FROM other_items** DB.from("items").intersect(DB.from("other_items"), {alias : "i"}).sql;* //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS i** @throws {patio.QueryError} if the operation is not supported.* @param {patio.Dataset} dataset the dataset to intersect* @param {Object} [opts] options* @param {String|patio.sql.Identifier} [opts.alias] Use the given value as the {@link patio.Dataset#fromSelf} alias* @param {Boolean} [opts.all] Set to true to use INTERSECT ALL instead of INTERSECT, so duplicate rows can occur* @param {Boolean} [opts.fromSelf] Set to false to not wrap the returned dataset in a {@link patio.Dataset#fromSelf}.** @return {patio.Dataset} a cloned dataset with the INTERSECT clause.**/intersect: function (dataset, opts) {- 18
opts = isUndefined(opts) ? {} : opts;- 18
if (!isHash(opts)) {- 5
opts = {all: opts};}- 18
if (!this.supportsIntersectExcept) {- 2
throw new QueryError("INTERSECT not supported");- 16
} else if (opts.all && !this.supportsIntersectExceptAll) {- 1
throw new QueryError("INTERSECT ALL not supported");}- 15
return this.compoundClone("intersect", dataset, opts);},/*** Inverts the current filter.** @example* DB.from("items").filter({category : 'software'}).invert()* //=> SELECT * FROM items WHERE (category != 'software')** @example* DB.from("items").filter({category : 'software', id : 3}).invert()* //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))** @return {patio.Dataset} a cloned dataset with the filter inverted.**/invert: function () {- 3
var having = this.__opts.having, where = this.__opts.where;- 3
if (!(having || where)) {- 1
throw new QueryError("No current filter");}- 2
var o = {};- 2
if (having) {- 1
o.having = BooleanExpression.invert(having);}- 2
if (where) {- 2
o.where = BooleanExpression.invert(where);}- 2
return this.mergeOptions(o);},/*** Returns a cloned dataset with an inner join applied.** @see patio.Dataset#joinTable*/join: function () {- 320
return this.innerJoin.apply(this, arguments);},/*** Returns a joined dataset. Uses the following arguments:** @example** DB.from("items").joinTable("leftOuter", "categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;* //=>'SELECT* ** FROM* `items`* LEFT OUTER JOIN* `categories` ON (* (`categories`.`categoryId` = `items`.`id`)* AND* (`categories`.`categoryId` IN (1,2, 3))* )* DB.from("items").leftOuter("categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql;* //=>'SELECT* ** FROM* `items`* LEFT OUTER JOIN* `categories` ON (* (`categories`.`categoryId` = `items`.`id`)* AND* (`categories`.`categoryId` IN (1,2, 3))* )** DB.from("items").leftOuterJoin("categories", {categoryId:"id"}).sql* //=> SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")** DB.from("items").rightOuterJoin("categories", {categoryId:"id"}).sql* //=> SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")** DB.from("items").fullOuterJoin("categories", {categoryId:"id"}).sql* //=> SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")** DB.from("items").innerJoin("categories", {categoryId:"id"}).sql* //=> SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."categoryId" = "items"."id")** DB.from("items").leftJoin("categories", {categoryId:"id"}).sql* //=> SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."categoryId" = "items"."id")** DB.from("items").rightJoin("categories", {categoryId:"id"}).sql* //=> SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."categoryId" = "items"."id")** DB.from("items").fullJoin("categories", {categoryId:"id"}).sql* //=> SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."categoryId" = "items"."id")** DB.from("items").naturalJoin("categories").sql* //=> SELECT * FROM "items" NATURAL JOIN "categories"** DB.from("items").naturalLeftJoin("categories").sql* //=> SELECT * FROM "items" NATURAL LEFT JOIN "categories"** DB.from("items").naturalRightJoin("categories").sql* //=> SELECT * FROM "items" NATURAL RIGHT JOIN "categories"** DB.from("items").naturalFullJoin("categories").sql* //=> SELECT * FROM "items" NATURAL FULL JOIN "categories"'** DB.from("items").crossJoin("categories").sql* //=> SELECT * FROM "items" CROSS JOIN "categories"** @param {String} type the type of join to do.* @param {String|patio.sql.Identifier|patio.Dataset|Object} table depends on the type.* <ul>* <li>{@link patio.Dataset} - a subselect is performed with an alias</li>* <li>Object - an object that has a tableName property.</li>* <li>String|{@link patio.sql.Identifier} - the name of the table</li>* </ul>* @param [expr] - depends on type* <ul>* <li>Object|Array of two element arrays - Assumes key (1st arg) is column of joined table (unless already* qualified), and value (2nd arg) is column of the last joined or primary table (or the* implicitQualifier option</li>.* <li>Array - If all members of the array are string or {@link patio.sql.Identifier}, considers* them as columns and uses a JOIN with a USING clause. Most databases will remove duplicate columns from* the result set if this is used.</li>* <li>null|undefined(not passed in) - If a cb is not given, doesn't use ON or USING, so the JOIN should be a NATURAL* or CROSS join. If a block is given, uses an ON clause based on the block, see below.</li>* <li>Everything else - pretty much the same as a using the argument in a call to {@link patio.Dataset#filter},* so strings are considered literal, {@link patio.sql.Identifiers} specify boolean columns, and patio.sql* expressions can be used. Uses a JOIN with an ON clause.</li>* </ul>* @param {Object} options an object of options.* @param {String|patio.sql.Identifier} [options.tableAlias=undefined] the name of the table's alias when joining, necessary for joining* to the same table more than once. No alias is used by default.* @param {String|patio.sql.Identifier} [options.implicitQualifier=undefined] The name to use for qualifying implicit conditions. By default,* the last joined or primary table is used.* @param {Function} [cb] cb - The cb argument should only be given if a JOIN with an ON clause is used,* in which case it is called with* <ul>* <li>table alias/name for the table currently being joined</li>* <li> the table alias/name for the last joined (or first table)* <li>array of previous</li>* </ul>* the cb should return an expression to be used in the ON clause.** @return {patio.Dataset} a cloned dataset joined using the arguments.*/joinTable: function (type, table, expr, options, cb) {- 814
var args = argsToArray(arguments);- 814
if (isFunction(args[args.length - 1])) {- 12
cb = args[args.length - 1];- 12
args.pop();} else {- 802
cb = null;}- 814
type = args.shift(), table = args.shift(), expr = args.shift(), options = args.shift();- 814
expr = isUndefined(expr) ? null : expr, options = isUndefined(options) ? {} : options;- 814
var h;- 814
var usingJoin = isArray(expr) && expr.length && expr.every(function (x) {- 223
return isString(x) || isInstanceOf(x, Identifier);});- 814
if (usingJoin && !this.supportsJoinUsing) {- 1
h = {};- 1
expr.forEach(function (s) {- 1
h[s] = s;});- 1
return this.joinTable(type, table, h, options);}- 813
var tableAlias, lastAlias;- 813
if (isHash(options)) {- 803
tableAlias = options.tableAlias;- 803
lastAlias = options.implicitQualifier;- 10
} else if (isString(options) || isInstanceOf(options, Identifier)) {- 9
tableAlias = options;- 9
lastAlias = null;} else {- 1
throw new QueryError("Invalid options format for joinTable %j4", [options]);}- 812
var tableAliasNum, tableName;- 812
if (isInstanceOf(table, Dataset)) {- 11
if (!tableAlias) {- 6
tableAliasNum = (this.__opts.numDatasetSources || 0) + 1;- 6
tableAlias = this._datasetAlias(tableAliasNum);}- 11
tableName = tableAlias;} else {- 801
if (!isUndefined(table.tableName)) {- 1
table = table.tableName;}- 801
if (isArray(table)) {- 2
table = table.map(this.stringToIdentifier, this);} else {- 799
table = isString(table) ? this.stringToIdentifier(table) : table;- 799
var parts = this._splitAlias(table), implicitTableAlias = parts[1];- 799
table = parts[0];- 799
tableAlias = tableAlias || implicitTableAlias;- 799
tableName = tableAlias || table;}}- 812
var join;- 812
if (!expr && !cb) {- 22
join = new JoinClause(type, table, tableAlias);- 790
} else if (usingJoin) {- 9
if (cb) {- 1
throw new QueryError("cant use a cb if an array is given");}- 8
join = new JoinUsingClause(expr, type, table, tableAlias);} else {- 781
lastAlias = lastAlias || this.__opts["lastJoinedTable"] || this.firstSourceAlias;- 780
if (Expression.isConditionSpecifier(expr)) {- 768
var newExpr = [];- 768
for (var i in expr) {- 1161
var val = expr[i];- 1161
if (isArray(val) && val.length === 2) {- 418
i = val[0], val = val[1];}- 1161
var k = this.qualifiedColumnName(i, tableName), v;- 1161
if (isInstanceOf(val, Identifier)) {- 477
v = val.qualify(lastAlias);} else {- 684
v = val;}- 1161
newExpr.push([k, v]);}- 768
expr = newExpr;}- 780
if (isFunction(cb)) {- 11
var expr2 = cb.apply(sql, [tableName, lastAlias, this.__opts.join || []]);- 11
expr = expr ? new BooleanExpression("AND", expr, expr2) : expr2;}- 780
join = new JoinOnClause(expr, type, table, tableAlias);}- 810
var opts = {join: (this.__opts.join || []).concat([join]), lastJoinedTable: tableName};- 810
if (tableAliasNum) {- 6
opts.numDatasetSources = tableAliasNum;}- 810
return this.mergeOptions(opts);},/*** If given an integer, the dataset will contain only the first l results.If a second argument is given, it is used as an offset. To use* an offset without a limit, pass null as the first argument.** @example** DB.from("items").limit(10)* //=> SELECT * FROM items LIMIT 10* DB.from("items").limit(10, 20)* //=> SELECT * FROM items LIMIT 10 OFFSET 20* DB.from("items").limit([3, 7]).sql* //=> SELECT * FROM items LIMIT 5 OFFSET 3');* DB.from("items").limit(null, 20)* //=> SELECT * FROM items OFFSET 20** DB.from("items").limit('6', sql['a() - 1']).sql* => 'SELECT * FROM items LIMIT 6 OFFSET a() - 1');** @param {Number|String|Number[]} limit the limit to apply* @param {Number|String|patio.sql.LiteralString} offset the offset to apply** @return {patio.Dataset} a cloned dataset witht the LIMIT and OFFSET applied.**/limit: function (limit, offset) {- 46
if (this.__opts.sql) {- 7
return this.fromSelf().limit(limit, offset);}- 39
if (isArray(limit) && limit.length === 2) {- 1
offset = limit[0];- 1
limit = limit[1] - limit[0] + 1;}- 39
if (isString(limit) || isInstanceOf(limit, LiteralString)) {- 2
limit = parseInt("" + limit, 10);}- 39
if (isNumber(limit) && limit < 1) {- 2
throw new QueryError("Limit must be >= 1");}- 37
var opts = {limit: limit};- 37
if (offset) {- 9
if (isString(offset) || isInstanceOf(offset, LiteralString)) {- 1
offset = parseInt("" + offset, 10);- 1
isNaN(offset) && (offset = 0);}- 9
if (isNumber(offset) && offset < 0) {- 1
throw new QueryError("Offset must be >= 0");}- 8
opts.offset = offset;}- 36
return this.mergeOptions(opts);},/**** Returns a cloned dataset with a not equal expression added to the WHERE* clause.** @example* DB.from("test").neq({x : 1});* //=> SELECT * FROM test WHERE (x != 1)* DB.from("test").neq({x : 1, y : 10});* //=> SELECT * FROM test WHERE ((x != 1) AND (y != 10))** @param {Object} obj object used to create the not equal expression** @return {patio.Dataset} a cloned dataset with the not equal expression added to the WHERE clause.*/neq: function (obj) {- 2
return this.filter(this.__createBoolExpression("neq", obj));},/**** Returns a cloned dataset with an equal expression added to the WHERE* clause.** @example* DB.from("test").eq({x : 1});* //=> SELECT * FROM test WHERE (x = 1)* DB.from("test").eq({x : 1, y : 10});* //=> SELECT * FROM test WHERE ((x = 1) AND (y = 10))** @param {Object} obj object used to create the equal expression** @return {patio.Dataset} a cloned dataset with the equal expression added to the WHERE clause.*/eq: function (obj) {- 2
return this.filter(this.__createBoolExpression("eq", obj));},/**** Returns a cloned dataset with a greater than expression added to the WHERE* clause.** @example* DB.from("test").gt({x : 1});* //=> SELECT * FROM test WHERE (x > 1)* DB.from("test").gt({x : 1, y : 10});* //=> SELECT * FROM test WHERE ((x > 1) AND (y > 10))** @param {Object} obj object used to create the greater than expression.** @return {patio.Dataset} a cloned dataset with the greater than expression added to the WHERE clause.*/gt: function (obj) {- 2
return this.filter(this.__createBoolExpression("gt", obj));},/**** Returns a cloned dataset with a less than expression added to the WHERE* clause.** @example* DB.from("test").lt({x : 1});* //=> SELECT * FROM test WHERE (x < 1)* DB.from("test").lt({x : 1, y : 10});* //=> SELECT * FROM test WHERE ((x < 1) AND (y < 10))** @param {Object} obj object used to create the less than expression.** @return {patio.Dataset} a cloned dataset with the less than expression added to the WHERE clause.*/lt: function (obj) {- 2
return this.filter(this.__createBoolExpression("lt", obj));},/*** Returnes a cloned dataset with the IS NOT expression added to the WHERE* clause.** @example** DB.from("test").isNot({boolFlag : null});* => SELECT * FROM test WHERE (boolFlag IS NOT NULL);* DB.from("test").isNot({boolFlag : false, otherFlag : true, name : null});* => SELECT * FROM test WHERE ((boolFlag IS NOT FALSE) AND (otherFlag IS NOT TRUE) AND (name IS NOT NULL));** @param {Object} obj object used to create the IS NOT expression for.** @return {patio.Dataset} a cloned dataset with the IS NOT expression added to the WHERE clause.**/isNot: function (obj) {- 4
return this.filter(this.__createBoolExpression("isNot", obj));},/*** Returnes a cloned dataset with the IS expression added to the WHERE* clause.** @example** DB.from("test").is({boolFlag : null});* => SELECT * FROM test WHERE (boolFlag IS NULL);* DB.from("test").is({boolFlag : false, otherFlag : true, name : null});* => SELECT * FROM test WHERE ((boolFlag IS FALSE) AND (otherFlag IS TRUE) AND (name IS NULL));** @param {Object} obj object used to create the IS expression for.** @return {patio.Dataset} a cloned dataset with the IS expression added to the WHERE clause.**/is: function (obj) {- 4
return this.filter(this.__createBoolExpression("is", obj));},/*** Returnes a cloned dataset with the IS NOT NULL boolean expression added to the WHERE* clause.** @example** DB.from("test").isNotNull("boolFlag");* => SELECT * FROM test WHERE (boolFlag IS NOT NULL);* DB.from("test").isNotNull("boolFlag", "otherFlag");* => SELECT * FROM test WHERE (boolFlag IS NOT NULL AND otherFlag IS NOT NULL);** @param {String...} arr variable number of arguments to create an IS NOT NULL expression for.** @return {patio.Dataset} a cloned dataset with the IS NOT NULL expression added to the WHERE clause.**/isNotNull: function (arr) {- 3
arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);- 2
return this.filter(this.__createBoolExpression("isNot", arr));},/*** Returnes a cloned dataset with the IS NULL boolean expression added to the WHERE* clause.** @example** DB.from("test").isNull("boolFlag");* => SELECT * FROM test WHERE (boolFlag IS NULL);* DB.from("test").isNull("boolFlag", "otherFlag");* => SELECT * FROM test WHERE (boolFlag IS NULL AND otherFlag IS NULL);** @param {String...} arr variable number of arguments to create an IS NULL expression for.** @return {patio.Dataset} a cloned dataset with the IS NULL expression added to the WHERE clause.**/isNull: function (arr) {- 3
arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null);- 2
return this.filter(this.__createBoolExpression("is", arr));},/*** Returnes a cloned dataset with the IS NOT TRUE boolean expression added to the WHERE* clause.** @example** DB.from("test").isNotTrue("boolFlag");* => SELECT * FROM test WHERE (boolFlag IS NOT TRUE);* DB.from("test").isNotTrue("boolFlag", "otherFlag");* => SELECT * FROM test WHERE (boolFlag IS NOT TRUE AND otherFlag IS NOT TRUE);** @param {String...} arr variable number of arguments to create an IS NOT TRUE expression for.** @return {patio.Dataset} a cloned dataset with the IS NOT TRUE expression added to the WHERE clause.**/isNotTrue: function (arr) {- 3
arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);- 2
return this.filter(this.__createBoolExpression("isNot", arr));},/*** Returnes a cloned dataset with the IS TRUE boolean expression added to the WHERE* clause.** @example** DB.from("test").isTrue("boolFlag");* => SELECT * FROM test WHERE (boolFlag IS TRUE);* DB.from("test").isTrue("boolFlag", "otherFlag");* => SELECT * FROM test WHERE (boolFlag IS TRUE AND otherFlag IS TRUE);** @param {String...} arr variable number of arguments to create an IS TRUE expression for.** @return {patio.Dataset} a cloned dataset with the IS TRUE expression added to the WHERE clause.**/isTrue: function (arr) {- 3
arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true);- 2
return this.filter(this.__createBoolExpression("is", arr));},/*** Returnes a cloned dataset with the IS NOT FALSE boolean expression added to the WHERE* clause.** @example** DB.from("test").isNotFalse("boolFlag");* => SELECT * FROM test WHERE (boolFlag IS NOT FALSE);* DB.from("test").isNotFalse("boolFlag", "otherFlag");* => SELECT * FROM test WHERE (boolFlag IS NOT FALSE AND otherFlag IS NOT FALSE);* @param {String...} arr variable number of arguments to create an IS NOT FALSE expression for.** @return {patio.Dataset} a cloned dataset with the IS NOT FALSE expression added to the WHERE clause.**/isNotFalse: function (arr) {- 3
arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);- 2
return this.filter(this.__createBoolExpression("isNot", arr));},/*** Returnes a cloned dataset with the IS FALSE boolean expression added to the WHERE* clause.** @example** DB.from("test").isFalse("boolFlag");* => SELECT * FROM test WHERE (boolFlag IS FALSE);* DB.from("test").isFalse("boolFlag", "otherFlag");* => SELECT * FROM test WHERE (boolFlag IS FALSE AND otherFlag IS FALSE);* @param {String...} arr variable number of arguments to create an IS FALSE expression for.** @return {patio.Dataset} a cloned dataset with the IS FALSE expression added to the WHERE clause.**/isFalse: function (arr) {- 3
arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false);- 2
return this.filter(this.__createBoolExpression("is", arr));},/**** Returns a cloned dataset with a greater than or equal to expression added to the WHERE* clause.** @example* DB.from("test").gte({x : 1});* //=> SELECT * FROM test WHERE (x >= 1)* DB.from("test").gte({x : 1, y : 10});* //=> SELECT * FROM test WHERE ((x >= 1) AND (y >= 10))** @param {Object} obj object used to create the greater than or equal to expression.** @return {patio.Dataset} a cloned dataset with the greater than or equal to expression added to the WHERE clause.*/gte: function (arr) {- 2
arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "gte");- 2
return this.filter(this.__createBoolExpression("gte", arr));},/**** Returns a cloned dataset with a less than or equal to expression added to the WHERE* clause.** @example* DB.from("test").gte({x : 1});* //=> SELECT * FROM test WHERE (x <= 1)* DB.from("test").gte({x : 1, y : 10});* //=> SELECT * FROM test WHERE ((x <= 1) AND (y <= 10))** @param {Object} obj object used to create the less than or equal to expression.** @return {patio.Dataset} a cloned dataset with the less than or equal to expression added to the WHERE clause.*/lte: function (obj) {- 2
var arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "lte");- 2
return this.filter(this.__createBoolExpression("lte", obj));},/*** Returns a cloned dataset with a between clause added* to the where clause.** @example* ds.notBetween({x:[1, 2]}).sql;* //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))** ds.find({x:{notBetween:[1, 2]}}).sql;* //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))* @param {Object} obj object where the key is the column and the value is an array where the first element* is the item to be greater than or equal to than and the second item is less than or equal to than.** @return {patio.Dataset} a cloned dataset with a between clause added* to the where clause.*/between: function (obj) {- 2
return this.filter(this.__createBetweenExpression(obj));},/*** Returns a cloned dataset with a not between clause added* to the where clause.** @example* ds.notBetween({x:[1, 2]}).sql;* //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))** ds.find({x:{notBetween:[1, 2]}}).sql;* //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))* @param {Object} obj object where the key is the column and the value is an array where the first element* is the item to be less than and the second item is greater than.** @return {patio.Dataset} a cloned dataset with a not between clause added* to the where clause.*/notBetween: function (obj) {- 2
return this.filter(this.__createBetweenExpression(obj, true));},/*** Returns a cloned dataset with the given lock style. If style is a* string, it will be used directly.Currently "update" is respected* by most databases, and "share" is supported by some.** @example* DB.from("items").lockStyle('FOR SHARE') # SELECT * FROM items FOR SHARE** @param {String} style the lock style to use.** @return {patio.Dataset} a cloned datase with the given lock style.**/lockStyle: function (style) {- 4
return this.mergeOptions({lock: style});},/*** Returns a copy of the dataset with the order changed. If the dataset has an* existing order, it is ignored and overwritten with this order. If null is given* the returned dataset has no order. This can accept multiple arguments* of varying kinds, such as SQL functions. This also takes a function similar* to {@link patio.Dataset#filter}** @example** DB.from("items").order("name")* //=> SELECT * FROM items ORDER BY name** DB.from("items").order("a", "b")* //=> SELECT * FROM items ORDER BY a, b** DB.from("items").order(sql.literal('a + b'))* //=> SELECT * FROM items ORDER BY a + b** DB.from("items").order(sql.identifier("a").plus("b"))* //=> SELECT * FROM items ORDER BY (a + b)** DB.from("items").order(sql.identifier("name").desc())* //=> SELECT * FROM items ORDER BY name DESC** DB.from("items").order(sql.identifier("name").asc({nulls : "last"))* //=> SELECT * FROM items ORDER BY name ASC NULLS LAST** DB.from("items").order(function(){* return this.sum("name").desc();* }); //=> SELECT * FROM items ORDER BY sum(name) DESC** DB.from("items").order(null)* //=>SELECT * FROM items* @param arg variable number of arguments similar to {@link patio.Dataset#filter}** @return {patio.Dataset} a cloned dataset with the order changed.* */order: function (args) {- 416
args = argsToArray(arguments);- 416
var order = [];- 416
args = compact(args).length ? args : null;- 416
if (args) {- 299
args.forEach(function (a) {- 362
if (isString(a)) {- 240
order.push(this.stringToIdentifier(a));- 122
} else if (isFunction(a)) {- 16
var res = a.apply(sql, [sql]);- 16
order = order.concat(isArray(res) ? res : [res]);} else {- 106
order.push(a);}}, this);} else {- 117
order = null;}- 416
return this.mergeOptions({order: order});},/*** Alias of {@link patio.Dataset#orderMore};*/orderAppend: function () {- 4
return this.orderMore.apply(this, arguments);},/*** @see patio.Dataset#order*/orderBy: function () {- 6
return this.order.apply(this, arguments);},/*** Returns a copy of the dataset with the order columns added* to the end of the existing order. For more detail* @see patio.Dataset#order** @example** DB.from("items").order("a").order("b");* //=> SELECT * FROM items ORDER BY b** DB.from("items").order("a").orderMore("b");* //=>SELECT * FROM items ORDER BY a, b*/orderMore: function () {- 11
var args = argsToArray(arguments);- 11
if (this.__opts.order) {- 9
args = this.__opts.order.concat(args);}- 11
return this.order.apply(this, args);},/*** Returns a copy of the dataset with the order columns added* to the beginning of the existing order. For more detail* @see patio.Dataset#order** @example* DB.from("items").order("a").order("b");* //=> SELECT * FROM items ORDER BY b** DB.from("items").order("a").orderPrepend("b");* //=>SELECT * FROM items ORDER BY b, a****/orderPrepend: function () {- 4
var ds = this.order.apply(this, arguments);- 4
return this.__opts.order ? ds.orderMore.apply(ds, this.__opts.order) : ds;},/*** Qualify to the given table, or {@link patio.Dataset#firstSourceAlias} if not table is given.** @example* DB.from("items").filter({id : 1}).qualify();* //=> SELECT items.* FROM items WHERE (items.id = 1)** DB.from("items").filter({id : 1}).qualify("i");* //=> SELECT i.* FROM items WHERE (i.id = 1)** @param {String|patio.sql.Identifier} [table={@link patio.Dataset#firstSourceAlias}] the table name to qualify to.** @return {patio.Dataset} a cloned dataset qualified to the table or {@link patio.Dataset#firstSourceAlias}**/qualify: function (table) {- 19
table = table || this.firstSourceAlias;- 19
return this.qualifyTo(table);},/*** Qualify the dataset to its current first source(first from clause). This is useful* if you have unqualified identifiers in the query that all refer to* the first source, and you want to join to another table which* has columns with the same name as columns in the current dataset.* See {@link patio.Dataset#qualifyTo}** @example** DB.from("items").filter({id : 1}).qualifyToFirstSource();* //=> SELECT items.* FROM items WHERE (items.id = 1)** @return {patio.Dataset} a cloned dataset that is qualified with the first source.* */qualifyToFirstSource: function () {- 18
return this.qualifyTo(this.firstSourceAlias);},/*** Return a copy of the dataset with unqualified identifiers in the* SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the* given table. If no columns are currently selected, select all* columns of the given table.** @example* DB.from("items").filter({id : 1}).qualifyTo("i");* //=> SELECT i.* FROM items WHERE (i.id = 1)** @param {String} table the name to qualify identifier to.** @return {patio.Dataset} a cloned dataset with unqualified identifiers qualified.*/qualifyTo: function (table) {- 40
var o = this.__opts;- 40
if (o.sql) {- 2
return this.mergeOptions();}- 38
var h = {};- 38
array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) {- 65
h[k] = this._qualifiedExpression(o[k], table);}, this);- 38
if (!o.select || isEmpty(o.select)) {- 14
h.select = [new ColumnAll(table)];}- 38
return this.mergeOptions(h);},/*** Same as {@link patio.Dataset@qualifyTo} except that it forces qualification on methods called* after it has been called.** @example** //qualfyTo would generate* DB.from("items").qualifyTo("i").filter({id : 1});* //=> SELECT i.* FROM items WHERE (id = 1)** //alwaysQualify qualifies filter also.* DB.from("items").alwaysQualify("i").filter({id : 1});* //=> SELECT i.* FROM items WHERE (i.id = 1)*** @param {String|patio.sql.Identifier} [table=this.firstSourceAlias] the table to qualify to.* @return {patio.Dataset} a cloned dataset that will always qualify.*/alwaysQualify: function (table) {- 3
return this.mergeOptions({alwaysQualify: table || this.firstSourceAlias});},/*** Returns a copy of the dataset with the order reversed. If no order is* given, the existing order is inverted.** @example* DB.from("items").reverse("id");* //=> SELECT * FROM items ORDER BY id DESC** DB.from("items").order("id").reverse();* //=> SELECT * FROM items ORDER BY id DESC** DB.from("items").order("id").reverse(sql.identifier("name").asc);* //=> SELECT * FROM items ORDER BY name ASC** @param {String|patio.sql.Identifier|Function} args variable number of columns add to order before reversing.** @return {patio.Dataset} a cloned dataset with the order reversed.***/reverse: function (args) {- 46
args = argsToArray(arguments);- 46
return this.order.apply(this, this._invertOrder(args.length ? args : this.__opts.order));},/*** @see patio.Dataset#reverse*/reverseOrder: function () {- 16
return this.reverse.apply(this, arguments);},/*** Returns a copy of the dataset with the columns selected changed* to the given columns. This also takes a function similar to {@link patio.Dataset#filter}** @example* DB.from("items").select("a");* //=> SELECT a FROM items** DB.from("items").select("a", "b");* //=> SELECT a, b FROM items** DB.from("items").select("a", function(){* return this.sum("b")* }).sql; //=> SELECT a, sum(b) FROM items** @param {String|patio.sql.Identifier|Function} args variable number of colums to select* @return {patio.Dataset} a cloned dataset with the columns selected changed.*/select: function (args) {- 732
args = flatten(argsToArray(arguments));- 732
var columns = [];- 732
args.forEach(function (c) {- 1292
if (isFunction(c)) {- 23
var res = c.apply(sql, [sql]);- 23
columns = columns.concat(isArray(res) ? res : [res]);} else {- 1269
columns.push(c);}});- 732
var select = [];- 732
columns.forEach(function (c) {- 1295
if (isHash(c)) {- 4
for (var i in c) {- 5
select.push(new AliasedExpression(this.stringToIdentifier(i), c[i]));}- 1291
} else if (isString(c)) {- 421
select.push(this.stringToIdentifier(c));} else {- 870
select.push(c);}}, this);- 732
return this.mergeOptions({select: select});},/*** Returns a cloned dataset that selects *.** @return {patio.Dataset} a cloned dataset that selects *.*/selectAll: function () {- 6
return this.mergeOptions({select: null});},/*** Selects the columns if only if there is not already select sources.** @example** var ds = DB.from("items"); //SELECT * FROM items** ds.select("a"); //SELECT a FROM items;* ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;* ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;** @param cols columns to select if there is not already select sources.* @return {patio.Dataset} a cloned dataset with the appropriate select sources.*/selectIfNoSource: function (cols) {- 0
var ret;- 0
if (!this.hasSelectSource) {- 0
ret = this.select.apply(this, arguments);} else {- 0
ret = this.mergeOptions();}- 0
return ret;},/*** Returns a copy of the dataset with the given columns added* to the existing selected columns. If no columns are currently selected,* it will select the columns given in addition to *.** @example* DB.from("items").select("a").selectAppend("b").sql;* //=> SELECT b FROM items** DB.from("items").select("a").selectAppend("b", "c", "d").sql* //=> SELECT a, b, c, d FROM items** DB.from("items").selectAppend("b").sql* //=> SELECT *, b FROM items** @param [...] cols variable number of columns to add to the select statement** @return {patio.Dataset} returns a cloned dataset with the new select columns appended.*/selectAppend: function (cols) {- 7
cols = argsToArray(arguments);- 7
var currentSelect = this.__opts.select;- 7
if (!currentSelect || !currentSelect.length) {- 3
currentSelect = [this._static.WILDCARD];}- 7
return this.select.apply(this, currentSelect.concat(cols));},/*** Returns a copy of the dataset with the given columns added* to the existing selected columns. If no columns are currently selected* it will just select the columns given.** @example* DB.from("items").select("a").select("b").sql;* //=> SELECT b FROM items** DB.from("items").select("a").selectMore("b", "c", "d").sql* //=> SELECT a, b, c, d FROM items** DB.from("items").selectMore("b").sql* //=> SELECT b FROM items** @param [...] cols variable number of columns to add to the select statement** @return {patio.Dataset} returns a cloned dataset with the new select columns appended.*/selectMore: function (cols) {- 10
cols = argsToArray(arguments);- 10
var currentSelect = this.__opts.select;- 10
return this.select.apply(this, (currentSelect || []).concat(cols));},/*** Set the default values for insert and update statements. The values hash passed* to insert or update are merged into this hash, so any values in the hash passed* to insert or update will override values passed to this method.** @example* DB.from("items").setDefaults({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();* //=> INSERT INTO items (a, c, b) VALUES ('d', 'c', 'b')** @param {Object} hash object with key value pairs to use as override values** @return {patio.Dataset} a cloned dataset with the defaults added to the current datasets defaults.*/setDefaults: function (hash) {- 5
return this.mergeOptions({defaults: merge({}, this.__opts.defaults || {}, hash)});},/*** Set values that override hash arguments given to insert and update statements.* This hash is merged into the hash provided to insert or update, so values* will override any values given in the insert/update hashes.** @example* DB.from("items").setOverrides({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql();* //=> INSERT INTO items (a, c, b) VALUES ('a', 'c', 'b')** @param {Object} hash object with key value pairs to use as override values** @return {patio.Dataset} a cloned dataset with the overrides added to the current datasets overrides.*/setOverrides: function (hash) {- 5
return this.mergeOptions({overrides: merge({}, this.__opts.overrides || {}, hash)});},/*** Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.* @example* DB.from("items").group("a").having({a : 1}).where("b").unfiltered().sql;* //=> SELECT * FROM items GROUP BY a** @return {patio.Dataset} a cloned dataset with no HAVING or WHERE clause.*/unfiltered: function () {- 14
return this.mergeOptions({where: null, having: null});},/*** Returns a copy of the dataset with no GROUP or HAVING clause.** @example* DB.from("t").group("a").having({a : 1}).where("b").ungrouped().sql;* //=> SELECT * FROM t WHERE b** @return {patio.Dataset} a cloned dataset with no GROUP or HAVING clause.*/ungrouped: function () {- 1
return this.mergeOptions({group: null, having: null});},/*** Adds a UNION clause using a second dataset object.* A UNION compound dataset returns all rows in either the current dataset* or the given dataset.* Options:* :alias :: Use the given value as the from_self alias* :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur* :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.** @example* DB.from("items").union(DB.from("otherItems")).sql;* //=> SELECT * FROM items UNION SELECT * FROM other_items** DB.from("items").union(DB.from("otherItems"), {all : true, fromSelf : false}).sql;* //=> SELECT * FROM items UNION ALL SELECT * FROM other_items** DB.from("items").union(DB.from("otherItems"), {alias : "i"})* //=> SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS i** @param {patio.Dataset} dataset dataset to union with* @param {Object} opts addional options* @param {String|patio.sql.Identifier} [opts.alias] Alias to use as the fromSelf alias.* @param {Boolean} [opt.all=false] Set to true to use UNION ALL instead of UNION so duplicate rows can occur* @param {Boolean} [opts.fromSelf=true] Set to false to not wrap the returned dataset in a fromSelf.** @return {patio.Dataset} a cloned dataset with the union.**/union: function (dataset, opts) {- 21
opts = isUndefined(opts) ? {} : opts;- 21
if (!isHash(opts)) {- 3
opts = {all: opts};}- 21
return this.compoundClone("union", dataset, opts);},/*** Returns a copy of the dataset with no limit or offset.** @example* DB.from("t").limit(10, 20).unlimited().sql;* //=> SELECT * FROM t** @return {patio.Dataset} a cloned dataset with no limit or offset.*/unlimited: function () {- 1
return this.mergeOptions({limit: null, offset: null});},/*** Returns a copy of the dataset with no order.** @example* DB.from("t").order("a", sql.identifier("b").desc()).unordered().sql;* //=> SELECT * FROM t** @return {patio.Dataset} a cloned dataset with no order.*/unordered: function () {- 114
return this.order(null);},/*** Add a condition to the WHERE clause. See {@link patio.Dataset#filter} for argument types.** @example* DB.from("test").where('price < ? AND id in ?', 100, [1, 2, 3]).sql;* //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"* DB.from("test").where('price < {price} AND id in {ids}', {price:100, ids:[1, 2, 3]}).sql;* //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))")**/where: function () {- 254
return this._filter.apply(this, ["where"].concat(argsToArray(arguments)));},/*** Add a common table expression (CTE) with the given name and a dataset that defines the CTE.* A common table expression acts as an inline view for the query.** @name with* @example** DB.from("t")["with"]("t", db.from("x"))["with"]("j", db.from("y")).sql;* //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y) SELECT * FROM t'** DB.from("t")["with"]("t", db.from("x")).withRecursive("j", db.from("y"), db.from("j")).sql;* //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t'** DB.from("t")["with"]("t", db.from("x"), {args:["b"]}).sql;* //=> 'WITH t(b) AS (SELECT * FROM x) SELECT * FROM t'** @param {String} name the name of the to assign to the CTE.* @param {patio.Dataset} dataset the dataset to use for the CTE.* @param {Object} opts extra options.* @param {String[]} [opts.args] colums/args for the CTE.* @param {Boolean} [opts.recursive] set to true that the CTE is recursive.** @return {patio.Dataset} a cloned dataset with the CTE.*/"with": function (name, dataset, opts) {- 6
if (!this.supportsCte) {- 1
throw new QueryError("this dataset does not support common table expressions");}- 5
return this.mergeOptions({"with": (this.__opts["with"] || []).concat([merge(opts || {}, {name: this.stringToIdentifier(name), dataset: dataset})])});},/*** Add a recursive common table expression (CTE) with the given name, a dataset that* defines the nonrecursive part of the CTE, and a dataset that defines the recursive part* of the CTE.** @example** //Sing withRecursive call.* DB.from("t").withRecursive("t", db.from("x"), db.from("t")).sql;* //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'** //Multiple withRecursive calls.* DB.from("t").withRecursive("t", db.from("x"), db.from("t"))* .withRecursive("j", db.from("y"), db.from("j")).sql;* //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t),* j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t';** //Adding args* DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {args:["b", "c"]}).sql;* //=> 'WITH t(b, c) AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'** //Setting union all to false* DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {unionAll:false}).sql;* //=> 'WITH t AS (SELECT * FROM x UNION SELECT * FROM t) SELECT * FROM t');** @param {String} name the name to assign to the CTE* @param {patio.Dataset} nonRecursive the non-recursive part of the CTE* @param {patio.Dataset} recursive the recursive part of the CTE* @param {Object} [opts={}] extra options* @param {String[]} [opts.args] columns to include with the CTE* @param {Boolena} [opts.unionAll] set to false to use UNION instead of UNION ALL when combining non recursive* with recursive.** @return {patio.Dataset} a cloned dataset with the CTE.*/withRecursive: function (name, nonRecursive, recursive, opts) {- 7
if (!this.supportsCte) {- 1
throw new QueryError("This dataset does not support common table expressions");}- 6
opts = opts || {};- 6
var wit = (this.__opts["with"] || []).concat([merge(opts, {recursive: true, name: this.stringToIdentifier(name), dataset: nonRecursive.union(recursive, {all: opts.unionAll !== false, fromSelf: false})})]);- 6
return this.mergeOptions({"with": wit});},/*** Returns a copy of the dataset with the static SQL used. This is useful if you want* to keep the same {@link patio.Dataset#rowCb}/{@link patio.Dataset#graph},* but change the SQL used to custom SQL.** @example* DB.from("items").withSql('SELECT * FROM foo')* //=> SELECT * FROM foo** @param {String} sql sql for the dataset to use.** @return {patio.Dataset} a cloned dataset with the static sql set.*/withSql: function (sql) {- 46
var args = argsToArray(arguments).slice(1);- 46
if (args.length) {- 23
sql = new PlaceHolderLiteralString(sql, args);}- 46
return this.mergeOptions({sql: sql});},/*** Add the dataset to the list of compounds** @param {String} type the type of compound (i.e. "union", "intersect")* @param {patio.Dataset} dataset the dataset to add to* @param [Object] [options={}] compound option to use (i.e {all : true})** @return {patio.Dataset} ds with the dataset added to the compounds.*/compoundClone: function (type, dataset, options) {- 51
var ds = this._compoundFromSelf().mergeOptions({compounds: (array.toArray(this.__opts.compounds || [])).concat([[type, dataset._compoundFromSelf(), options.all]])});- 51
return options.fromSelf === false ? ds : ds.fromSelf(options);},/*** Returns the a cloned dataset with out the {@link patio.Dataset#rowCb}** @example* var ds = DB.from("test");* ds.rowCb = function(r){* r.a = r.a * 2;* }** ds.all().chain(function(ret){* //ret === [{a : 4}, {a : 6}]* });* ds.naked().all().chain(function(ret){* //ret === [{a : 2}, {a : 3}];* });** @return {patio.Dataset} a cloned dataset with out the {@link patio.Dataset#rowCb}*/naked: function () {- 2212
var ds = this.mergeOptions({});- 2212
ds.rowCb = null;- 2212
return ds;},/*** @private** Adds a group of conditions joined by the second arg, wrapped in parens, connected to an existing where/having* clause by the first arg.* If the where/having clause doesn't yet exist, a where clause is created with the group.** @example** DB.from("items").filter({id, [1,2,3]})._addGroupedCondition("AND", "OR", [{price: {lt : 0}}, {price: {gt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10)))** DB.from("items")._addGroupedCondition("AND", "OR", [{price: {lt : 0}}, {price: {gt: 10}]).sql;* //=> SELECT* ** FROM* items* WHERE* ((price < 0) OR (price > 10))** @param {String} If there is an existing where/having clause, this arg will join the new condition group to it. "AND" if undef. Should be "AND" or "OR"* @param {String} The conditions will be joined this. Same expectations as the first param.* @param See {@link patio.Dataset#filter}.** @return {patio.Dataset} a cloned dataset with the condition group added to the WHERE/HAVING clause.*/_addGroupedCondition: function (addedByBool, groupedByBool) {- 15
groupedByBool = isUndefined(groupedByBool) ? "AND" : groupedByBool;- 15
var tOpts = this.__opts,clause = (tOpts.having ? "having" : "where"),clauseObj = tOpts[clause];- 15
var args = argsToArray(arguments, 2);- 15
args = args.length === 1 ? args[0] : args;- 15
var opts = {};- 15
if (clauseObj) {- 9
addedByBool = isUndefined(addedByBool) ? "AND" : addedByBool;- 9
opts[clause] = new BooleanExpression(addedByBool, clauseObj, this._filterExpr(args, null, groupedByBool));} else {- 6
opts[clause] = this._filterExpr(args, null, groupedByBool);}- 15
return this.mergeOptions(opts);},/*** @private* Protected** Internal filter method so it works on either the having or where clauses.*/_filter: function (clause) {- 3764
var cond = argsToArray(arguments).slice(1), cb;- 3764
if (cond.length && isFunction(cond[cond.length - 1])) {- 59
cb = cond.pop();}- 3764
cond = cond.length === 1 ? cond[0] : cond;- 3764
if ((cond == null || cond === undefined || cond === "") || (isArray(cond) && cond.length === 0 && !cb) || (isObject(cond) && isEmpty(cond) && !cb)) {- 293
return this.mergeOptions();} else {- 3471
cond = this._filterExpr(cond, cb);- 3468
var cl = this.__opts[clause];- 3468
cl && (cond = new BooleanExpression("AND", cl, cond));- 3468
var opts = {};- 3468
opts[clause] = cond;- 3468
return this.mergeOptions(opts);}},/*** Splits a possible implicit alias, handling both {@link patio.sql.AliasedExpression}s* and strings. Returns an array of two elements, with the first being the* main expression, and the second being the alias. Alias may be null if it is a* string that does not contain an alias {table}___{alias}.*/_splitAlias: function (c) {- 799
var ret;- 799
if (isInstanceOf(c, AliasedExpression)) {- 5
ret = [c.expression, c.alias];- 794
} else if (isString(c)) {- 0
var parts = this._splitString(c), cTable = parts[0], column = parts[1], alias = parts[2];- 0
if (alias) {- 0
ret = [cTable ? new QualifiedIdentifier(cTable, column) : column, alias];} else {- 0
ret = [c, null];}} else {- 794
ret = [c, null];}- 799
return ret;},/*** @private* Inverts the given order by breaking it into a list of column references* and inverting them.** ds.invertOrder([sql.identifier("id").desc()]]) //=> [id]* ds.invertOrder("category", sql.identifier("price").desc()]) #=> [category.desc(), price]*/_invertOrder: function (order) {- 46
var ret = order;- 46
if (order) {- 45
ret = order.map(function (o) {- 57
if (isInstanceOf(o, OrderedExpression)) {- 17
return o.invert();} else {- 40
return new OrderedExpression(isString(o) ? new Identifier(o) : o);}}, this);}- 46
return ret;},/*** Creates a boolean expression that each key is compared to its value using the provided operator.** @example** ds.__createBoolExpression("gt", {x : 1, y:2, z : 5}) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))* ds.__createBoolExpression("gt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5))* ds.__createBoolExpression("lt", {x : 1, y:2, z : 5}) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))* ds.__createBoolExpression("lt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))** @param {String} op valid boolean expression operator to capare each K,V pair with* @param {Object| Array } obj object or two dimensional array containing key value pairs** @return {patio.sql.BooleanExpression} boolean expression joined by a AND of each key value pair compared by the op*/__createBoolExpression: function (op, obj) {- 32
var pairs = [];- 32
if (Expression.isConditionSpecifier(obj)) {- 32
if (isHash(obj)) {- 18
obj = array.toArray(obj);}- 32
obj.forEach(function (pair) {- 42
pairs.push(new BooleanExpression(op, new Identifier(pair[0]), pair[1]));});}- 32
return pairs.length === 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));},/*** @private** Creates a boolean expression where the key is '>=' value 1 and '<=' value two.** @example** ds.__createBetweenExpression({x : [1,2]}) => //=> WHERE ((x >= 1) AND (x <= 10))* ds.__createBetweenExpression({x : [1,2]}, true) => //=> WHERE ((x < 1) OR (x > 10))** @param {Object} obj object where the keys are columns and the values are two element arrays.* @param {Boolean} [invert] if set to true it inverts the between to make it not between the two values** @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.*/__createBetweenExpression: function (obj, invert) {- 4
var pairs = [];- 4
for (var i in obj) {- 4
var v = obj[i];- 4
if (isArray(v) && v.length) {- 2
var ident = this.stringToIdentifier(i);- 2
pairs.push(new BooleanExpression("AND", new BooleanExpression("gte", ident, v[0]), new BooleanExpression("lte", ident, v[1])));} else {- 2
throw new QueryError("Between requires an array for the value");}}- 2
var ret = pairs.length === 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs));- 2
return invert ? BooleanExpression.invert(ret) : ret;},/*** @private* Converts an array to a two dimensional array where the first element* is the identifier and the second argument is the value that the value should equal* used by is{Null|NotNull|True|NotTrue|False|notFalse} functions to join all the values passed in.* @param {String[]|patio.sql.Identifier} arr array of elements to make a condition specifier out of* @param defaultOp the value to assign a value if one is not provided.** @return { [[]] } an array of two element arrays.*/__arrayToConditionSpecifier: function (arr, defaultOp) {- 22
var ret = [];- 22
arr.forEach(function (a) {- 28
if (isString(a)) {- 18
a = this.stringToIdentifier(a);}- 28
if (isInstanceOf(a, Identifier)) {- 18
ret.push([a, defaultOp]);- 10
} else if (isHash(a)) {- 4
ret = ret.concat(array.toArray(a));} else {- 6
throw new QueryError("Invalid condition specifier " + a);}}, this);- 16
return ret;},/*** @private** SQL expression object based on the expr type. See {@link patio.Dataset#filter}*/_filterExpr: function (expr, cb, joinCond) {- 4542
expr = (isUndefined(expr) || isNull(expr) || (isArray(expr) && !expr.length)) ? null : expr;- 4542
if (expr && cb) {- 1
return new BooleanExpression(joinCond || "AND", this._filterExpr(expr, null, joinCond), this._filterExpr(cb, null, joinCond));- 4541
} else if (cb) {- 58
expr = cb;}- 4541
if (isInstanceOf(expr, Expression)) {- 429
if (isInstanceOf(expr, NumericExpression, StringExpression)) {- 2
throw new QueryError("Invalid SQL Expression type : " + expr);}- 427
return expr;- 4112
} else if (isArray(expr)) {- 934
if (expr.length) {- 934
var first = expr[0];- 934
if (isString(first)) {- 25
return new PlaceHolderLiteralString(first, expr.slice(1), true);- 909
} else if (Expression.isConditionSpecifier(expr)) {- 890
return BooleanExpression.fromValuePairs(expr, joinCond);} else {- 19
return BooleanExpression.fromArgs([joinCond || "AND"].concat(expr.map(function (e) {- 39
return this._filterExpr(e, null, "AND");}, this)));}}- 3178
} else if (isFunction(expr)) {- 59
return this._filterExpr(expr.call(sql, sql), null, joinCond);- 3119
} else if (isBoolean(expr)) {- 7
return new BooleanExpression("NOOP", expr);- 3112
} else if (isString(expr)) {- 1
return this.stringToIdentifier(expr);- 3111
} else if (isInstanceOf(expr, LiteralString)) {- 16
return new LiteralString("(" + expr + ")");- 3095
} else if (isHash(expr)) {- 3094
return BooleanExpression.fromValuePairs(expr, joinCond);} else {- 1
throw new QueryError("Invalid filter argument");}},/**@ignore*/getters: {/*** @ignore* Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.*/isSimpleSelectAll: function () {- 33
var o = {}, opts = this.__opts, count = 0;- 33
for (var i in opts) {- 70
if (opts[i] != null && this._static.NON_SQL_OPTIONS.indexOf(i) === -1) {- 37
o[i] = opts[i];- 37
count++;}}- 33
var f = o.from;- 33
return count === 1 && f.length === 1 && (isString(f[0]) || isInstanceOf(f[0], AliasedExpression, Identifier));}}},static: {/**@lends patio.Dataset*//*** These strings have {name}Join methods created (e.g. {@link patio.Dataset#innerJoin}) that* call {@link patio.Dataset#joinTable} with the string, passing along the arguments and* block from the method call.**/CONDITIONED_JOIN_TYPES: ["inner", "fullOuter", "rightOuter", "leftOuter", "full", "right", "left"],/**** These strings have {name}Join methods created (e.g. naturalJoin) that* call {@link patio.Dataset#joinTable}. They only accept a single table* argument which is passed to {@link patio.Dataset#joinTable}, and they throw an error* if called with a block.**/UNCONDITIONED_JOIN_TYPES: ["natural", "naturalLeft", "naturalRight", "naturalFull", "cross"],/*** The dataset options that require the removal of cached columns* if changed.*/COLUMN_CHANGE_OPTS: ["select", "sql", "from", "join"],/*** Which options don't affect the SQL generation. Used by {@link patio.Dataset#simpleSelectAll}* to determine if this is a simple SELECT * FROM table.*/NON_SQL_OPTIONS: ["server", "defaults", "overrides", "graph", "eagerGraph", "graphAliases"],/*** All methods that return modified datasets with a joined table added.*/JOIN_METHODS: ["join", "joinTable"],/*** Methods that return modified datasets*/QUERY_METHODS: ['addGraphAliases', "and", "distinct", "except", "exclude", "filter", "find", "is", "isNot","eq", "neq", "lt", "lte", "gt", "gte", "forUpdate", "from", "fromSelf", "graph", "grep", "group","groupAndCount", "groupBy", "having", "intersect", "invert", "limit", "lockStyle", "naked", "or", "order","orderAppend", "orderBy", "orderMore", "orderPrepend", "qualify", "reverse","reverseOrder", "select", "selectAll", "selectAppend", "selectMore", "setDefaults","setGraphAliases", "setOverrides", "unfiltered", "ungraphed", "ungrouped", "union", "unlimited","unordered", "where", "with", "withRecursive", "withSql"],init: function () {- 36
this._super(arguments);//initialize our join methods array- 36
var joinMethods = this.JOIN_METHODS;- 36
var queryMethods = this.QUERY_METHODS;- 36
this.UNCONDITIONED_JOIN_TYPES.forEach(function (joinType) {- 180
var m = joinType + "Join";- 180
joinMethods.push(m);- 180
queryMethods.push(m);});- 36
this.CONDITIONED_JOIN_TYPES.forEach(function (joinType) {- 252
var m = joinType + "Join";- 252
joinMethods.push(m);- 252
queryMethods.push(m);});- 36
this.QUERY_METHODS = queryMethods.concat(joinMethods);}}}).as(module);
|
plugins/inheritance.js
|
Coverage98.89
SLOC295
LOC90
Missed1
|
- 1
var comb = require("comb"),asyncArray = comb.async,Promise = comb.Promise,PromiseList = comb.PromiseList;- 1
comb.define(null, {instance: {},"static": {configure: function (model) {}}}).as(exports, "SingleTableInheritance");/*** @class This plugin enables* <a href="http://www.martinfowler.com/eaaCatalog/classTableInheritance.html" target="patioapi">* class table inheritance* </a>.**<div>* Consider the following table model.* @code* employee* - id* - name (varchar)* - kind (varchar)* / \* staff manager* - id (fk employee) - id (fk employee)* - manager_id (fk manger) - numStaff (number)* |* executive* - id (fk manager)** <ul>* <li><b>employee</b>: This is the parent table of all employee instances.</li>* <li><b>staff</b>: Table that inherits from employee where and represents.</li>* <li><b>manager</b>: Another subclass of employee.</li>* <li><b>executive</b>: Subclass of manager that also inherits from employee through inhertiance</li>* </ul>* <p>* When setting up you tables the parent table should contain a String column that contains the "kind" of class it is.* (i.e. employee, staff, manager, executive). This allows the plugin to return the proper instance type when querying* the tables.* </p>* <p>* All other tables that inherit from employee should contain a foreign key to their direct super class that is the* same name as the primary key of the parent table(<b>employee</b>). So, in the* above example <b>staff</b> and <b>manager</b> both contain foreign keys to employee and <b>executive</b> contains* a foreign key to <b>manager</b> and they are all named <b>id</b>.* </p>*</div>** To set up you models the base super class should contain the ClassTableInheritancePlugin** {@code* var Employee = (exports.Employee = patio.addModel("employee", {* plugins : [patio.plugins.ClassTableInheritancePlugin],* static:{* init:function () {* this._super(arguments);* this.configure({key : "kind"});* }* }* }));* }* All sub classes should just inherit their super class* {@code* var Staff = (exports.Staff = patio.addModel("staff", Employee, {* static:{* init:function () {* this._super(arguments);* this.manyToOne("manager", {key : "managerId", fetchType : this.fetchType.EAGER});* }* }* }));* var Manager = (exports.Manager = patio.addModel("manager", Employee, {* static:{* init:function () {* this._super(arguments);* this.oneToMany("staff", {key : "managerId", fetchType : this.fetchType.EAGER});* }* }* }));* }** Executive inherits from manager, and through inheritance will also receive the oneToMany relationship with staff* {@code* var Executive = (exports.Executive = patio.addModel("executive", Manager));* }** Working with models** {@code* comb.when(* new Employee({name:"Bob"}).save(),* new Staff({name:"Greg"}).save(),* new Manager({name:"Jane"}).save(),* new Executive({name:"Sue"}).save()* ).chain(function(){* return Employee.all().chain(function(emps){* var bob = emps[0], greg = emps[1], jane = emps[2], sue = emps[3];* console.log(bob instanceof Employee); //true* console.log(greg instanceof Employee); //true* console.log(greg instanceof Staff); //true* console.log(jane instanceof Employee); //true* console.log(jane instanceof Manager); //true* console.log(sue instanceof Employee); //true* console.log(sue instanceof Manager); //true* console.log(sue instanceof Executive); //true* });* });* }** @name ClassTableInheritancePlugin* @memberof patio.plugins*/- 1
comb.define(null, {instance: {// Delete the row from all backing tables, starting from the// most recent table and going through all superclasses._remove: function () {- 6
var q = this._getPrimaryKeyQuery();- 6
return new PromiseList(this._static.__ctiTables.slice().reverse().map(function (table) {- 12
return this.db.from(table).filter(q).remove();}, this), true).promise();},// Save each column according to the columns in each table_save: function () {- 6
var Self = this._static, ret;- 6
if (Self === Self.__ctiBaseModel) {- 1
ret = this._super(arguments);} else {- 5
var pk = this.primaryKey[0], tables = Self.__ctiTables, ctiColumns = Self.__ctiColumns, self = this,isRestricted = Self.isRestrictedPrimaryKey, db = this.db;- 5
ret = asyncArray.forEach(tables,function (table, index) {- 11
var cols = ctiColumns[table], insert = {}, val, i = -1, colLength = cols.length, c;- 11
while (++i < colLength) {- 27
c = cols[i];- 27
if ((index !== 0 || (index === 0 && (!isRestricted || pk.indexOf(c) === -1))) && !comb.isUndefined(val = self[c])) {- 18
insert[c] = val;}}- 11
return db.from(table).insert(insert).chain(function (id) {- 11
if (comb.isUndefined(self.primaryKeyValue) && !comb.isUndefined(id) && index === 0) {- 5
self.__ignore = true;//how to handle composite keys.- 5
self[pk] = id;- 5
self.__ignore = false;}});}, 1).chain(function () {- 5
self.__isNew = false;- 5
self.__isChanged = false;- 5
return self._saveReload();});}- 6
return ret.promise();},// update each column according to the columns in each table_update: function () {- 4
var q = this._getPrimaryKeyQuery(), changed = this.__changed;- 4
var modelStatic = this._static,ctiColumns = modelStatic.__ctiColumns,tables = modelStatic.__ctiTables,self = this;- 4
return new PromiseList(tables.map(function (table) {- 9
var cols = ctiColumns[table], update = {};- 9
cols.forEach(function (c) {- 22
if (!comb.isUndefined(changed[c])) {- 7
update[c] = changed[c];}});- 9
return comb.isEmpty(update) ? new Promise().callback() : self.db.from(table).filter(q).update(update);}), true).chain(function () {- 4
return self._updateReload();}).promise();}},static: {/**@lends patio.plugins.ClassTableInheritancePlugin*//*** Configures the plugins with the provided options. <b>Note:</b> This should only be called in the* initial parent class.** @param {Object} [opts] Additional options to configure behavior of the plugin* @param {String} [opts.key="key"] the column in the base table that contains the name of the subclass.* @param {Funciton} [opts.keyCb] A callback to invoke on on the key returned from the database. This is useful* if you are working with other orms that save the keys differently.*/configure: function (opts) {- 4
this.__configureOpts = opts;- 4
return this;},sync: function (cb) {- 31
var ret, opts = this.__configureOpts;- 31
if (this.__configureOpts && !this.__configured) {- 1
var self = this;- 1
return this._super().chain(function () {- 1
if (!self.__configured) {- 1
var baseModel = self.__ctiBaseModel = self;- 1
var key = self.__ctiKey = opts.key || "key";- 1
var keyCallback = opts.keyCb || function (k) {- 26
return k;};- 1
self.__ctiModels = [self];- 1
self.__ctiTables = [self.tableName];- 1
var cols = self.__ctiColumns = {};- 1
cols[self.tableName] = self.columns;- 1
self.dataset.rowCb = function (r) {- 26
if (key) {- 26
var model = self.patio.getModel(keyCallback(r[key]));- 26
if (model !== baseModel) {- 23
var q = {};- 23
model.primaryKey.forEach(function (k) {- 23
q[k] = r[k];});- 23
return model.dataset.naked().filter(q).one().chain(function (vals) {- 23
return model.load(vals);});} else {- 3
return self.load(r);}} else {- 0
return self.load(r);}};- 1
self.pre("save", function (next) {- 1
if (key) {- 1
this[key] = this.tableName.toString();}- 1
next();});- 1
self.__configured = true;}- 1
return self;}).classic(cb);} else {- 30
ret = this._super(arguments);}- 30
return ret.promise();},/*** <b>Not typically called by user code</b>. Sets up subclass inheritance.** @param {patio.Model} model model that this class is inheriting from.*/inherits: function (model) {- 3
this._super(arguments);- 3
var ctiKey = this.__ctiKey = model.__ctiKey;- 3
this.__ctiTables = model.__ctiTables.slice();- 3
this.__ctiModels = model.__ctiModels.slice();- 3
this.__ctiModels.push(this);- 3
this.__ctiTables.push(this.tableName);- 3
this.__ctiColumns = comb.merge({}, model.__ctiColumns);- 3
this.__ctiColumns[this.tableName] = this.columns;- 3
this.__ctiBaseModel = model.__ctiBaseModel;//copy over our schema- 3
var newSchema = comb.merge({}, this.__schema);- 3
var schemas = model.__ctiModels.map(function (m) {- 4
return m.schema;}).reverse();- 3
schemas.forEach(function (s) {- 4
for (var i in s) {- 13
newSchema[i] = s[i];}}, this);- 3
this._setSchema(newSchema);- 3
this._setDataset(model.dataset.join(this.tableName, this.primaryKey));- 3
this._setPrimaryKey(this.__ctiBaseModel.primaryKey);- 3
this.pre("save", function (next) {- 5
if (ctiKey) {- 5
this[ctiKey] = this.tableName.toString();}- 5
next();});}}}).as(exports, "ClassTableInheritance");
|
dataset/graph.js
|
Coverage99.20
SLOC402
LOC125
Missed1
|
- 1
var comb = require("comb"),Promise = comb.Promise,string = comb.string,format = string.format,SQL = require("../sql"),sql = SQL.sql,isUndefinedOrNull = comb.isUndefinedOrNull,define = comb.define,merge = comb.merge,argsToArray = comb.argsToArray,isString = comb.isString,isHash = comb.isHash,isArray = comb.isArray,isObject = comb.isObject,isFunction = comb.isFunction,isBoolean = comb.isBoolean,isEmpty = comb.isEmpty,isInstanceOf = comb.isInstanceOf,toArray = comb.array.toArray,errors = require("../errors"),QueryError = errors.QueryError;/** This file contains dataset graphing related features. All methods return* a copy of the dataset.*///leave for later initialization- 1
var Dataset;//checks for a table alies if there isnt one throw an error.- 1
var raiseAliasError = function (options) {- 2
var isAlias = isUndefinedOrNull(options.tableAlias);- 2
throw new QueryError(format("this %s has already been used, please specify %s", isAlias ? "alias" : "table", isAlias ? "a different alias" : "an alias in options"));};- 1
define({/**@ignore*/instance: {/*** @lends patio.Dataset.prototype*//*** @ignore*/constructor: function () {- 27052
!Dataset && (Dataset = require("../index").Dataset);- 27052
this._super(arguments);},/*** Adds the given graph aliases to the list of graph aliases to use,* unlike {@link patio.Dataset#setGraphAliases}, which replaces the list (the equivalent* of {@link patio.Dataset#selectMore} when graphing). See {@link patio.Dataset#setGraphAliases}.** @example* var DB = patio.defaultDatabase;* // SELECT ..., table.column AS someAlias* DB.from("table").addGraphAliases({someAlias : ["table", "column"]);* //returns from graphing* // => {table : {column : someAlias_value, ...}, ...}** @param {Object} graphAliases the graph aliases to use.* Where key is the alias name and the value is an array where arr[0] = 'tableName' arr[1] = "colName'.** @return {patio.Dataset} deep copy of the original dataset with the added graphAliases.*/addGraphAliases: function (graphAliases) {- 3
var ds = this.selectMore.apply(this, this.__graphAliasColumns(graphAliases));- 3
ds.__opts.graphAliases = merge((ds.__opts.graphAliases || (ds.__opts.graph ? ds.__opts.graph.columnAliases : {}) || {}), graphAliases);- 3
return ds;},/*** Allows you to join multiple datasets/tables and have the result set* split into component tables.** This differs from the usual usage of join, which returns the result set* as a single hash.** <pre class="code">** //CREATE TABLE artists (id INTEGER, name TEXT);* //CREATE TABLE albums (id INTEGER, name TEXT, artist_id INTEGER);** var DB = patio.defaultDatabase, ds = db.from("artists");* ds.leftOuterJoin("albums", {artistId : "id"}).first* //=> {id : albums.id, name : albums.name, artist_id : albums.artist_id}** var p = comb.executeInOrder(ds, function(ds){* var graphedDs = ds.graph("albums", {artist_id : "id"});* return graphedDs.first();* });* p.chain(function(obj){* //obj == {artists : {* id : artists.id,* name : artists.name* },* albums : {* id : albums.id,* name : albums.name,* artist_id=>albums.artist_id* }* }* });* </pre>** Using a join such as leftOuterJoin, the attribute names that are shared between* the tables are combined in the single return hash. You can get around that by* using {@link patio.Dataset#select} with correct aliases for all of the columns, but it is simpler to* use {@link patio.Dataset#graph} and have the result set split for you. In addition, {@link patio.Dataset#graph} respects* any {@link patio.Dataset#rowCb} of the current dataset and the datasets you use with {@link patio.Dataset#graph}.** If you are graphing a table and all columns for that table are null, this* indicates that no matching rows existed in the table, so graph will return null.* instead of a hash with all nil values:** <pre class="code">* // Psuedo code there will be promises returned* /// If the artist doesn't have any albums** var DB = patio.defaultDatabase, ds = db.from("artists");* var obj = ds.graph(:albums, :artist_id=>:id).first()* //obj == { artists : {id : artists.id, name : artists.name}, albums : null};* </pre>*** @parameter {String|patio.Dataset|Object} dataset This can be a string (representing a table), another {@link patio.Dataset},* or an object that has a dataset property that returns a string or dataset.* @parameter joinConditions Any condition(s) allowed by {@link patio.Dataset#joinTable}.** @parameter {Object} [options] options to use when creating the graph** @parameter {String|sql.LiteralString|sql.Identifier} [options.fromSelfAlias] The alias to use when the* receiver is not a graphed dataset but it contains multiple FROM tables or a JOIN.* In this case, the receiver is wrapped in a {@link patio.Dataset#fromSelf} before graphing,* and this option determines the alias to use.* @parameter {String|sql.LiteralString|sql.Identifier} [options.implicitQualifier] The qualifier of implicit conditions,* see {@link patio.Dataset#joinTable}.* @parameter [String] [options.joinType="leftOuter"] The type of join to use (passed to {@link patio.Dataset#joinTable}.).* @parameter [String[]|sql.LiteralString[]|sql.Identifier[]|Boolean] [options.select] An array of columns to select. When not used, selects* all columns in the given dataset. When set to false, selects no* columns and is like simply joining the tables, though graph keeps* some metadata about the join that makes it important to use {@link patio.Dataset#graph} instead* of {@link patio.Dataset#joinTable}* @parameter {String|sql.LiteralString|sql.Identifier} [options.tableAlias] The alias to use for the table. If not specified, doesn't* alias the table. You will get an error if the the alias (or table) name is* used more than once.* @parameter {Function} block A function that is passed to {@link patio.Dataset#joinTable}.**/graph: function (dataset, joinConditions, options, block) {- 52
var ret = new Promise();- 52
var args = argsToArray(arguments, 1);- 52
block = isFunction(args[args.length - 1]) ? args.pop() : null;- 52
joinConditions = args.shift() || null;- 52
options = args.shift() || {};// Allow the use of a model, dataset, or string as the first argument// Find the table name/dataset based on the argument- 52
dataset.hasOwnProperty("dataset") && (dataset = dataset.dataset);- 52
var tableAlias = options.tableAlias, table;- 52
if (isString(dataset)) {- 18
table = sql.identifier(dataset);- 18
dataset = this.db.from(dataset);- 18
isUndefinedOrNull(tableAlias) && (tableAlias = table);- 34
} else if (isInstanceOf(dataset, Dataset)) {- 33
if (dataset.isSimpleSelectAll) {- 29
table = dataset.__opts.from[0];- 29
isUndefinedOrNull(tableAlias) && (tableAlias = table);} else {- 4
table = dataset;- 4
isUndefinedOrNull(tableAlias) && (tableAlias = this._datasetAlias((this.__opts.numDatasetSources || 0) + 1));}} else {- 1
throw new QueryError("The dataset arg should be a string, dataset or model");}- 51
var aliases;// Only allow table aliases that haven't been used- 51
var thisOpts = this.__opts, thisOptsGraph = thisOpts.graph;- 51
if (isObject(thisOptsGraph) && isHash((aliases = thisOptsGraph.tableAliases)) && !isUndefinedOrNull(aliases[tableAlias.value])) {- 1
raiseAliasError(options);}// Use a from_self if this is already a joined table- 50
var ds = (!thisOptsGraph && (thisOpts.from.length > 1 || thisOpts.join)) ? this.fromSelf({alias: options.fromSelfAlias || this.firstSourceAlias}) : this;// Join the table early in order to avoid cloning the dataset twice- 50
ds = ds.joinTable(options.joinType || "leftOuter", table, joinConditions, {tableAlias: tableAlias, implicitQualifier: options.implicitQualifier}, block);- 50
var opts = ds.__opts;// Whether to include the table in the result set- 50
var addTable = isBoolean(options.select) ? options.select : true;// Whether to add the columns to the list of column aliases- 50
var addColumns = isUndefinedOrNull(opts.graphAliases);// Setup the initial graph data structure if it doesn't exist- 50
var graph;- 50
var populateGraphPromise;- 50
if (isUndefinedOrNull((graph = opts.graph))) {- 41
var master = this._toTableName(ds.firstSourceAlias);- 41
("" + master === "" + tableAlias) && raiseAliasError(options);// Master hash storing all .graph related information- 40
graph = opts.graph = {};// Associates column aliases back to tables and columns- 40
var columnAliases = graph.columnAliases = {};// Associates table alias (the master is never aliased)- 40
var tableAliases = graph.tableAliases = {};- 40
tableAliases[master] = this;// All columns in the master table are never// aliased, but are not included if set_graph_aliases// has been used.- 40
if (addColumns) {- 39
var select = opts.select = [];- 39
populateGraphPromise = this.columns.chain(function (cols) {- 39
cols.forEach(function (column) {- 113
columnAliases[column] = [master, column];- 113
select.push(new sql.QualifiedIdentifier(master, column));});- 39
return graph;});} else {- 1
populateGraphPromise = new Promise().callback(graph);}} else {- 9
populateGraphPromise = new Promise().callback(graph);}- 49
return populateGraphPromise.chain(function (graph) {- 49
var ret;// Add the table alias to the list of aliases// Even if it isn't been used in the result set,// we add a key for it with a nil value so we can check if it// is used more than once- 49
var tableAliases = graph.tableAliases;- 49
tableAliases[tableAlias] = addTable ? dataset : null;// Add the columns to the selection unless we are ignoring them- 49
if (addTable && addColumns) {- 46
var select = opts.select;- 46
var columnAliases = graph.columnAliases;// Which columns to add to the result set- 46
var dsColPromise;- 46
if (options.select) {- 1
dsColPromise = new Promise().callback(options.select);} else {- 45
dsColPromise = dataset.columns;}// If the column hasn't been used yet, don't alias it.// If it has been used, try tableColumn.- 46
ret = dsColPromise.chain(function (cols) {- 46
cols.forEach(function (column) {- 182
var colAlias, identifier;- 182
if (columnAliases[column]) {- 135
var columnAlias = format("%s_%s", [tableAlias, column]);- 135
colAlias = columnAlias;- 135
identifier = new sql.QualifiedIdentifier(tableAlias, column).as(columnAlias);} else {- 47
colAlias = column;- 47
identifier = new sql.QualifiedIdentifier(tableAlias, column);}- 182
columnAliases[colAlias] = [tableAlias, column];- 182
select.push(identifier);});- 46
return ds;});} else {- 3
ret = ds;}- 49
return ret;});},/*** This allows you to manually specify the graph aliases to use* when using graph. You can use it to only select certain* columns, and have those columns mapped to specific aliases* in the result set. This is the equivalent of {@link patio.Dataset#select} for a* graphed dataset, and must be used instead of {@link patio.Dataset#select} whenever* graphing is used.** @example** var DB = patio.defaultDatabase, ds = DB.from("artists");* var p = comb.executeInOrder(ds, function(ds){* var graphedDs = ds.graph("albums", {artist_id : id});* //SELECT artists.name AS artist_name, albums.name AS album_name, 42 AS forty_two FROM table* return graphedDs.setGraphAliases({artist_name : ["artists", "name"],* album_name : ["albums", "name"],* forty_two : ["albums", "fourtwo", 42]).first();* });** p.chain(function(obj){* //obj == {artists : {name : artists.name}, albums : {name : albums.name, fourtwo : 42}}* });** @parameter {Object} graphAliases Should be a hash with keys being column aliases, and values being* arrays with two or three elements. The first element of the array should be the table alias,* and the second should be the actual column name. If the array* has a third element, it is used as the value returned, instead of* tableAlias.columnName.***/setGraphAliases: function (graphAliases) {- 10
var ds = this.select.apply(this, this.__graphAliasColumns(graphAliases));- 10
ds.__opts.graphAliases = graphAliases;- 10
return ds;},/*** Remove the splitting of results into subhashes, and all metadata* related to the current graph (if any).*/ungraphed: function () {- 241
return this.mergeOptions({graph: null});},//Transform the hash of graph aliases to an array of columns__graphAliasColumns: function (graphAliases) {- 13
var ret = [];- 13
if (isArray(graphAliases)) {- 1
var newGraphAliases = {};- 1
for (var i in graphAliases) {- 2
newGraphAliases[graphAliases[i][0]] = graphAliases[i][1];}- 1
graphAliases = newGraphAliases;}- 13
for (var colAlias in graphAliases) {- 20
var tc = graphAliases[colAlias];- 20
var identifier = tc[2] || new sql.QualifiedIdentifier(tc[0], tc[1]);- 20
if (tc[2] || "" + tc[1] !== "" +colAlias) {- 10
identifier = new sql.AliasedExpression(identifier, colAlias);}- 20
ret.push(identifier);}- 13
return ret;},/*** Fetch the rows, split them into component table parts,* transform and run the {@link patio.Dataset#rowCb} on each part (if applicable),* and yield a hash of the parts.* */graphEach: function (cb) {// Reject tables with nil datasets, as they are excluded from// the result set- 12
var datasets = toArray(this.__opts.graph.tableAliases).filter(function (e) {- 28
return !isUndefinedOrNull(e[1]);});// Get just the list of table aliases into a local variable, for speed- 12
var tableAliases = datasets.map(function (e) {- 27
return e[0];});- 12
datasets = datasets.map(function (e) {- 27
return [e[0], e[1], e[1].rowCb];});// Use the manually set graph aliases, if any, otherwise// use the ones automatically created by .graph- 12
var columnAliases = this.__opts.graphAliases || this.__opts.graph.columnAliases;- 12
var ret = this.fetchRows(this.selectSql).map(function (r) {- 15
var graph = {};// Create the sub hashes, one per table- 15
tableAliases.forEach(function (ta) {- 36
graph[ta] = {};});// Split the result set based on the column aliases// If there are columns in the result set that are// not in column_aliases, they are ignored- 15
for (var colAlias in columnAliases) {- 114
var tc = columnAliases[colAlias];- 114
var ta = tc[0], column = tc[1];- 114
!graph[ta] && (graph[ta] = {});- 114
graph[ta][column] = r[colAlias];}- 15
datasets.forEach(function (d) {- 36
var ta = d[0], ds = d[1], dsCb = d[2];- 36
var g = graph[ta];- 36
if (!isEmpty(g) && Object.keys(g).some(function (x) {- 49
return !isUndefinedOrNull(g[x]);})) {- 30
graph[ta] = dsCb ? dsCb(g) : g;} else {- 6
graph[ta] = null;}});- 15
return graph;});- 12
if (cb) {- 0
ret.forEach(cb);}- 12
return ret;}}}).as(module);
|
adapters/index.js
|
Coverage100.00
SLOC3
LOC3
Missed0
|
- 1
exports.mysql = require("./mysql");- 1
exports.postgres = require("./postgres.js");- 1
exports.redshift = require("./redshift.js");
|
adapters/redshift.js
|
Coverage100.00
SLOC57
LOC17
Missed0
|
- 1
var pg = require("./postgres"),PostgresDatabase = pg.PostgresDatabase,PostgresDataset = pg.PostgresDataset;- 1
var Dataset = PostgresDataset.extend({instance: {//redshift does not support returning_insertReturningSql: function (sql) {- 1
return "";}}});- 1
PostgresDatabase.extend({instance: {//handle serial type__typeLiteralGenericInteger: function (column) {- 4
return column.serial ? "bigint identity(0, 1)" : this.__typeLiteralSpecific(column);},__columnDefinitionSql: function (column) {- 7
var ret = this._super(arguments);- 7
if (column.distKey) {- 1
ret += " distkey";}- 7
if (column.sortKey) {- 2
ret += " sortkey";}- 7
return ret;},//Use MySQL specific syntax for engine type and character encoding__createTableSql: function (name, generator, options) {- 4
options = options || {};- 4
var distStyle = options.distStyle;- 4
distStyle = distStyle ? " diststyle " + distStyle : "";- 4
return [this._super(arguments), distStyle].join("");},getters: {dataset: function () {- 2
return new Dataset(this);}}},"static": {PRIMARY_KEY: " primary key",init: function () {- 1
this.setAdapterType("redshift");}}}).as("RedshiftDatabase");
|
associations/index.js
|
Coverage100.00
SLOC18
LOC2
Missed0
|
- 1
var comb = require("comb"),merge = comb.merge;/*** @ignore* @name patio.associations* @namespace* */- 1
comb(exports).merge({oneToMany:require("./oneToMany"),manyToOne:require("./manyToOne"),oneToOne:require("./oneToOne"),manyToMany:require("./manyToMany"),fetch:{LAZY:"lazy",EAGER:"eager"}});
|
associations/oneToOne.js
|
Coverage100.00
SLOC86
LOC26
Missed0
|
- 1
var comb = require("comb"),Promise = comb.Promise,isUndefinedOrNull = comb.isUndefinedOrNull,isNull = comb.isNull,PromiseList = comb.PromiseList,ManyToOne = require("./manyToOne"),define = comb.define;/*** @class Class to define a manyToOne association.** </br>* <b>NOT to be instantiated directly</b>* Its just documented for reference.** @name ManyToOne* @augments patio.associations.ManyToOne* @memberOf patio.associations***/- 1
define(ManyToOne, {instance:{/**@lends patio.associations.OneToOne.prototype*/type:"oneToOne",__remove:false,//override//@see _Association_fetchMethod:"one",isOwner:true,_preSave:function (next, model) {//handle case where no association was initially set- 46
if (!this.associationLoaded(model) || !this.getAssociation(model)) {- 7
this.__setValue(model, null);}//- 46
next();},//override//@see _Association_postSave:function (next, model) {- 46
var loaded = this.associationLoaded(model), val;- 46
if (loaded && (val = this.getAssociation(model))) {- 39
this._setAssociationKeys(model, val);- 39
val.save().classic(next);} else {- 7
next();}},_preUpdate : function(next){- 18
next();},_postUpdate:function (next, model) {- 18
var removeAssociationFlagName = this.removeAssociationFlagName;- 18
if (model[removeAssociationFlagName]) {- 7
var q = {};- 7
this._setAssociationKeys(model, q, null);- 7
model[this.associatedDatasetName].update(q).classic(next);- 7
this.__setValue(model, null);- 7
model[removeAssociationFlagName] = false;} else {- 11
next();}},//override//@see _Association_setter:function (val, model) {- 53
var name = this.name;- 53
if (!isUndefinedOrNull(val)) {- 46
this.__setValue(model, this._toModel(val));- 7
} else if (!model.isNew && isNull(val)) {- 7
model.__isChanged = true;- 7
this.__setValue(model, this._toModel(val));- 7
model[this.removeAssociationFlagName] = true;}}}}).as(module);
|
database/dataset.js
|
Coverage100.00
SLOC106
LOC19
Missed0
|
- 1
var comb = require("comb"),define = comb.define,argsToArray = comb.argsToArray,isFunction = comb.isFunction,errors = require("../errors"),NotImplemented = errors.NotImplemented,Promise = comb.Promise,Dataset = require("../dataset");- 1
var Database = define(null, {instance: {/**@lends patio.Database.prototype*//***Fetches records for an arbitrary SQL statement. If a block is given,* it is used to iterate over the records:* <pre class="code">* DB.fetch('SELECT * FROM items', function(r){* //do something with row* });*</pre>* If a block is not given then {@link patio.Database#fetch} method returns a {@link patio.Dataset} instance:* <pre class="code">* DB.fetch('SELECT * FROM items').all().chain(function(records){* //do something with the records.* });* </pre>** {@link patio.Database#fetch} can also perform parameterized queries for protection against SQL* injection:* <pre class="code">* DB.fetch('SELECT * FROM items WHERE name = ?', myName).all().chain(function(records){* //do something with the records.* });* </pre>** @param {String...} args variable number of args where the first argument is a String. If more than* one argument is given then the SQL will be treated as a place holder string. See {@link patio.Dataset#withSql}.* @param {Function} [block=null] if the last argument given is a function then {@link patio.Dataset#forEach} will be* invoked on the dataset with the block called for each row.** @return {Promise|patio.Dataset} if no block is given then a {@link patio.Dataset} will be returned. If a block* is given then the return value will be a Promise that will be invoked after all records have been returned* and processed through the block.* */fetch: function (args, block) {- 23
var ret;- 23
args = argsToArray(arguments);- 23
block = isFunction(args[args.length - 1]) ? args.pop() : null;- 23
var ds = this.dataset.withSql.apply(this.dataset, args);- 23
if (block) {- 3
ret = ds.forEach(block).chain(function () {- 3
return ds;}).promise();} else {- 20
ret = ds;}- 23
return ret;},/*** Returns a new {@link patio.Dataset} with the [@link patio.Dataset#from} method invoked. If a block is given,* it is used as a filter(see {@link patio.Dataset#filter} on the dataset.* @example* DB.from("items").sql //=> SELECT * FROM items* DB.from("items", function(){* return this.id.gt(2)* }).sql; //=> SELECT * FROM items WHERE (id > 2)* @param {String...} args table/s to pass to {@link patio.Dataset#from} with.* @param {function} [block] an option block to pass to {@link patio.Dataset#filter} with.** @return {patio.Dataset} a dataset to use for querying the {@link patio.Database} with.* */from: function (args, block) {- 491
args = argsToArray(arguments);- 491
block = isFunction(args[args.length - 1]) ? args.pop() : null;- 491
var ds = this.dataset;- 491
ds = ds.from.apply(ds, args);- 491
return block ? ds.filter(block) : ds;},/*** Returns a new {@link patio.Dataset} with the {@link patio.Dataset#select} method invoked.* @example* DB.select(1) //=> SELECT 1* DB.select(function(){* return this.server_version();* }).sql; //=> SELECT server_version()* DB.select("id").from("items").sql; //=> SELECT id FROM items* @link {patio.Dataset} a dataset to query the {@link patio.Database} with.**/select: function () {- 2
var ds = this.dataset;- 2
return ds.select.apply(ds, arguments);},/**@ignore*/getters: {dataset: function () {- 10
return new Dataset(this);}}}}).as(module);
|
database/logging.js
|
Coverage100.00
SLOC196
LOC55
Missed0
|
- 1
var comb = require("comb"),define = comb.define,Promise = comb.Promise,spreadArgs = comb.__spreadArgs,isFunction = comb.isFunction,logging = comb.logging,Logger = logging.Logger,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 () {- 7364
LOGGER.info.apply(LOGGER, arguments);},/*** Logs a DEBUG level message to the "patio.Database" logger.*/logDebug: function () {- 7053
LOGGER.debug.apply(LOGGER, arguments);},/*** Logs an ERROR level message to the "patio.Database" logger.*/logError: function (error) {- 75
LOGGER.error.apply(LOGGER, arguments);},/*** Logs a WARN level message to the "patio.Database" logger.*/logWarn: function () {- 1
LOGGER.warn.apply(LOGGER, arguments);},/*** Logs a TRACE level message to the "patio.Database" logger.*/logTrace: function () {- 1
LOGGER.trace.apply(LOGGER, arguments);},/*** Logs a FATAL level message to the "patio.Database" logger.*/logFatal: function () {- 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, opts, cb) {- 7127
if (isFunction(opts)) {- 1842
cb = opts;- 1842
opts = null;}- 7127
opts = opts || {};- 7127
var ret, start = new Date(), self = this;- 7127
sql = sql.trim();- 7127
this.logInfo("Executing; %s", sql);- 7127
if (isFunction(cb)) {- 7126
if (opts.stream) {- 5
ret = new Promise();- 5
var stream = cb();- 5
stream.on("end", function () {- 3
self.logDebug("Duration: % 6dms; %s", new Date() - start, sql);}).on("error", function (err) {- 2
err = new QueryError(format("%s: %s", err.message, sql));- 2
self.logError(err);});- 5
ret = stream;} else {- 7121
sql = sql.trim();- 7121
ret = new Promise();- 7121
cb().chain(function () {- 7049
self.logDebug("Duration: % 6dms; %s", new Date() - start, sql);- 7049
spreadArgs(ret.callback, arguments);}, function (err) {- 72
var details = err.detail;- 72
err = new QueryError(format("%s%s: %s", err.message, details ? " DETAIL('" + details + "')" : "", sql));- 72
self.logError(err);- 72
ret.errback(err);}).addErrback(ret.errback);- 7121
ret.both(function () {- 7121
ret = self = start = sql = null;});}} else {- 1
throw new QueryError("CB is required");}- 7126
return ret;},/*Log the given SQL and then execute it on the connection, used by*the transaction code.* */__logConnectionExecute: function (conn, sql) {- 1838
var connectionExecuteMethod = this.connectionExecuteMethod;- 1838
return this.__logAndExecute(sql, function () {- 1838
return conn[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);
|
dataset/features.js
|
Coverage100.00
SLOC223
LOC30
Missed0
|
- 1
var comb = require("comb"),define = comb.define;- 1
define({/**@ignore*/instance: {__providesAccurateRowsMatched: true,__requiresSqlStandardDateTimes: false,__supportsCte: true,__supportsDistinctOn: false,__supportsIntersectExcept: true,__supportsIntersectExceptAll: true,__supportsIsTrue: true,__supportsJoinUsing: true,__supportsModifyingJoins: false,__supportsMultipleColumnIn: true,__supportsTimestampTimezones: false,__supportsTimestampUsecs: true,__supportsWindowFunctions: false,/**@ignore*/getters: {/**@lends patio.Dataset.prototype*/// Whether this dataset quotes identifiers./**@ignore*/quoteIdentifiers: function () {- 45959
return this.__quoteIdentifiers;},// 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./**@ignore*/providesAccurateRowsMatched: function () {- 13885
return this.__providesAccurateRowsMatched;},//Whether the dataset requires SQL standard datetimes (false by default,// as most allow strings with ISO 8601 format)./**@ignore*/requiresSqlStandardDateTimes: function () {- 13892
return this.__requiresSqlStandardDateTimes;},// Whether the dataset supports common table expressions (the WITH clause)./**@ignore*/supportsCte: function () {- 13898
return this.__supportsCte;},// Whether the dataset supports the DISTINCT ON clause, false by default./**@ignore*/supportsDistinctOn: function () {- 2314
return this.__supportsDistinctOn;},//Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default./**@ignore*/supportsIntersectExcept: function () {- 13921
return this.__supportsIntersectExcept;},//Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default./**@ignore*/supportsIntersectExceptAll: function () {- 13897
return this.__supportsIntersectExceptAll;},//Whether the dataset supports the IS TRUE syntax./**@ignore*/supportsIsTrue: function () {- 14168
return this.__supportsIsTrue;},//Whether the dataset supports the JOIN table USING (column1, ...) syntax./**@ignore*/supportsJoinUsing: function () {- 13896
return this.__supportsJoinUsing;},//Whether modifying joined datasets is supported./**@ignore*/supportsModifyingJoins: function () {- 2560
return this.__supportsModifyingJoins;},//Whether the IN/NOT IN operators support multiple columns when an/**@ignore*/supportsMultipleColumnIn: function () {- 13889
return this.__supportsMultipleColumnIn;},//Whether the dataset supports timezones in literal timestamps/**@ignore*/supportsTimestampTimezones: function () {- 2311
return this.__supportsTimestampTimezones;},//Whether the dataset supports fractional seconds in literal timestamps/**@ignore*/supportsTimestampUsecs: function () {- 13885
return this.__supportsTimestampUsecs;},//Whether the dataset supports window functions./**@ignore*/supportsWindowFunctions: function () {- 13885
return this.__supportsWindowFunctions;}},/**@ignore*/setters: {/**@lends patio.Dataset.prototype*/// Whether this dataset quotes identifiers./**@ignore*/quoteIdentifiers: function (val) {- 13897
this.__quoteIdentifiers = val;},// 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./**@ignore*/providesAccurateRowsMatched: function (val) {- 13885
this.__providesAccurateRowsMatched = val;},//Whether the dataset requires SQL standard datetimes (false by default,// as most allow strings with ISO 8601 format)./**@ignore*/requiresSqlStandardDateTimes: function (val) {- 13885
this.__requiresSqlStandardDateTimes = val;},// Whether the dataset supports common table expressions (the WITH clause)./**@ignore*/supportsCte: function (val) {- 13886
this.__supportsCte = val;},// Whether the dataset supports the DISTINCT ON clause, false by default./**@ignore*/supportsDistinctOn: function (val) {- 2312
this.__supportsDistinctOn = val;},//Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default./**@ignore*/supportsIntersectExcept: function (val) {- 13887
this.__supportsIntersectExcept = val;},//Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default./**@ignore*/supportsIntersectExceptAll: function (val) {- 13887
this.__supportsIntersectExceptAll = val;},//Whether the dataset supports the IS TRUE syntax./**@ignore*/supportsIsTrue: function (val) {- 13885
this.__supportsIsTrue = val;},//Whether the dataset supports the JOIN table USING (column1, ...) syntax./**@ignore*/supportsJoinUsing: function (val) {- 13887
this.__supportsJoinUsing = val;},//Whether modifying joined datasets is supported./**@ignore*/supportsModifyingJoins: function (val) {- 2312
this.__supportsModifyingJoins = val;},//Whether the IN/NOT IN operators support multiple columns when an/**@ignore*/supportsMultipleColumnIn: function (val) {- 13885
this.__supportsMultipleColumnIn = val;},//Whether the dataset supports timezones in literal timestamps/**@ignore*/supportsTimestampTimezones: function (val) {- 2311
this.__supportsTimestampTimezones = val;},//Whether the dataset supports fractional seconds in literal timestamps/**@ignore*/supportsTimestampUsecs: function (val) {- 13885
this.__supportsTimestampUsecs = val;},//Whether the dataset supports window functions./**@ignore*/supportsWindowFunctions: function (val) {- 13885
this.__supportsWindowFunctions = val;}}},static: {/**@lends patio.Dataset*//*** @property {String[]}* @default ["quoteIdentifiers","providesAccurateRowsMatched","requiresSqlStandardDateTimes","supportsCte",* "supportsDistinctOn","supportsIntersectExcept","supportsIntersectExceptAll","supportsIsTrue","supportsJoinUsing",* "supportsModifyingJoins","supportsMultipleColumnIn","supportsTimestampTimezones","supportsTimestampUsecs",* "supportsWindowFunctions"]* Array of features.*/FEATURES: ["quoteIdentifiers", "providesAccurateRowsMatched", "requiresSqlStandardDateTimes", "supportsCte","supportsDistinctOn", "supportsIntersectExcept", "supportsIntersectExceptAll", "supportsIsTrue", "supportsJoinUsing","supportsModifyingJoins", "supportsMultipleColumnIn", "supportsTimestampTimezones", "supportsTimestampUsecs","supportsWindowFunctions"]}}).as(module);
|
plugins/cache.js
|
Coverage100.00
SLOC80
LOC29
Missed0
|
- 1
var comb = require("comb"),Promise = comb.Promise,PromiseList = comb.PromiseList,Hive = require("hive-cache");- 1
var hive;- 1
var i = 0;- 1
var LOGGER = comb.logging.Logger.getLogger("patio.plugins.CachePlugin");/*** @class Adds in memory caching support for models.** @example** var MyModel = patio.addModel("testTable", {* plugins : [patio.plugins.CachePlugin];* });** //NOW IT WILL CACHE** @name CachePlugin* @memberOf patio.plugins*/- 1
exports.CachePlugin = comb.define(null, {instance: {constructor: function () {- 6
this._super(arguments);- 6
this.post("load", this._postLoad);- 6
this._static.initHive();},reload: function () {- 1
var self = this;- 1
return this._super(arguments).chain(function (m) {- 1
hive.replace((self.tableName.toString() + self.primaryKeyValue.toString()), m);- 1
return m;});},_postLoad: function (next) {- 25
hive.replace(this.tableName + this.primaryKeyValue, this);- 25
next();},update: function (options, errback) {- 1
var self = this;- 1
return this._super(arguments).chain(function (val) {- 1
hive.remove(self.tableName + self.primaryKeyValue, val);- 1
return val;});},remove: function (errback) {- 1
hive.remove(this.tableName + this.primaryKeyValue);- 1
return this._super(arguments);}},static: {initHive: function () {- 6
if (!hive) {- 1
hive = new Hive();}- 6
this.cache = hive;},findById: function (id) {- 3
var cached = hive.get(this.tableName + id);- 3
if (!cached) {- 1
return this._super(arguments);} else {- 2
var ret = new Promise();- 2
ret.callback(cached);- 2
return ret;}}}});
|
plugins/columnMapper.js
|
Coverage100.00
SLOC194
LOC46
Missed0
|
- 1
var comb = require("comb"),toArray = comb.array.toArray,isInstanceOf = comb.isInstanceOf,isBoolean = comb.isBoolean,when = comb.when,isHash = comb.isHash,isString = comb.isString,sql = require("../sql.js").sql,AliasedExpression = sql.AliasedExpression,Identifier = sql.Identifier,isConditionSpecifier = sql.Expression.isConditionSpecifier,ModelError = require("../errors.js").ModelError;/*** @class This plugin exposes the ability to map columns on other tables to this Model.** See {@link patio.plugins.ColumnMapper.mappedColumn} for more information.** @name ColumnMapper* @memberof patio.plugins**/- 1
comb.define(null, {"static": {/**@lends patio.plugins.ColumnMapper*//*** Boolean flag indicating if mapped columns should be re-fetched on update.** <b>NOTE</b> This can be overridden by passing {reload : false} to the {@link patio.Model#update} method.* @default true*/fetchMappedColumnsOnUpdate: true,/*** Boolean flag indicating if mapped columns should be re-fetched on save.** <b>NOTE</b> This can be overridden by passing {reload : false} to the {@link patio.Model#save} method.* @default true*/fetchMappedColumnsOnSave: true,/*** Add a mapped column from another table. This is useful if there columns on* another table but you do not want to load the association every time.*** For example assume we have an employee and works table. Well we might want the salary from the works table,* but do not want to add it to the employee table.** <b>NOTE:</b> mapped columns are READ ONLY.** {@code* patio.addModel("employee")* .oneToOne("works")* .mappedColumn("salary", "works", {employeeId : patio.sql.identifier("id")});* }** You can also change the name of the of the column** {@code* patio.addModel("employee")* .oneToOne("works")* .mappedColumn("mySalary", "works", {employeeId : patio.sql.identifier("id")}, {* column : "salary"* });* }** If you want to prevent the mapped columns from being reloaded after a save or update you can set the* <code>fetchMappedColumnsOnUpdate</code> or <code>fetchMappedColumnsOnSave</code> to false.** {@code** var Employee = patio.addModel("employee")* .oneToOne("works")* .mappedColumn("mySalary", "works", {employeeId : patio.sql.identifier("id")}, {* column : "salary"* });** //prevent the mapped columns from being fetched after a save.* Employee.fetchMappedColumnsOnSave = false;** //prevent the mapped columns from being re-fetched after an update.* Employee.fetchMappedColumnsOnUpdate = false;* }** You can also override prevent the properties from being reloaded by setting the <code>reload</code> or <code>reloadMapped</code> options when saving or updating.** {@code* //prevents entire model from being reloaded including mapped columns* employee.save(null, {reload : false});* employee.update(null, {reload : false});** //just prevents just the mapped columns from being reloaded* employee.save(null, {reloadMapped : false});* employee.update(null, {reloadMapped : false});* }** @param {String} name the name you want the column represented as on the model.* @param {String|patio.Model} table the table or model you want the property mapped from* @param condition the join condition. See {@link patio.Dataset#joinTable}.* @param {Object} [opts={}] additional options* @param {String} [opts.joinType="left"] the join type to use when gathering the properties.* @param {String|patio.sql.Identifer} [opts.column=null] the column on the remote table that should be used* as the local copy.** @return {patio.Model} returns the model for chaining.*/mappedColumn: function (name, table, condition, opts) {- 6
opts = opts || {};- 6
if (name) {- 6
name = sql.stringToIdentifier(name);- 6
if (table && condition) {- 4
opts = comb.merge({joinType: "left", table: table, condition: condition, column: name}, opts);- 4
this._mappedColumns[name] = opts;} else {- 2
throw new ModelError("mapped column requires a table and join condition");}}- 4
return this;},sync: function () {- 35
var ret = this._super(arguments);- 35
if (!this.synced) {- 1
var self = this;- 1
ret = ret.chain(function () {- 1
var ds = self.dataset.naked().select(), mappedColumns = self._mappedColumns, joinTableAlias = "columnMapper", selects = [];- 1
Object.keys(mappedColumns).forEach(function (column, i) {- 4
var opts = mappedColumns[column],condition = opts.condition;- 4
if (isConditionSpecifier(condition)) {- 4
condition = toArray(condition);- 4
condition.forEach(function (cond) {- 4
var val = cond[1];- 4
if (!isInstanceOf(val, AliasedExpression) && isInstanceOf(val, Identifier)) {- 4
cond[1] = val.qualify(self.tableName);}});- 4
var tableAlias = sql.identifier(joinTableAlias + i);- 4
ds = ds.joinTable(opts.joinType, opts.table, condition, {tableAlias: tableAlias});- 4
selects.push(sql.stringToIdentifier(opts.column).qualify(tableAlias).as(column));}});- 1
ds = ds.select(selects);- 1
var middleWare = function (next) {- 17
var self = this;- 17
ds.filter(this._getPrimaryKeyQuery()).qualify().one().chain(function (vals) {- 17
self.setValues(vals);- 17
return vals;}).classic(next);};- 1
self.post("load", middleWare);- 1
self.post("save", function (next, options) {- 9
options = options || {};- 9
if (self.fetchMappedColumnsOnSave &&isBoolean(options.reload) ? options.reload :isBoolean(options.reloadMapped) ? options.reloadMapped :self.reloadOnSave) {- 6
middleWare.apply(this, arguments);} else {- 3
next();}});- 1
self.post("update", function (next, options) {- 4
options = options || {};- 4
if (self.fetchMappedColumnsOnUpdate &&isBoolean(options.reload) ? options.reload :isBoolean(options.reloadMapped) ? options.reloadMapped :self.reloadOnUpdate) {- 1
middleWare.apply(this, arguments);} else {- 3
next();}});});}- 35
return ret;},init: function () {- 2
this._super(arguments);- 2
this._mappedColumns = {};}}}).as(module);
|
plugins/index.js
|
Coverage100.00
SLOC16
LOC2
Missed0
|
- 1
var comb = require("comb"), inheritance = require("./inheritance");/*** @ignore* @namespace* @name patio.plugins*/- 1
comb.merge(exports, {QueryPlugin:require("./query").QueryPlugin,CachePlugin:require("./cache").CachePlugin,AssociationPlugin:require("./association").AssociationPlugin,TimeStampPlugin:require("./timestamp"),ClassTableInheritancePlugin:inheritance.ClassTableInheritance,ColumnMapper:require("./columnMapper.js"),ValidatorPlugin:require("./validation.js")});
|
plugins/timestamp.js
|
Coverage100.00
SLOC145
LOC27
Missed0
|
- 1
var define = require("comb").define, DateTime;/*** @class Time stamp plugin to support creating timestamp** @example** //initialize default timestamp functionality* var MyModel = patio.addModel("testTable", {* plugins : [patio.plugins.TimeStampPlugin],** static : {* init : function(){* this._super("arguments");* this.timestamp();* }* }* });**** //custom updated column* var MyModel = patio.addModel("testTable", {* plugins : [patio.plugins.TimeStampPlugin],** static : {* init : function(){* this._super("arguments");* this.timestamp({updated : "myUpdatedColumn"});* }* }* });** //custom created column* var MyModel = patio.addModel("testTable", {* plugins : [patio.plugins.TimeStampPlugin],** static : {* init : function(){* this._super("arguments");* this.timestamp({created : "customCreatedColumn"});* }* }* });** //set both custom columns* var MyModel = patio.addModel("testTable", {* plugins : [patio.plugins.TimeStampPlugin],** static : {* init : function(){* this._super("arguments");* this.timestamp({created : "customCreatedColumn", updated : "myUpdatedColumn"});* }* }* });** //Set to update the updated column when row is created* var MyModel = patio.addModel("testTable", {* plugins : [patio.plugins.TimeStampPlugin],** static : {* init : function(){* this._super("arguments");* this.timestamp({updateOnCreate : true});* }* }* });** //Set all three options* var MyModel = patio.addModel("testTable", {* plugins : [patio.plugins.TimeStampPlugin],** static : {* init : function(){* this._super("arguments");* this.timestamp({created : "customCreatedColumn", updated : "myUpdatedColumn", updateOnCreate : true});* }* }* });***** @name TimeStampPlugin* @memberOf patio.plugins*/- 1
module.exports = exports = define(null, {instance:{constructor:function () {- 21
this._super(arguments);- 21
var options = (this._timestampOptions = this._static._timestampOptions);- 21
this._updateColumn = options.updated || "updated";- 21
this._createdColumn = options.created || "created";- 21
this._updateOnCreate = options.updateOnCreate || false;},getInsertSql:function () {- 3
this[this._createdColumn] = new Date();- 3
if (this._updateOnCreate) {- 1
this[this._updateColumn] = new Date();}- 3
return this._super(arguments);},getUpdateSql:function () {- 3
this[this._updateColumn] = new Date();- 3
return this._super(arguments);}},static:{/**@lends patio.plugins.TimeStampPlugin*//*** Adds timestamp functionality to a table.* @param {Object} [options]* @param {String} [options.updated="updated"] the name of the column to set the updated timestamp on.* @param {String} [options.created="created"] the name of the column to set the created timestamp on* @param {Boolean} [options.updateOnCreate=false] Set to true to set the updated column on creation**/timestamp:function (options) {- 3
options = options || {};- 3
this._timestampOptions = options;- 3
var updateColumn = options.updated || "updated";- 3
var createdColumn = options.created || "created";- 3
var updateOnCreate = options.updateOnCreate || false;- 3
this.pre("save", function (next) {- 12
this[createdColumn] = new Date();- 12
if (updateOnCreate) {- 4
this[updateColumn] = new Date();}- 12
next();});- 3
this.pre("update", function (next) {- 3
this[updateColumn] = new Date();- 3
next();});- 3
return this;}}});
|
time.js
|
Coverage100.00
SLOC630
LOC72
Missed0
|
- 1
var comb = require("comb"),PatioError = require("./errors").PatioError,date = comb.date,SQL = require("./sql").sql,define = comb.define,dateFormat = date.format,isDate = comb.isDate,isUndefined = comb.isUndefined,isInstanceOf = comb.isInstanceOf;- 1
var DEFAULT_DATE_FORMAT = "yyyy-MM-dd";- 1
var TWO_YEAR_DATE_FORMAT = "yy-MM-dd";- 1
var DEFAULT_YEAR_FORMAT = "yyyy";- 1
var DEFAULT_TIME_FORMAT = "HH:mm:ss";- 1
var DEFAULT_TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ssZ";- 1
var DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ssZ";/*** Mixin that provides time formatting/coversion functions.** @constructor* @name Time* @memberOf patio** @property {String} [dateFormat={@link patio.Time#DEFAULT_DATE_FORMAT}] the format to use to formatting/converting dates.* @property {String} [yearFormat={@link patio.Time#DEFAULT_YEAR_FORMAT}] the format to use to formatting/converting dates.* @property {String} [timeFormat={@link patio.Time#DEFAULT_TIME_FORMAT}] the format to use to formatting/converting dates.* @property {String} [timeStampFormat={@link patio.Time#DEFAULT_TIMESTAMP_FORMAT}] the format to use to formatting/converting dates.* @property {String} [dateTimeFormat={@link patio.Time#DEFAULT_DATETIME_FORMAT}] the format to use to formatting/converting dates.**/- 1
define(null, {instance:{/*** @lends patio.Time.prototype*//**** @constant* @type String** @description default date format.** @default yyyy-MM-dd** @example** patio.DEFAULT_DATE_FORMAT = "yyyy MM, dd";*/DEFAULT_DATE_FORMAT:"yyyy-MM-dd",/*** @constant* @type String** @description Two year date format* This is used in date coversions when convertTwoDigitYears is used.** If this format fails then dateFormat|DEFAULT_DATE_FORMAT is used.** @default yy-MM-dd** @example** patio.TWO_YEAR_DATE_FORMAT = "yy MM, dd";*/TWO_YEAR_DATE_FORMAT:"yy-MM-dd",/*** @constant* @type String** @description Default year format* @default yyyy** @example** patio.DEFAULT_YEAR_FORMAT = "yy";*/DEFAULT_YEAR_FORMAT:"yyyy",/*** @constant* @type String** @description Default time format** @default HH:mm:ss** @example** patio.DEFAULT_TIME_FORMAT = "HH:mm:ss:SS";*/DEFAULT_TIME_FORMAT:"HH:mm:ss",/*** @constant* @type String** @description Default timestamp format** @default yyyy-MM-dd HH:mm:ss** @example** patio.DEFAULT_TIMESTAMP_FORMAT = "yyyy-MM-dd hh:mm:ss:SS a";*/DEFAULT_TIMESTAMP_FORMAT:"yyyy-MM-dd HH:mm:ss",/*** @constant* @type String** @description Default datetime format** @default yyyy-MM-dd HH:mm:ss** @example** patio.DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd hh:mm:ss:SS a";*/DEFAULT_DATETIME_FORMAT:"yyyy-MM-dd HH:mm:ss",/*** @constant* @type String** @description Timestamp format used if the default fails. This format includes timezone info.** @default yyyy-MM-dd HH:mm:ssZ*/TIMESTAMP_FORMAT_TZ:"yyyy-MM-dd HH:mm:ssZ",/*** @constant* @type String** @description Two year timestamp format. If convertTwoDigitYear is set to true and the timeStampFormat* fails this format will be tried.** @default yy-MM-dd HH:mm:ss**/TIMESTAMP_TWO_YEAR_FORMAT:"yy-MM-dd HH:mm:ss",/*** @constant* @type String** @description Datetime format used if the default fails. This format includes timezone info.** @default yyyy-MM-dd HH:mm:ssZ*/DATETIME_FORMAT_TZ:"yyyy-MM-dd HH:mm:ssZ",/*** @constant* @type String** @description Two year datetime format. If convertTwoDigitYear is set to true and the timeStampFormat* fails this format will be tried.** @default yy-MM-dd HH:mm:ss**/DATETIME_TWO_YEAR_FORMAT:"yy-MM-dd HH:mm:ss",/*** @constant* @type String** @description ISO-8601 format** @default yyyy-MM-ddTHH:mm:ssZ*/ISO_8601:"yyyy-MM-ddTHH:mm:ssZ",/*** @constant* @type String** @description Two year ISO-8601 format* @default yy-MM-ddTHH:mm:ssZ*/ISO_8601_TWO_YEAR:"yy-MM-ddTHH:mm:ssZ",/*** @type Boolean** @description By default patio will try to covert all two digit years.* To turn this off:** @default true** @example* patio.convertTwoDigitYears = false;*/convertTwoDigitYears:true,__dateFormat:undefined,__yearFormat:undefined,__timeFormat:undefined,__timeStampFormat:undefined,__dateTimeFormat:undefined,/*** Converts a {@link patio.sql.Year} to a string.* The format used is {@link patio.Time#yearFormat},* which defaults to {@link patio.Time#DEFAULT_YEAR_FORMAT}** @example** var date = new Date(2004, 1, 1, 1, 1, 1),* year = new sql.Year(2004);* patio.yearToString(date); //=> '2004'* patio.yearToString(year); //=> '2004'* patio.yearFormat = "yy";* patio.yearToString(date); //=> '04'* patio.yearToString(year); //=> '04'*** @param {Date\sql.Year} dt the year to covert to to a string.** @returns {String} the date string.*/yearToString:function (dt, format) {- 8
return dateFormat(isInstanceOf(dt, SQL.Year) ? dt.date : dt, format || this.yearFormat);},/*** Converts a {@link sql.Time} to a string.* The format used is {@link patio.Time#timeFormat},* which defaults to {@link patio.Time#DEFAULT_TIME_FORMAT}** @example** var date = new Date(null, null, null, 13, 12, 12),* time = new sql.Time(13,12,12);* patio.timeToString(date); //=> '13:12:12'* patio.timeToString(time); //=> '13:12:12'* patio.timeFormat = "hh:mm:ss";* patio.timeToString(date); //=> '01:12:12'* patio.timeToString(time); //=> '01:12:12'** @param {Date\patio.sql.Time} dt the time to covert to to a string.** @returns {String} the date string.*/timeToString:function (dt, format) {- 7
return dateFormat(isInstanceOf(dt, SQL.Time) ? dt.date : dt, format || this.timeFormat);},/*** Converts a @link{patio.sql.DateTime} to a string.* The format used is {@link patio.Time#dateTimeFormat},* which defaults to {@link patio.Time#DEFAULT_DATETIME_FORMAT}** @example** var date = new Date(2004, 1, 1, 12, 12, 12),* dateTime = new sql.DateTime(2004, 1, 1, 12, 12, 12),* offset = "-0600";* patio.dateTimeToString(date); //=> '2004-02-01 12:12:12'* patio.dateTimeToString(dateTime); //=> '2004-02-01 12:12:12'** patio.dateTimeFormat = patio.DATETIME_TWO_YEAR_FORMAT;* patio.dateTimeToString(date); //=> '04-02-01 12:12:12'* patio.dateTimeToString(dateTime); //=> '04-02-01 12:12:12'** patio.dateTimeFormat = patio.DATETIME_FORMAT_TZ;* patio.dateTimeToString(date); //=> '2004-02-01 12:12:12-0600'* patio.dateTimeToString(dateTime); //=> '2004-02-01 12:12:12-0600'** patio.dateTimeFormat = patio.ISO_8601;* patio.dateTimeToString(date); //=> '2004-02-01T12:12:12-0600'* patio.dateTimeToString(dateTime); //=> '2004-02-01T12:12:12-0600'** patio.dateTimeFormat = patio.ISO_8601_TWO_YEAR;* patio.dateTimeToString(date); //=> '04-02-01T12:12:12-0600'* patio.dateTimeToString(dateTime); //=> '04-02-01T12:12:12-0600'** @param {Date\patio.sql.DateTime} dt the datetime to covert to to a string.** @returns {String} the date string.*/dateTimeToString:function (dt, format) {- 46
return dateFormat(isInstanceOf(dt, SQL.DateTime) ? dt.date : dt, format || this.dateTimeFormat);},/*** Converts a {@link patio.sql.TimeStamp} to a string.* The format used is {@link patio.Time#timeStampFormat},* which defaults to {@link patio.Time#DEFAULT_TIMESTAMP_FORMAT}** @example** var date = new Date(2004, 1, 1, 12, 12, 12),* dateTime = new sql.TimeStamp(2004, 1, 1, 12, 12, 12),* offset = "-0600";* patio.timeStampToString(date); //=> '2004-02-01 12:12:12'* patio.timeStampToString(dateTime); //=> '2004-02-01 12:12:12'** patio.timeStampFormat = patio.TIMESTAMP_TWO_YEAR_FORMAT;* patio.timeStampToString(date); //=> '04-02-01 12:12:12'* patio.timeStampToString(dateTime); //=> '04-02-01 12:12:12'** patio.timeStampFormat = patio.TIMESTAMP_FORMAT_TZ;* patio.timeStampToString(date); //=> '2004-02-01 12:12:12-0600'* patio.timeStampToString(dateTime); //=> '2004-02-01 12:12:12-0600'** patio.timeStamp = patio.ISO_8601;* patio.timeStampToString(date); //=> '2004-02-01T12:12:12-0600'* patio.timeStampToString(dateTime); //=> '2004-02-01T12:12:12-0600'** patio.timeStamp = patio.ISO_8601_TWO_YEAR;* patio.timeStampToString(date); //=> '04-02-01T12:12:12-0600'* patio.timeStampToString(dateTime); //=> '04-02-01T12:12:12-0600'*** @param {Date\patio.sql.TimeStamp} dt the timestamp to convert to to a string.** @returns {String} the date string.*/timeStampToString:function (dt, format) {- 21
return dateFormat(isInstanceOf(dt, SQL.TimeStamp) ? dt.date : dt, format || this.timeStampFormat);},/*** Converts a date to a string.** @example** var date = new Date(2004, 1, 1),* timeStamp = new sql.TimeStamp(2004, 1, 1, 12, 12, 12),* dateTime = new sql.DateTime(2004, 1, 1, 12, 12, 12),* year = new sql.Year(2004),* time = new sql.Time(12,12,12),** //convert years* patio.dateToString(year); //=> '2004'* patio.yearFormat = "yy";* patio.dateToString(year); //=> '04'* patio.yearFormat = patio.DEFAULT_YEAR_FORMAT;* patio.dateToString(year); //=> '2004'** //convert times* patio.dateToString(time); //=> '12:12:12'** //convert dates* patio.dateToString(date); //=> '2004-02-01'* patio.dateFormat = patio.TWO_YEAR_DATE_FORMAT;* patio.dateToString(date); //=> '04-02-01'* patio.dateFormat = patio.DEFAULT_DATE_FORMAT;* patio.dateToString(date); //=> '2004-02-01'* //convert dateTime* patio.dateToString(dateTime); //=> '2004-02-01 12:12:12'* patio.dateTimeFormat = patio.DATETIME_TWO_YEAR_FORMAT;* patio.dateToString(dateTime); //=> '04-02-01 12:12:12'* patio.dateTimeFormat = patio.DATETIME_FORMAT_TZ;* patio.dateToString(dateTime); //=> '2004-02-01 12:12:12-0600'* patio.dateTimeFormat = patio.ISO_8601;* patio.dateToString(dateTime); //=> '2004-02-01T12:12:12-0600'* patio.dateTimeFormat = patio.ISO_8601_TWO_YEAR;* patio.dateToString(dateTime); //=> '04-02-01T12:12:12-0600'* patio.dateTimeFormat = patio.DEFAULT_DATETIME_FORMAT;* patio.dateToString(dateTime); //=> '2004-02-01 12:12:12'* //convert timestamps* patio.dateToString(timeStamp); //=> '2004-02-01 12:12:12'* patio.timeStampFormat = patio.TIMESTAMP_TWO_YEAR_FORMAT;* patio.dateToString(timeStamp); //=> '04-02-01 12:12:12'* patio.timeStampFormat = patio.TIMESTAMP_FORMAT_TZ;* patio.dateToString(timeStamp); //=> '2004-02-01 12:12:12-0600'* patio.timeStampFormat = patio.ISO_8601;* patio.dateToString(timeStamp); //=> '2004-02-01T12:12:12-0600'* patio.timeStampFormat = patio.ISO_8601_TWO_YEAR;* patio.dateToString(timeStamp); //=> '04-02-01T12:12:12-0600'* patio.timeStampFormat = patio.DEFAULT_TIMESTAMP_FORMAT;* patio.dateToString(timeStamp); //=> '2004-02-01 12:12:12'** @param {Date\patio.sql.Time|patio.sql.Year|patio.sql.DateTime|patio.sql.TimeStamp} dt the date to covert to to a string.** @returns {String} the date string.*/dateToString:function (dt, format) {- 62
var ret = "";- 62
if (isInstanceOf(dt, SQL.Time)) {- 3
ret = this.timeToString(dt, format);- 59
} else if (isInstanceOf(dt, SQL.Year)) {- 4
ret = this.yearToString(dt, format);- 55
} else if (isInstanceOf(dt, SQL.DateTime)) {- 34
ret = this.dateTimeToString(dt, format);- 21
} else if (isInstanceOf(dt, SQL.TimeStamp)) {- 9
ret = this.timeStampToString(dt, format);- 12
} else if (isDate(dt)) {- 12
ret = dateFormat(dt, format || this.dateFormat);}- 62
return ret;},/*** Converts a year date string to a {@link patio.sql.Year}** @example** var year = new sql.Year(2004);* patio.stringToYear("2004"); //=> year* patio.yearFormat = "yy";* patio.stringToYear("04"); //=> year** @param {String} dt the string to covert to a {@link patio.sql.Year}* @param {String} [format=patio.Time#yearFormat] the format to use when converting the date.** @throws {PatioError} thrown if the conversion fails.* @return {patio.sql.Year} the {@link patio.sql.Year}*/stringToYear:function (dt, format) {- 5
var ret = date.parse(dt, format || this.yearFormat);- 5
if (!ret) {- 1
throw new PatioError("Unable to convert year: " + dt);}- 4
return new SQL.Year(ret);},/*** Converts a time date string to a {@link patio.sql.Time}** @example** var time = new sql.Time(12,12,12);* patio.stringToTime("12:12:12"); //=> time** @param {String} dt the string to convert to a {@link patio.sql.Time}* @param {String} [format=patio.Time#timeFormat] the format to use when converting the date.** @throws {PatioError} thrown if the conversion fails.* @return {patio.sql.Time} the {@link patio.sql.Time}*/stringToTime:function (dt, format) {- 14
var ret = date.parse(dt, format || this.timeFormat);- 14
if (!ret) {- 7
throw new PatioError("Unable to convert time: " + dt);}- 7
return new SQL.Time(ret);},/*** Converts a date string to a Date** @example** var date = new Date(2004, 1,1,0,0,0);* patio.stringToDate('2004-02-01'); //=> date** patio.dateFormat = patio.TWO_YEAR_DATE_FORMAT;* patio.stringToDate('04-02-01'); //=> date** @param {String} dt the string to convert to a Date* @param {String} [format=patio.Time#dateFormat] the format to use when converting the date.** @throws {PatioError} thrown if the conversion fails.* @return {Date} the {@link Date}*/stringToDate:function (dt, format) {- 19
var ret;- 19
if (this.convertTwoDigitYears) {- 19
ret = date.parse(dt, this.TWO_YEAR_DATE_FORMAT);}- 19
if (!ret) {- 9
ret = date.parse(dt, format || this.dateFormat);}- 19
if (!ret) {- 9
throw new PatioError("Unable to convert date: " + dt);}- 10
return ret;},/*** Converts a datetime date string to a {@link patio.sql.DateTime}** @example** var dateTime = new sql.DateTime(2004, 1, 1, 12, 12, 12),* offset = getTimeZoneOffset();* patio.stringToDateTime('2004-02-01 12:12:12'); //=> dateTime** patio.dateTimeFormat = patio.DATETIME_TWO_YEAR_FORMAT;* patio.stringToDateTime('04-02-01 12:12:12-0600'); //=> dateTime** patio.dateTimeFormat = patio.DATETIME_FORMAT_TZ;* patio.stringToDateTime('2004-02-01 12:12:12-0600'); //=> dateTime** patio.dateTimeFormat = patio.ISO_8601;* patio.stringToDateTime('2004-02-01T12:12:12-0600'); //=> dateTime** patio.dateTimeFormat = patio.ISO_8601_TWO_YEAR;* patio.stringToDateTime('04-02-01T12:12:12-0600'); //=> dateTime** @param {String} dt the string to convert to a {@link patio.sql.DateTime}* @param {String} [format=patio.Time#dateTimeFormat] the format to use when converting the date.** @throws {PatioError} thrown if the conversion fails.* @return {patio.sql.DateTime} the {@linkpatio.sql.DateTime}*/stringToDateTime:function (dt, fmt) {- 21
var useT = dt.indexOf("T") !== -1;//test if there is a T in the string so we can try to properly convert it- 21
var format = fmt ? fmt : useT ? this.ISO_8601 : this.DEFAULT_DATETIME_FORMAT;- 21
var ret = date.parse(dt, format);//if the coversion failed try it with a time zone- 21
!ret && (ret = date.parse(dt, this.DATETIME_FORMAT_TZ));- 21
if (!ret && this.convertTwoDigitYears) {//if we still fail and we need to convert two digit years try the twoYearFormat- 10
var twoYearFormat = fmt ? fmt : useT ? this.ISO_8601_TWO_YEAR : this.DATETIME_TWO_YEAR_FORMAT;- 10
ret = date.parse(dt, twoYearFormat);//try with time zone- 10
!ret && (ret = date.parse(dt, twoYearFormat + "Z"));}- 21
if (!ret) {- 10
throw new PatioError("Unable to convert datetime: " + dt);}- 11
return new SQL.DateTime(ret);},/*** Converts a timestamp date string to a {@link patio.sql.TimeStamp}** @example** var timeStamp = new sql.TimeStamp(2004, 1, 1, 12, 12, 12);* patio.stringToTimeStamp('2004-02-01 12:12:12'); //=> timeStamp** patio.timeStampFormat = patio.TIMESTAMP_TWO_YEAR_FORMAT;* patio.stringToTimeStamp('04-02-01 12:12:12-0600'); //=> timeStamp** patio.timeStampFormat = patio.TIMESTAMP_FORMAT_TZ;* patio.stringToTimeStamp('2004-02-01 12:12:12-0600'); //=> timeStamp** patio.timeStampFormat = patio.ISO_8601;* patio.stringToTimeStamp('2004-02-01T12:12:12-0600'); //=> timeStamp** patio.timeStampFormat = patio.ISO_8601_TWO_YEAR;* patio.stringToTimeStamp('04-02-01T12:12:12-0600'); //=> timeStamp** @param {String} dt the string to convert to a {@link patio.sql.TimeStamp}* @param {String} [format=patio.Time#timeStampFormat] the format to use when converting the date.** @throws {PatioError} thrown if the conversion fails.* @return {patio.sql.TimeStamp} the {@link patio.sql.TimeStamp}*/stringToTimeStamp:function (dt, fmt) {- 50
var useT = dt.indexOf("T") !== -1;//test if there is a T in the string so we can try to properly convert it- 50
var format = fmt ? fmt : useT ? this.ISO_8601 : this.DEFAULT_TIMESTAMP_FORMAT;- 50
var ret = date.parse(dt, format);//if the coversion failed try it with a time zone- 50
!ret && (ret = date.parse(dt, this.TIMESTAMP_FORMAT_TZ));- 50
if (!ret && this.convertTwoDigitYears) {//if we still fail and we need to convert two digit years try the twoYearFormat- 4
var twoYearFormat = fmt ? fmt : useT ? this.ISO_8601_TWO_YEAR : this.TIMESTAMP_TWO_YEAR_FORMAT;- 4
ret = date.parse(dt, twoYearFormat);//try with time zone- 4
!ret && (ret = date.parse(dt, twoYearFormat + "Z"));}- 50
if (!ret) {- 4
throw new PatioError("Unable to convert timestamp: " + dt);}- 46
return new SQL.TimeStamp(ret);},/**@ignore*/getters:{/**@ignore*//**@ignore*/dateFormat:function (format) {- 21
return isUndefined(this.__dateFormat) ? this.DEFAULT_DATE_FORMAT : this.__dateFormat;},/**@ignore*/yearFormat:function (format) {- 13
return isUndefined(this.__yearFormat) ? this.DEFAULT_YEAR_FORMAT : this.__yearFormat;},/**@ignore*/timeFormat:function (format) {- 17
return isUndefined(this.__timeFormat) ? this.DEFAULT_TIME_FORMAT : this.__timeFormat;},/**@ignore*/timeStampFormat:function (format) {- 20
return isUndefined(this.__timeStampFormat) ? this.DEFAULT_TIMESTAMP_FORMAT : this.__timeStampFormat;},/**@ignore*/dateTimeFormat:function (format) {- 19
return isUndefined(this.__dateTimeFormat) ? this.DEFAULT_DATETIME_FORMAT : this.__dateTimeFormat;}},/**@ignore*/setters:{/**@ignore*//**@ignore*/dateFormat:function (format) {- 6
this.__dateFormat = format;},/**@ignore*/yearFormat:function (format) {- 6
this.__yearFormat = format;},/**@ignore*/timeFormat:function (format) {- 2
this.__timeFormat = format;},/**@ignore*/timeStampFormat:function (format) {- 15
this.__timeStampFormat = format;},/**@ignore*/dateTimeFormat:function (format) {- 15
this.__dateTimeFormat = format;}}}}).as(module);
|
utils.js
|
Coverage100.00
SLOC27
LOC15
Missed0
|
- 1
var stream = require("stream"),comb = require("comb"),isPromiseLike = comb.isPromiseLike,Promise = comb.Promise,hitch = comb.hitch;- 1
function pipeAll(source, dest) {- 11
source.on("error", function (err) {- 4
dest.emit("error", err);});- 11
source.pipe(dest);}- 1
function resolveOrPromisfyFunction(cb, scope, args) {- 2630
args = comb.argsToArray(arguments, 2);- 2630
var promise = new Promise(), ret;- 2630
var cbRet = cb.apply(scope, args.concat([hitch(promise, promise.resolve)]));- 2622
if (cbRet && isPromiseLike(cbRet, Promise)) {- 2619
ret = cbRet;} else {- 3
ret = promise;}- 2622
return ret;}- 1
exports.pipeAll = pipeAll;- 1
exports.resolveOrPromisfyFunction = resolveOrPromisfyFunction;