From 9782bd1b3be31bbadc31e687aa18d301cde9d177 Mon Sep 17 00:00:00 2001 From: Doug Martin Date: Tue, 28 Aug 2012 16:02:07 -0500 Subject: [PATCH 1/3] updated docs and bug fixes --- docs-md/coverage.html | 461 ++++++++++--------- docs-md/querying.md | 2 +- docs/coverage.html | 461 ++++++++++--------- docs/patio.html | 6 +- docs/patio_Dataset.html | 152 +++++-- docs/querying.html | 730 +++++++++++++++--------------- lib/dataset/index.js | 18 + lib/dataset/query.js | 24 + lib/dataset/sql.js | 149 +++--- lib/model.js | 7 +- test/model.test.js | 26 +- test/plugins/columnMapper.test.js | 1 - 12 files changed, 1110 insertions(+), 927 deletions(-) diff --git a/docs-md/coverage.html b/docs-md/coverage.html index c6344a66..70d5468b 100644 --- a/docs-md/coverage.html +++ b/docs-md/coverage.html @@ -256,10 +256,10 @@
- Coverage89.22 - SLOC21912 - LOC5351 - Missed577 + Coverage89.21 + SLOC21921 + LOC5357 + Missed578
@@ -943,10 +943,10 @@
- Coverage72.53 - SLOC1076 - LOC91 - Missed25 + Coverage71.74 + SLOC1078 + LOC92 + Missed26
@@ -1682,8 +1682,10 @@
  • * @param opts
  • */
  • configureLogging:function (opts) {
  • -
  • 0 comb.logger.configure();
  • -
  • 0 LOGGER.level = "info";
  • +
  • 0 comb.logger.configure(opts);
  • +
  • 0 if (!opts) {
  • +
  • 0 LOGGER.level = "info";
  • +
  • }
  • },
  • /**
  • @@ -2519,7 +2521,7 @@
  • },
  • _quotedIdentifier:function (c) {
  • -
  • 25927 return format('"%s"', c);
  • +
  • 26239 return format('"%s"', c);
  • },
  • __fullTextStringJoin:function (cols) {
  • @@ -2573,12 +2575,12 @@
  • 3048 var cols = [];
  • 3048 if (rows && rows.length) {
  • 2543 cols = this.__columns = fields && fields.length ? fields.map(function (f) {
  • -
  • 18922 return f.name;
  • +
  • 18560 return f.name;
  • }) : Object.keys(rows[0]);
  • //the pg driver does auto type coercion
  • 2543 cols = cols.map(function (c) {
  • -
  • 18922 return [oi(c), function (o) {
  • -
  • 28420 return o;
  • +
  • 18560 return [oi(c), function (o) {
  • +
  • 27740 return o;
  • }, c];
  • }, this);
  • }
  • @@ -2605,7 +2607,7 @@
  • 3654 var h = {};
  • 3654 var row = rows[index];
  • 3654 cols.forEach(function (col) {
  • -
  • 28420 h[col[0]] = col[1](row[col[2]]);
  • +
  • 27740 h[col[0]] = col[1](row[col[2]]);
  • }, this);
  • 3654 when(cb(h)).then(function (val) {
  • @@ -2657,15 +2659,15 @@
  • },
  • supportsDistinctOn:function () {
  • -
  • 12670 return true;
  • +
  • 12996 return true;
  • },
  • supportsModifyingJoins:function () {
  • -
  • 15114 return true;
  • +
  • 15440 return true;
  • },
  • supportsTimestampTimezones:function () {
  • -
  • 12668 return true;
  • +
  • 12994 return true;
  • }
  • }
  • @@ -3743,13 +3745,13 @@
  • * @return the typecasted value.
  • * */
  • typecastValue:function (columnType, value) {
  • -
  • 32725 if (isNull(value) || isUndefined(value)) {
  • +
  • 32726 if (isNull(value) || isUndefined(value)) {
  • 5793 return null;
  • }
  • -
  • 26932 var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
  • -
  • 26932 try {
  • -
  • 26932 if (isFunction(this[meth])) {
  • -
  • 26932 return this[meth](value);
  • +
  • 26933 var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
  • +
  • 26933 try {
  • +
  • 26933 if (isFunction(this[meth])) {
  • +
  • 26933 return this[meth](value);
  • } else {
  • 0 return value;
  • }
  • @@ -3915,7 +3917,7 @@
  • // Typecast the value to a String
  • __typecastValueString:function (value) {
  • -
  • 19607 return "" + value;
  • +
  • 19608 return "" + value;
  • }
  • },
  • @@ -6274,9 +6276,9 @@
    - Coverage88.36 - SLOC1061 - LOC318 + Coverage88.51 + SLOC1067 + LOC322 Missed37
    @@ -6474,24 +6476,24 @@
  • * @borrows patio.Dataset#leftJoin as leftJoin
  • * */
  • constructor:function (options, fromDb) {
  • -
  • 3218 if (this.synced) {
  • -
  • 3218 this.__emitter = new EventEmitter();
  • -
  • 3218 this._super(arguments);
  • -
  • 3218 this.patio = patio || require("./index");
  • -
  • 3218 fromDb = isBoolean(fromDb) ? fromDb : false;
  • -
  • 3218 this.__changed = {};
  • -
  • 3218 this.__values = {};
  • -
  • 3218 if (fromDb) {
  • -
  • 1753 this._hook("pre", "load");
  • -
  • 1753 this.__isNew = false;
  • -
  • 1753 this.__setFromDb(options, true);
  • -
  • 1753 if (this._static.emitOnLoad) {
  • -
  • 1753 this.emit("load", this);
  • -
  • 1753 this._static.emit("load", this);
  • +
  • 3111 if (this.synced) {
  • +
  • 3111 this.__emitter = new EventEmitter();
  • +
  • 3111 this._super(arguments);
  • +
  • 3111 this.patio = patio || require("./index");
  • +
  • 3111 fromDb = isBoolean(fromDb) ? fromDb : false;
  • +
  • 3111 this.__changed = {};
  • +
  • 3111 this.__values = {};
  • +
  • 3111 if (fromDb) {
  • +
  • 1754 this._hook("pre", "load");
  • +
  • 1754 this.__isNew = false;
  • +
  • 1754 this.__setFromDb(options, true);
  • +
  • 1754 if (this._static.emitOnLoad) {
  • +
  • 1754 this.emit("load", this);
  • +
  • 1754 this._static.emit("load", this);
  • }
  • } else {
  • -
  • 1465 this.__isNew = true;
  • -
  • 1465 this.__set(options);
  • +
  • 1357 this.__isNew = true;
  • +
  • 1357 this.__set(options);
  • }
  • } else {
  • 0 throw new ModelError("Model " + this.tableName + " has not been synced");
  • @@ -6499,26 +6501,32 @@
  • },
  • __set:function (values, ignore) {
  • -
  • 1587 values = values || {};
  • -
  • 1587 this.__ignore = ignore === true;
  • -
  • 1587 Object.keys(values).forEach(function (attribute) {
  • +
  • 1479 values = values || {};
  • +
  • 1479 this.__ignore = ignore === true;
  • +
  • 1479 Object.keys(values).forEach(function (attribute) {
  • 6632 var value = values[attribute];
  • //check if the column is a constrained value and is allowed to be set
  • 6632 !ignore && this._checkIfColumnIsConstrained(attribute);
  • 6632 this[attribute] = value;
  • }, this);
  • -
  • 1587 this.__ignore = false;
  • +
  • 1479 this.__ignore = false;
  • },
  • __setFromDb:function (values, ignore) {
  • -
  • 3056 values = values || {};
  • -
  • 3056 this.__ignore = ignore === true;
  • -
  • 3056 Object.keys(values).forEach(function (column) {
  • -
  • 25828 var value = values[column];
  • +
  • 3057 values = values || {};
  • +
  • 3057 this.__ignore = ignore === true;
  • +
  • 3057 var schema = this.schema;
  • +
  • 3057 Object.keys(values).forEach(function (column) {
  • +
  • 25150 var value = values[column];
  • // Typecast value retrieved from db
  • -
  • 25828 this.__values[column] = this._typeCastValue(column, value, ignore);
  • +
  • 25150 if (schema.hasOwnProperty(column)) {
  • +
  • 25149 this.__values[column] = this._typeCastValue(column, value, ignore);
  • +
  • } else {
  • +
  • 1 console.log(column);
  • +
  • 1 this[column] = value;
  • +
  • }
  • }, this);
  • -
  • 3056 this.__ignore = false;
  • +
  • 3057 this.__ignore = false;
  • },
  • @@ -6576,11 +6584,11 @@
  • },
  • _getColumnValue:function (name) {
  • -
  • 5426 var val = this.__values[name];
  • -
  • 5426 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
  • -
  • 5426 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
  • +
  • 5427 var val = this.__values[name];
  • +
  • 5427 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
  • +
  • 5427 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
  • -
  • 5426 return columnValue;
  • +
  • 5427 return columnValue;
  • },
  • _setColumnValue:function (name, val) {
  • @@ -6600,25 +6608,25 @@
  • //typecast_value method, so database adapters can override/augment the handling
  • //for database specific column types.
  • _typeCastValue:function (column, value, fromDatabase) {
  • -
  • 33384 var colSchema, clazz = this._static;
  • -
  • 33384 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
  • -
  • 32704 var type = colSchema.type;
  • -
  • 32704 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
  • +
  • 32705 var colSchema, clazz = this._static;
  • +
  • 32705 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
  • +
  • 32705 var type = colSchema.type;
  • +
  • 32705 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
  • 3 value = null;
  • }
  • -
  • 32704 var raiseOnError = clazz.raiseOnTypecastError;
  • -
  • 32704 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
  • +
  • 32705 var raiseOnError = clazz.raiseOnTypecastError;
  • +
  • 32705 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
  • 0 throw new ModelError("null is not allowed for the " + column + " column.");
  • }
  • -
  • 32704 try {
  • -
  • 32704 value = clazz.db.typecastValue(type, value);
  • +
  • 32705 try {
  • +
  • 32705 value = clazz.db.typecastValue(type, value);
  • } catch (e) {
  • 0 if (raiseOnError === true) {
  • 0 throw e;
  • }
  • }
  • }
  • -
  • 33384 return value;
  • +
  • 32705 return value;
  • },
  • /**
  • @@ -6690,8 +6698,8 @@
  • 0 return emitter.listeners.apply(emitter, arguments);
  • },
  • emit:function () {
  • -
  • 11119 var emitter = this.__emitter;
  • -
  • 11119 return emitter.emit.apply(emitter, arguments);
  • +
  • 11120 var emitter = this.__emitter;
  • +
  • 11120 return emitter.emit.apply(emitter, arguments);
  • },
  • @@ -6732,7 +6740,7 @@
  • },
  • schema:function () {
  • -
  • 73400 return this._static.schema;
  • +
  • 75099 return this._static.schema;
  • },
  • columns:function () {
  • @@ -6740,7 +6748,7 @@
  • },
  • synced:function () {
  • -
  • 12891 return this._static.synced;
  • +
  • 12784 return this._static.synced;
  • }
  • }
  • @@ -7049,7 +7057,7 @@
  • _defineColumnGetter:function (name) {
  • 779 this.prototype.__defineGetter__(name, function () {
  • -
  • 5426 return this._getColumnValue(name);
  • +
  • 5427 return this._getColumnValue(name);
  • });
  • },
  • @@ -7105,14 +7113,14 @@
  • * @type patio.Database
  • */
  • db:function () {
  • -
  • 46373 var db = this.__db;
  • -
  • 46373 if (!db) {
  • +
  • 46374 var db = this.__db;
  • +
  • 46374 if (!db) {
  • 86 db = this.__db = patio.defaultDatabase;
  • }
  • -
  • 46373 if (!db) {
  • +
  • 46374 if (!db) {
  • 0 throw new ModelError("patio has not been connected to a database");
  • }
  • -
  • 46373 return db;
  • +
  • 46374 return db;
  • },
  • /**
  • @@ -7159,8 +7167,8 @@
  • * @type Object
  • */
  • schema:function () {
  • -
  • 73404 if (this.synced) {
  • -
  • 73404 return this.__schema;
  • +
  • 75103 if (this.synced) {
  • +
  • 75103 return this.__schema;
  • } else {
  • 0 throw new ModelError("Model has not been synced yet");
  • }
  • @@ -7353,9 +7361,9 @@
    - Coverage89.05 - SLOC308 - LOC137 + Coverage89.13 + SLOC309 + LOC138 Missed15
    @@ -7418,7 +7426,8 @@
  • 0 ds = ds.apply(parent, [parent]);
  • }
  • 326 if (!ds) {
  • -
  • 326 ds = this.model.dataset.naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {
  • +
  • 326 ds = this.model.dataset;
  • +
  • 326 ds = ds.select(ds.firstSourceAlias.all()).naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {
  • 326 return sql.stringToIdentifier(k);
  • })).concat(zip(keys[0], this.parentPrimaryKey.map(function (k) {
  • 326 return parent[k];
  • @@ -9060,21 +9069,21 @@
  • 1var virtualRow = function (name) {
  • -
  • 1069 var WILDCARD = new LiteralString('*');
  • -
  • 1069 var QUESTION_MARK = new LiteralString('?');
  • -
  • 1069 var COMMA_SEPARATOR = new LiteralString(', ');
  • -
  • 1069 var DOUBLE_UNDERSCORE = '__';
  • -
  • -
  • 1069 var parts = name.split(DOUBLE_UNDERSCORE);
  • -
  • 1069 var table = parts[0], column = parts[1];
  • -
  • 1069 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • -
  • 1069 var prox = methodMissing(ident, function (m) {
  • +
  • 874 var WILDCARD = new LiteralString('*');
  • +
  • 874 var QUESTION_MARK = new LiteralString('?');
  • +
  • 874 var COMMA_SEPARATOR = new LiteralString(', ');
  • +
  • 874 var DOUBLE_UNDERSCORE = '__';
  • +
  • +
  • 874 var parts = name.split(DOUBLE_UNDERSCORE);
  • +
  • 874 var table = parts[0], column = parts[1];
  • +
  • 874 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • +
  • 874 var prox = methodMissing(ident, function (m) {
  • 4 return function () {
  • 3 var args = argsToArray(arguments);
  • 3 return SQLFunction.fromArgs([m, name].concat(args));
  • }
  • }, column ? QualifiedIdentifier : Identifier);
  • -
  • 1069 var ret = createFunctionWrapper(prox, function (m) {
  • +
  • 874 var ret = createFunctionWrapper(prox, function (m) {
  • 548 var args = argsToArray(arguments);
  • 548 if (args.length) {
  • 542 return SQLFunction.fromArgs([name].concat(args));
  • @@ -9084,8 +9093,8 @@
  • }, function () {
  • 0 return SQLFunction.fromArgs(arguments);
  • });
  • -
  • 1069 ret.__proto__ = ident;
  • -
  • 1069 return ret;
  • +
  • 874 ret.__proto__ = ident;
  • +
  • 874 return ret;
  • };
  • 1var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
  • @@ -9537,7 +9546,7 @@
  • });
  • 1exports.sql = methodMissing(sql, function (name) {
  • -
  • 1069 return virtualRow(name);
  • +
  • 874 return virtualRow(name);
  • });
  • 1var OPERTATOR_INVERSIONS = {
  • @@ -10189,7 +10198,7 @@
  • * @return {patio.sql.ColumnAll}
  • */
  • all:function () {
  • -
  • 3 return new ColumnAll(this);
  • +
  • 329 return new ColumnAll(this);
  • }
  • @@ -10347,13 +10356,13 @@
  • * @return {patio.sql.Expression} an expression.
  • */
  • fromArgs:function (args) {
  • -
  • 2410 var ret;
  • -
  • 2410 try {
  • -
  • 2410 ret = new this();
  • +
  • 2215 var ret;
  • +
  • 2215 try {
  • +
  • 2215 ret = new this();
  • } catch (ignore) {
  • }
  • -
  • 2410 this.apply(ret, args);
  • -
  • 2410 return ret;
  • +
  • 2215 this.apply(ret, args);
  • +
  • 2215 return ret;
  • },
  • /**
  • @@ -10545,7 +10554,7 @@
  • * @property table the table this all column expression represents.
  • */
  • constructor:function (table) {
  • -
  • 20 this.table = table;
  • +
  • 346 this.table = table;
  • },
  • /**
  • @@ -10557,9 +10566,9 @@
  • * @return String the SQL columnAll expression fragment.
  • */
  • toString:function (ds) {
  • -
  • 19 !Dataset && (Dataset = require("./dataset"));
  • -
  • 19 ds = ds || new Dataset();
  • -
  • 19 return ds.columnAllSql(this);
  • +
  • 345 !Dataset && (Dataset = require("./dataset"));
  • +
  • 345 ds = ds || new Dataset();
  • +
  • 345 return ds.columnAllSql(this);
  • }
  • }
  • }).as(sql, "ColumnAll");
  • @@ -11156,7 +11165,7 @@
  • * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
  • */
  • constructor:function (value) {
  • -
  • 16802 this.__value = value;
  • +
  • 16412 this.__value = value;
  • },
  • /**
  • @@ -11176,7 +11185,7 @@
  • /**@ignore*/
  • getters:{
  • value:function () {
  • -
  • 25154 return this.__value;
  • +
  • 25480 return this.__value;
  • }
  • }
  • }
  • @@ -11733,7 +11742,7 @@
  • 1var addStringMethod = function (op) {
  • 24 return function () {
  • -
  • 4294 return this.__str[op].apply(this.__str, arguments);
  • +
  • 3968 return this.__str[op].apply(this.__str, arguments);
  • }
  • };
  • @@ -11756,7 +11765,7 @@
  • * @param {String} str the literal string.
  • */
  • constructor:function (str) {
  • -
  • 3677 this.__str = str;
  • +
  • 3092 this.__str = str;
  • }
  • }
  • }).as(sql, "LiteralString");
  • @@ -12660,14 +12669,14 @@
  • * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
  • */
  • constructor:function (db, opts) {
  • -
  • 29216 this._super(arguments);
  • -
  • 29216 this.db = db;
  • -
  • 29216 this.__opts = {};
  • -
  • 29216 this.__rowCb = null;
  • -
  • 29216 if (db) {
  • -
  • 15039 this.__quoteIdentifiers = db.quoteIdentifiers;
  • -
  • 15039 this.__identifierInputMethod = db.identifierInputMethod;
  • -
  • 15039 this.__identifierOutputMethod = db.identifierOutputMethod;
  • +
  • 29542 this._super(arguments);
  • +
  • 29542 this.db = db;
  • +
  • 29542 this.__opts = {};
  • +
  • 29542 this.__rowCb = null;
  • +
  • 29542 if (db) {
  • +
  • 15365 this.__quoteIdentifiers = db.quoteIdentifiers;
  • +
  • 15365 this.__identifierInputMethod = db.identifierInputMethod;
  • +
  • 15365 this.__identifierOutputMethod = db.identifierOutputMethod;
  • }
  • },
  • @@ -12682,22 +12691,22 @@
  • * @return [patio.Dataset] a cloned dataset with the merged options
  • **/
  • mergeOptions:function (opts) {
  • -
  • 14948 opts = isUndefined(opts) ? {} : opts;
  • -
  • 14948 var ds = new this._static(this.db, {});
  • -
  • 14948 ds.rowCb = this.rowCb;
  • -
  • 14948 this._static.FEATURES.forEach(function (f) {
  • -
  • 209272 ds[f] = this[f];
  • +
  • 15274 opts = isUndefined(opts) ? {} : opts;
  • +
  • 15274 var ds = new this._static(this.db, {});
  • +
  • 15274 ds.rowCb = this.rowCb;
  • +
  • 15274 this._static.FEATURES.forEach(function (f) {
  • +
  • 213836 ds[f] = this[f];
  • }, this);
  • -
  • 14948 ds.__opts = merge({}, this.__opts, opts);
  • -
  • 14948 ds.identifierInputMethod = this.identifierInputMethod;
  • -
  • 14948 ds.identifierOutputMethod = this.identifierOutputMethod;
  • -
  • 14948 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
  • -
  • 14948 if (Object.keys(opts).some(function (o) {
  • -
  • 13536 return columnChangeOpts.indexOf(o) != -1;
  • +
  • 15274 ds.__opts = merge({}, this.__opts, opts);
  • +
  • 15274 ds.identifierInputMethod = this.identifierInputMethod;
  • +
  • 15274 ds.identifierOutputMethod = this.identifierOutputMethod;
  • +
  • 15274 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
  • +
  • 15274 if (Object.keys(opts).some(function (o) {
  • +
  • 13862 return columnChangeOpts.indexOf(o) != -1;
  • })) {
  • -
  • 2456 ds.__opts.columns = null;
  • +
  • 2782 ds.__opts.columns = null;
  • }
  • -
  • 14948 return ds;
  • +
  • 15274 return ds;
  • },
  • @@ -12784,31 +12793,31 @@
  • getters:{
  • rowCb:function () {
  • -
  • 23052 return this.__rowCb;
  • +
  • 23378 return this.__rowCb;
  • },
  • identifierInputMethod:function () {
  • -
  • 14948 return this.__identifierInputMethod;
  • +
  • 15274 return this.__identifierInputMethod;
  • },
  • identifierOutputMethod:function () {
  • -
  • 14948 return this.__identifierOutputMethod;
  • +
  • 15274 return this.__identifierOutputMethod;
  • },
  • firstSourceAlias:function () {
  • -
  • 579 var source = this.__opts.from;
  • -
  • 579 if (isUndefinedOrNull(source) || !source.length) {
  • +
  • 905 var source = this.__opts.from;
  • +
  • 905 if (isUndefinedOrNull(source) || !source.length) {
  • 2 throw new DatasetError("No source specified for the query");
  • }
  • -
  • 577 source = source[0];
  • -
  • 577 if (isInstanceOf(source, AliasedExpression)) {
  • +
  • 903 source = source[0];
  • +
  • 903 if (isInstanceOf(source, AliasedExpression)) {
  • 20 return source.alias;
  • -
  • 557 } else if (isString(source)) {
  • +
  • 883 } else if (isString(source)) {
  • 0 var parts = this._splitString(source);
  • 0 var alias = parts[2];
  • 0 return alias ? alias : source;
  • } else {
  • -
  • 557 return source;
  • +
  • 883 return source;
  • }
  • },
  • @@ -12836,16 +12845,16 @@
  • /**@lends patio.Dataset.prototype*/
  • identifierInputMethod:function (meth) {
  • -
  • 15038 this.__identifierInputMethod = meth;
  • +
  • 15364 this.__identifierInputMethod = meth;
  • },
  • identifierOutputMethod:function (meth) {
  • -
  • 15038 this.__identifierOutputMethod = meth;
  • +
  • 15364 this.__identifierOutputMethod = meth;
  • },
  • rowCb:function (cb) {
  • -
  • 18564 if (isFunction(cb) || isNull(cb)) {
  • -
  • 18559 this.__rowCb = cb;
  • +
  • 18890 if (isFunction(cb) || isNull(cb)) {
  • +
  • 18885 this.__rowCb = cb;
  • } else {
  • 5 throw new DatasetError("rowCb mus be a function");
  • }
  • @@ -13976,9 +13985,9 @@
  • constructor:function () {
  • //We initialize these here because otherwise
  • //the will be blank because of recursive dependencies.
  • -
  • 29216 !patio && (patio = require("../index"));
  • -
  • 29216 !Dataset && (Dataset = patio.Dataset);
  • -
  • 29216 this._super(arguments);
  • +
  • 29542 !patio && (patio = require("../index"));
  • +
  • 29542 !Dataset && (Dataset = patio.Dataset);
  • +
  • 29542 this._super(arguments);
  • },
  • /**
  • @@ -14263,15 +14272,15 @@
  • * @return {String} a literal representation of the value.
  • */
  • literal:function (v) {
  • -
  • 44422 if (isInstanceOf(v, LiteralString)) {
  • +
  • 44748 if (isInstanceOf(v, LiteralString)) {
  • 204 return "" + v;
  • -
  • 44218 } else if (isString(v)) {
  • +
  • 44544 } else if (isString(v)) {
  • 6110 return this._literalString(v);
  • -
  • 38108 } else if (isNumber(v)) {
  • +
  • 38434 } else if (isNumber(v)) {
  • 6997 return this._literalNumber(v);
  • }
  • -
  • 31111 else if (isInstanceOf(v, Expression)) {
  • -
  • 28577 return this._literalExpression(v);
  • +
  • 31437 else if (isInstanceOf(v, Expression)) {
  • +
  • 28903 return this._literalExpression(v);
  • }
  • 2534 else if (isInstanceOf(v, Dataset)) {
  • 104 return this._literalDataset(v);
  • @@ -14583,7 +14592,7 @@
  • * SQL fragment for specifying all columns in a given table
  • **/
  • columnAllSql:function (ca) {
  • -
  • 19 return string.format("%s.*", this.quoteSchemaTable(ca.table));
  • +
  • 345 return string.format("%s.*", this.quoteSchemaTable(ca.table));
  • },
  • /**
  • @@ -14789,18 +14798,18 @@
  • * quote the name with {@link patio.dataset._Sql#_quotedIdentifier}.
  • */
  • quoteIdentifier:function (name) {
  • -
  • 33767 if (isInstanceOf(name, LiteralString)) {
  • +
  • 34093 if (isInstanceOf(name, LiteralString)) {
  • 260 return name;
  • } else {
  • -
  • 33507 if (isInstanceOf(name, Identifier)) {
  • +
  • 33833 if (isInstanceOf(name, Identifier)) {
  • 22237 name = name.value;
  • }
  • -
  • 33507 name = this.inputIdentifier(name);
  • -
  • 33507 if (this.quoteIdentifiers) {
  • -
  • 26311 name = this._quotedIdentifier(name)
  • +
  • 33833 name = this.inputIdentifier(name);
  • +
  • 33833 if (this.quoteIdentifiers) {
  • +
  • 26623 name = this._quotedIdentifier(name)
  • }
  • }
  • -
  • 33507 return name;
  • +
  • 33833 return name;
  • },
  • /**
  • @@ -14810,9 +14819,9 @@
  • * identifierOutputMethod.
  • */
  • inputIdentifier:function (v) {
  • -
  • 33693 var i = this.__identifierInputMethod;
  • -
  • 33693 v = v.toString(this);
  • -
  • 33693 return !isUndefinedOrNull(i) ?
  • +
  • 34019 var i = this.__identifierInputMethod;
  • +
  • 34019 v = v.toString(this);
  • +
  • 34019 return !isUndefinedOrNull(i) ?
  • isFunction(v[i]) ?
  • v[i]() :
  • isFunction(comb[i]) ?
  • @@ -14828,9 +14837,9 @@
  • * identifierOutputMethod.
  • */
  • outputIdentifier:function (v) {
  • -
  • 19885 (v == '' && (v = 'untitled'));
  • -
  • 19885 var i = this.__identifierOutputMethod;
  • -
  • 19885 return !isUndefinedOrNull(i) ?
  • +
  • 19523 (v == '' && (v = 'untitled'));
  • +
  • 19523 var i = this.__identifierOutputMethod;
  • +
  • 19523 return !isUndefinedOrNull(i) ?
  • isFunction(v[i]) ?
  • v[i]() :
  • isFunction(comb[i]) ?
  • @@ -14846,10 +14855,10 @@
  • * quoted (if quoting identifiers)
  • */
  • quoteSchemaTable:function (table) {
  • -
  • 2231 var parts = this.schemaAndTable(table);
  • -
  • 2231 var schema = parts[0];
  • -
  • 2231 table = parts[1];
  • -
  • 2231 return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));
  • +
  • 2557 var parts = this.schemaAndTable(table);
  • +
  • 2557 var schema = parts[0];
  • +
  • 2557 table = parts[1];
  • +
  • 2557 return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));
  • },
  • @@ -14858,15 +14867,15 @@
  • * Split the schema information from the table
  • */
  • schemaAndTable:function (tableName) {
  • -
  • 2475 var sch = this.db ? this.db.defaultSchema || null : null;
  • -
  • 2475 if (isString(tableName)) {
  • +
  • 2801 var sch = this.db ? this.db.defaultSchema || null : null;
  • +
  • 2801 if (isString(tableName)) {
  • 1008 var parts = this._splitString(tableName);
  • 1008 var s = parts[0], table = parts[1];
  • 1008 return [s || sch, table];
  • -
  • 1467 } else if (isInstanceOf(tableName, QualifiedIdentifier)) {
  • +
  • 1793 } else if (isInstanceOf(tableName, QualifiedIdentifier)) {
  • 3 return [tableName.table, tableName.column]
  • -
  • 1464 } else if (isInstanceOf(tableName, Identifier)) {
  • -
  • 1464 return [null, tableName.value];
  • +
  • 1790 } else if (isInstanceOf(tableName, Identifier)) {
  • +
  • 1790 return [null, tableName.value];
  • } else {
  • 0 throw new QueryError("table should be a QualifiedIdentifier, Identifier, or String");
  • }
  • @@ -14915,7 +14924,7 @@
  • * expressions.
  • */
  • __expressionList:function (columns) {
  • -
  • 4284 return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);
  • +
  • 4610 return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);
  • },
  • //Format the timestamp based on the default_timestamp_format, with a couple
  • @@ -15037,7 +15046,7 @@
  • * @return SQL fragment for SQL::Expression, result depends on the specific type of expression.
  • * */
  • _literalExpression:function (v) {
  • -
  • 28587 return v.toString(this);
  • +
  • 28913 return v.toString(this);
  • },
  • /**
  • @@ -15703,7 +15712,7 @@
  • * @type String
  • */
  • identifierInputMethod:function () {
  • -
  • 15038 return this.__identifierInputMethod;
  • +
  • 15364 return this.__identifierInputMethod;
  • },
  • /**
  • @@ -15715,7 +15724,7 @@
  • * @type String
  • */
  • identifierOutputMethod:function () {
  • -
  • 15038 return this.__identifierOutputMethod;
  • +
  • 15364 return this.__identifierOutputMethod;
  • },
  • /**
  • @@ -15728,7 +15737,7 @@
  • * @default true
  • */
  • quoteIdentifiers:function () {
  • -
  • 15004 return this.__quoteIdentifiers;
  • +
  • 15330 return this.__quoteIdentifiers;
  • }
  • },
  • @@ -15871,10 +15880,10 @@
  • /**@ignore*/
  • constructor:function () {
  • -
  • 29216 if (!Dataset) {
  • +
  • 29542 if (!Dataset) {
  • 1 Dataset = require("../index").Dataset;
  • }
  • -
  • 29216 this._super(arguments);
  • +
  • 29542 this._super(arguments);
  • },
  • @@ -15904,7 +15913,7 @@
  • 2734 var a = [];
  • 2734 var ret = new Promise().classic(cb);
  • 2734 this.forEach(hitch(this, function (r) {
  • -
  • 2936 a.push(r);
  • +
  • 2934 a.push(r);
  • })).then(hitch(this, function () {
  • 2731 this.postLoad(a);
  • 2731 if (block) {
  • @@ -17150,10 +17159,10 @@
  • *
  • */
  • constructor:function () {
  • -
  • 3218 if (comb.isUndefinedOrNull(this.__associations)) {
  • -
  • 3055 this.__associations = {};
  • +
  • 3111 if (comb.isUndefinedOrNull(this.__associations)) {
  • +
  • 3002 this.__associations = {};
  • }
  • -
  • 3218 this._super(arguments);
  • +
  • 3111 this._super(arguments);
  • },
  • reload:function () {
  • @@ -18392,17 +18401,17 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 29216 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 29216 this._super(arguments);
  • -
  • 29216 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
  • -
  • 204512 if (!this[type + "Join"]) {
  • -
  • 204512 this[type + "Join"] = conditionedJoin.bind(this, type);
  • +
  • 29542 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 29542 this._super(arguments);
  • +
  • 29542 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
  • +
  • 206794 if (!this[type + "Join"]) {
  • +
  • 206794 this[type + "Join"] = conditionedJoin.bind(this, type);
  • }
  • }, this);
  • -
  • 29216 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
  • -
  • 146080 if (!this[type + "Join"]) {
  • -
  • 146080 this[type + "Join"] = unConditionJoin.bind(this, type);
  • +
  • 29542 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
  • +
  • 147710 if (!this[type + "Join"]) {
  • +
  • 147710 this[type + "Join"] = unConditionJoin.bind(this, type);
  • }
  • }, this);
  • @@ -19814,29 +19823,29 @@
  • * @return {patio.Dataset} a cloned dataset with the columns selected changed.
  • */
  • select:function (args) {
  • -
  • 535 args = flatten(argsToArray(arguments));
  • -
  • 535 var columns = [];
  • -
  • 535 args.forEach(function (c) {
  • -
  • 955 if (isFunction(c)) {
  • +
  • 861 args = flatten(argsToArray(arguments));
  • +
  • 861 var columns = [];
  • +
  • 861 args.forEach(function (c) {
  • +
  • 1281 if (isFunction(c)) {
  • 23 var res = c.apply(sql, [sql]);
  • 23 columns = columns.concat(isArray(res) ? res : [res]);
  • } else {
  • -
  • 932 columns.push(c);
  • +
  • 1258 columns.push(c);
  • }
  • });
  • -
  • 535 var select = [];
  • -
  • 535 columns.forEach(function (c) {
  • -
  • 958 if (isHash(c)) {
  • +
  • 861 var select = [];
  • +
  • 861 columns.forEach(function (c) {
  • +
  • 1284 if (isHash(c)) {
  • 3 for (var i in c) {
  • 4 select.push(new AliasedExpression(new Identifier(i), c[i]));
  • }
  • -
  • 955 } else if (isString(c)) {
  • +
  • 1281 } else if (isString(c)) {
  • 406 select.push(this.stringToIdentifier(c));
  • } else {
  • -
  • 549 select.push(c);
  • +
  • 875 select.push(c);
  • }
  • }, this);
  • -
  • 535 return this.mergeOptions({select:select});
  • +
  • 861 return this.mergeOptions({select:select});
  • },
  • @@ -21117,7 +21126,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(){
  • -
  • 48455 return this.__quoteIdentifiers;
  • +
  • 49107 return this.__quoteIdentifiers;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21125,20 +21134,20 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(){
  • -
  • 14948 return this.__providesAccurateRowsMatched;
  • +
  • 15274 return this.__providesAccurateRowsMatched;
  • },
  • //Whether the dataset requires SQL standard datetimes (false by default,
  • // as most allow strings with ISO 8601 format).
  • /**@ignore*/
  • requiresSqlStandardDateTimes:function(){
  • -
  • 14955 return this.__requiresSqlStandardDateTimes;
  • +
  • 15281 return this.__requiresSqlStandardDateTimes;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(){
  • -
  • 14961 return this.__supportsCte;
  • +
  • 15287 return this.__supportsCte;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • @@ -21150,25 +21159,25 @@
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(){
  • -
  • 14984 return this.__supportsIntersectExcept;
  • +
  • 15310 return this.__supportsIntersectExcept;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(){
  • -
  • 14960 return this.__supportsIntersectExceptAll;
  • +
  • 15286 return this.__supportsIntersectExceptAll;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(){
  • -
  • 15217 return this.__supportsIsTrue;
  • +
  • 15543 return this.__supportsIsTrue;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(){
  • -
  • 14959 return this.__supportsJoinUsing;
  • +
  • 15285 return this.__supportsJoinUsing;
  • },
  • //Whether modifying joined datasets is supported.
  • @@ -21180,7 +21189,7 @@
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(){
  • -
  • 14952 return this.__supportsMultipleColumnIn;
  • +
  • 15278 return this.__supportsMultipleColumnIn;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • @@ -21192,13 +21201,13 @@
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(){
  • -
  • 14948 return this.__supportsTimestampUsecs;
  • +
  • 15274 return this.__supportsTimestampUsecs;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(){
  • -
  • 14948 return this.__supportsWindowFunctions;
  • +
  • 15274 return this.__supportsWindowFunctions;
  • }
  • },
  • @@ -21209,7 +21218,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(val){
  • -
  • 14960 this.__quoteIdentifiers = val;
  • +
  • 15286 this.__quoteIdentifiers = val;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21217,20 +21226,20 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(val){
  • -
  • 14948 this.__providesAccurateRowsMatched = val;
  • +
  • 15274 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){
  • -
  • 14948 this.__requiresSqlStandardDateTimes = val;
  • +
  • 15274 this.__requiresSqlStandardDateTimes = val;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(val){
  • -
  • 14949 this.__supportsCte = val;
  • +
  • 15275 this.__supportsCte = val;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • @@ -21242,25 +21251,25 @@
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(val){
  • -
  • 14950 this.__supportsIntersectExcept = val;
  • +
  • 15276 this.__supportsIntersectExcept = val;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(val){
  • -
  • 14950 this.__supportsIntersectExceptAll = val;
  • +
  • 15276 this.__supportsIntersectExceptAll = val;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(val){
  • -
  • 14948 this.__supportsIsTrue = val;
  • +
  • 15274 this.__supportsIsTrue = val;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(val){
  • -
  • 14950 this.__supportsJoinUsing = val;
  • +
  • 15276 this.__supportsJoinUsing = val;
  • },
  • //Whether modifying joined datasets is supported.
  • @@ -21272,7 +21281,7 @@
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(val){
  • -
  • 14948 this.__supportsMultipleColumnIn = val;
  • +
  • 15274 this.__supportsMultipleColumnIn = val;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • @@ -21284,13 +21293,13 @@
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(val){
  • -
  • 14948 this.__supportsTimestampUsecs = val;
  • +
  • 15274 this.__supportsTimestampUsecs = val;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(val){
  • -
  • 14948 this.__supportsWindowFunctions = val;
  • +
  • 15274 this.__supportsWindowFunctions = val;
  • }
  • }
  • @@ -21384,8 +21393,8 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 29216 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 29216 this._super(arguments);
  • +
  • 29542 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 29542 this._super(arguments);
  • },
  • /**
  • diff --git a/docs-md/querying.md b/docs-md/querying.md index 9e988d34..72baa7d3 100644 --- a/docs-md/querying.md +++ b/docs-md/querying.md @@ -265,7 +265,7 @@ If there are multiple arguments in the hash, the filters are `AND`ed together: ``` //SELECT * FROM user WHERE id IN (1, 2, 3) AND name ~ 'Bo$' User.filter({id : [1,2,3], name : /Bo$/}); -`` +``` This works the same as if you used two separate filter calls: diff --git a/docs/coverage.html b/docs/coverage.html index c6344a66..70d5468b 100644 --- a/docs/coverage.html +++ b/docs/coverage.html @@ -256,10 +256,10 @@
    - Coverage89.22 - SLOC21912 - LOC5351 - Missed577 + Coverage89.21 + SLOC21921 + LOC5357 + Missed578
    @@ -943,10 +943,10 @@
    - Coverage72.53 - SLOC1076 - LOC91 - Missed25 + Coverage71.74 + SLOC1078 + LOC92 + Missed26
    @@ -1682,8 +1682,10 @@
  • * @param opts
  • */
  • configureLogging:function (opts) {
  • -
  • 0 comb.logger.configure();
  • -
  • 0 LOGGER.level = "info";
  • +
  • 0 comb.logger.configure(opts);
  • +
  • 0 if (!opts) {
  • +
  • 0 LOGGER.level = "info";
  • +
  • }
  • },
  • /**
  • @@ -2519,7 +2521,7 @@
  • },
  • _quotedIdentifier:function (c) {
  • -
  • 25927 return format('"%s"', c);
  • +
  • 26239 return format('"%s"', c);
  • },
  • __fullTextStringJoin:function (cols) {
  • @@ -2573,12 +2575,12 @@
  • 3048 var cols = [];
  • 3048 if (rows && rows.length) {
  • 2543 cols = this.__columns = fields && fields.length ? fields.map(function (f) {
  • -
  • 18922 return f.name;
  • +
  • 18560 return f.name;
  • }) : Object.keys(rows[0]);
  • //the pg driver does auto type coercion
  • 2543 cols = cols.map(function (c) {
  • -
  • 18922 return [oi(c), function (o) {
  • -
  • 28420 return o;
  • +
  • 18560 return [oi(c), function (o) {
  • +
  • 27740 return o;
  • }, c];
  • }, this);
  • }
  • @@ -2605,7 +2607,7 @@
  • 3654 var h = {};
  • 3654 var row = rows[index];
  • 3654 cols.forEach(function (col) {
  • -
  • 28420 h[col[0]] = col[1](row[col[2]]);
  • +
  • 27740 h[col[0]] = col[1](row[col[2]]);
  • }, this);
  • 3654 when(cb(h)).then(function (val) {
  • @@ -2657,15 +2659,15 @@
  • },
  • supportsDistinctOn:function () {
  • -
  • 12670 return true;
  • +
  • 12996 return true;
  • },
  • supportsModifyingJoins:function () {
  • -
  • 15114 return true;
  • +
  • 15440 return true;
  • },
  • supportsTimestampTimezones:function () {
  • -
  • 12668 return true;
  • +
  • 12994 return true;
  • }
  • }
  • @@ -3743,13 +3745,13 @@
  • * @return the typecasted value.
  • * */
  • typecastValue:function (columnType, value) {
  • -
  • 32725 if (isNull(value) || isUndefined(value)) {
  • +
  • 32726 if (isNull(value) || isUndefined(value)) {
  • 5793 return null;
  • }
  • -
  • 26932 var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
  • -
  • 26932 try {
  • -
  • 26932 if (isFunction(this[meth])) {
  • -
  • 26932 return this[meth](value);
  • +
  • 26933 var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();
  • +
  • 26933 try {
  • +
  • 26933 if (isFunction(this[meth])) {
  • +
  • 26933 return this[meth](value);
  • } else {
  • 0 return value;
  • }
  • @@ -3915,7 +3917,7 @@
  • // Typecast the value to a String
  • __typecastValueString:function (value) {
  • -
  • 19607 return "" + value;
  • +
  • 19608 return "" + value;
  • }
  • },
  • @@ -6274,9 +6276,9 @@
    - Coverage88.36 - SLOC1061 - LOC318 + Coverage88.51 + SLOC1067 + LOC322 Missed37
    @@ -6474,24 +6476,24 @@
  • * @borrows patio.Dataset#leftJoin as leftJoin
  • * */
  • constructor:function (options, fromDb) {
  • -
  • 3218 if (this.synced) {
  • -
  • 3218 this.__emitter = new EventEmitter();
  • -
  • 3218 this._super(arguments);
  • -
  • 3218 this.patio = patio || require("./index");
  • -
  • 3218 fromDb = isBoolean(fromDb) ? fromDb : false;
  • -
  • 3218 this.__changed = {};
  • -
  • 3218 this.__values = {};
  • -
  • 3218 if (fromDb) {
  • -
  • 1753 this._hook("pre", "load");
  • -
  • 1753 this.__isNew = false;
  • -
  • 1753 this.__setFromDb(options, true);
  • -
  • 1753 if (this._static.emitOnLoad) {
  • -
  • 1753 this.emit("load", this);
  • -
  • 1753 this._static.emit("load", this);
  • +
  • 3111 if (this.synced) {
  • +
  • 3111 this.__emitter = new EventEmitter();
  • +
  • 3111 this._super(arguments);
  • +
  • 3111 this.patio = patio || require("./index");
  • +
  • 3111 fromDb = isBoolean(fromDb) ? fromDb : false;
  • +
  • 3111 this.__changed = {};
  • +
  • 3111 this.__values = {};
  • +
  • 3111 if (fromDb) {
  • +
  • 1754 this._hook("pre", "load");
  • +
  • 1754 this.__isNew = false;
  • +
  • 1754 this.__setFromDb(options, true);
  • +
  • 1754 if (this._static.emitOnLoad) {
  • +
  • 1754 this.emit("load", this);
  • +
  • 1754 this._static.emit("load", this);
  • }
  • } else {
  • -
  • 1465 this.__isNew = true;
  • -
  • 1465 this.__set(options);
  • +
  • 1357 this.__isNew = true;
  • +
  • 1357 this.__set(options);
  • }
  • } else {
  • 0 throw new ModelError("Model " + this.tableName + " has not been synced");
  • @@ -6499,26 +6501,32 @@
  • },
  • __set:function (values, ignore) {
  • -
  • 1587 values = values || {};
  • -
  • 1587 this.__ignore = ignore === true;
  • -
  • 1587 Object.keys(values).forEach(function (attribute) {
  • +
  • 1479 values = values || {};
  • +
  • 1479 this.__ignore = ignore === true;
  • +
  • 1479 Object.keys(values).forEach(function (attribute) {
  • 6632 var value = values[attribute];
  • //check if the column is a constrained value and is allowed to be set
  • 6632 !ignore && this._checkIfColumnIsConstrained(attribute);
  • 6632 this[attribute] = value;
  • }, this);
  • -
  • 1587 this.__ignore = false;
  • +
  • 1479 this.__ignore = false;
  • },
  • __setFromDb:function (values, ignore) {
  • -
  • 3056 values = values || {};
  • -
  • 3056 this.__ignore = ignore === true;
  • -
  • 3056 Object.keys(values).forEach(function (column) {
  • -
  • 25828 var value = values[column];
  • +
  • 3057 values = values || {};
  • +
  • 3057 this.__ignore = ignore === true;
  • +
  • 3057 var schema = this.schema;
  • +
  • 3057 Object.keys(values).forEach(function (column) {
  • +
  • 25150 var value = values[column];
  • // Typecast value retrieved from db
  • -
  • 25828 this.__values[column] = this._typeCastValue(column, value, ignore);
  • +
  • 25150 if (schema.hasOwnProperty(column)) {
  • +
  • 25149 this.__values[column] = this._typeCastValue(column, value, ignore);
  • +
  • } else {
  • +
  • 1 console.log(column);
  • +
  • 1 this[column] = value;
  • +
  • }
  • }, this);
  • -
  • 3056 this.__ignore = false;
  • +
  • 3057 this.__ignore = false;
  • },
  • @@ -6576,11 +6584,11 @@
  • },
  • _getColumnValue:function (name) {
  • -
  • 5426 var val = this.__values[name];
  • -
  • 5426 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
  • -
  • 5426 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
  • +
  • 5427 var val = this.__values[name];
  • +
  • 5427 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
  • +
  • 5427 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
  • -
  • 5426 return columnValue;
  • +
  • 5427 return columnValue;
  • },
  • _setColumnValue:function (name, val) {
  • @@ -6600,25 +6608,25 @@
  • //typecast_value method, so database adapters can override/augment the handling
  • //for database specific column types.
  • _typeCastValue:function (column, value, fromDatabase) {
  • -
  • 33384 var colSchema, clazz = this._static;
  • -
  • 33384 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
  • -
  • 32704 var type = colSchema.type;
  • -
  • 32704 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
  • +
  • 32705 var colSchema, clazz = this._static;
  • +
  • 32705 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
  • +
  • 32705 var type = colSchema.type;
  • +
  • 32705 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
  • 3 value = null;
  • }
  • -
  • 32704 var raiseOnError = clazz.raiseOnTypecastError;
  • -
  • 32704 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
  • +
  • 32705 var raiseOnError = clazz.raiseOnTypecastError;
  • +
  • 32705 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
  • 0 throw new ModelError("null is not allowed for the " + column + " column.");
  • }
  • -
  • 32704 try {
  • -
  • 32704 value = clazz.db.typecastValue(type, value);
  • +
  • 32705 try {
  • +
  • 32705 value = clazz.db.typecastValue(type, value);
  • } catch (e) {
  • 0 if (raiseOnError === true) {
  • 0 throw e;
  • }
  • }
  • }
  • -
  • 33384 return value;
  • +
  • 32705 return value;
  • },
  • /**
  • @@ -6690,8 +6698,8 @@
  • 0 return emitter.listeners.apply(emitter, arguments);
  • },
  • emit:function () {
  • -
  • 11119 var emitter = this.__emitter;
  • -
  • 11119 return emitter.emit.apply(emitter, arguments);
  • +
  • 11120 var emitter = this.__emitter;
  • +
  • 11120 return emitter.emit.apply(emitter, arguments);
  • },
  • @@ -6732,7 +6740,7 @@
  • },
  • schema:function () {
  • -
  • 73400 return this._static.schema;
  • +
  • 75099 return this._static.schema;
  • },
  • columns:function () {
  • @@ -6740,7 +6748,7 @@
  • },
  • synced:function () {
  • -
  • 12891 return this._static.synced;
  • +
  • 12784 return this._static.synced;
  • }
  • }
  • @@ -7049,7 +7057,7 @@
  • _defineColumnGetter:function (name) {
  • 779 this.prototype.__defineGetter__(name, function () {
  • -
  • 5426 return this._getColumnValue(name);
  • +
  • 5427 return this._getColumnValue(name);
  • });
  • },
  • @@ -7105,14 +7113,14 @@
  • * @type patio.Database
  • */
  • db:function () {
  • -
  • 46373 var db = this.__db;
  • -
  • 46373 if (!db) {
  • +
  • 46374 var db = this.__db;
  • +
  • 46374 if (!db) {
  • 86 db = this.__db = patio.defaultDatabase;
  • }
  • -
  • 46373 if (!db) {
  • +
  • 46374 if (!db) {
  • 0 throw new ModelError("patio has not been connected to a database");
  • }
  • -
  • 46373 return db;
  • +
  • 46374 return db;
  • },
  • /**
  • @@ -7159,8 +7167,8 @@
  • * @type Object
  • */
  • schema:function () {
  • -
  • 73404 if (this.synced) {
  • -
  • 73404 return this.__schema;
  • +
  • 75103 if (this.synced) {
  • +
  • 75103 return this.__schema;
  • } else {
  • 0 throw new ModelError("Model has not been synced yet");
  • }
  • @@ -7353,9 +7361,9 @@
    - Coverage89.05 - SLOC308 - LOC137 + Coverage89.13 + SLOC309 + LOC138 Missed15
    @@ -7418,7 +7426,8 @@
  • 0 ds = ds.apply(parent, [parent]);
  • }
  • 326 if (!ds) {
  • -
  • 326 ds = this.model.dataset.naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {
  • +
  • 326 ds = this.model.dataset;
  • +
  • 326 ds = ds.select(ds.firstSourceAlias.all()).naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {
  • 326 return sql.stringToIdentifier(k);
  • })).concat(zip(keys[0], this.parentPrimaryKey.map(function (k) {
  • 326 return parent[k];
  • @@ -9060,21 +9069,21 @@
  • 1var virtualRow = function (name) {
  • -
  • 1069 var WILDCARD = new LiteralString('*');
  • -
  • 1069 var QUESTION_MARK = new LiteralString('?');
  • -
  • 1069 var COMMA_SEPARATOR = new LiteralString(', ');
  • -
  • 1069 var DOUBLE_UNDERSCORE = '__';
  • -
  • -
  • 1069 var parts = name.split(DOUBLE_UNDERSCORE);
  • -
  • 1069 var table = parts[0], column = parts[1];
  • -
  • 1069 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • -
  • 1069 var prox = methodMissing(ident, function (m) {
  • +
  • 874 var WILDCARD = new LiteralString('*');
  • +
  • 874 var QUESTION_MARK = new LiteralString('?');
  • +
  • 874 var COMMA_SEPARATOR = new LiteralString(', ');
  • +
  • 874 var DOUBLE_UNDERSCORE = '__';
  • +
  • +
  • 874 var parts = name.split(DOUBLE_UNDERSCORE);
  • +
  • 874 var table = parts[0], column = parts[1];
  • +
  • 874 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • +
  • 874 var prox = methodMissing(ident, function (m) {
  • 4 return function () {
  • 3 var args = argsToArray(arguments);
  • 3 return SQLFunction.fromArgs([m, name].concat(args));
  • }
  • }, column ? QualifiedIdentifier : Identifier);
  • -
  • 1069 var ret = createFunctionWrapper(prox, function (m) {
  • +
  • 874 var ret = createFunctionWrapper(prox, function (m) {
  • 548 var args = argsToArray(arguments);
  • 548 if (args.length) {
  • 542 return SQLFunction.fromArgs([name].concat(args));
  • @@ -9084,8 +9093,8 @@
  • }, function () {
  • 0 return SQLFunction.fromArgs(arguments);
  • });
  • -
  • 1069 ret.__proto__ = ident;
  • -
  • 1069 return ret;
  • +
  • 874 ret.__proto__ = ident;
  • +
  • 874 return ret;
  • };
  • 1var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
  • @@ -9537,7 +9546,7 @@
  • });
  • 1exports.sql = methodMissing(sql, function (name) {
  • -
  • 1069 return virtualRow(name);
  • +
  • 874 return virtualRow(name);
  • });
  • 1var OPERTATOR_INVERSIONS = {
  • @@ -10189,7 +10198,7 @@
  • * @return {patio.sql.ColumnAll}
  • */
  • all:function () {
  • -
  • 3 return new ColumnAll(this);
  • +
  • 329 return new ColumnAll(this);
  • }
  • @@ -10347,13 +10356,13 @@
  • * @return {patio.sql.Expression} an expression.
  • */
  • fromArgs:function (args) {
  • -
  • 2410 var ret;
  • -
  • 2410 try {
  • -
  • 2410 ret = new this();
  • +
  • 2215 var ret;
  • +
  • 2215 try {
  • +
  • 2215 ret = new this();
  • } catch (ignore) {
  • }
  • -
  • 2410 this.apply(ret, args);
  • -
  • 2410 return ret;
  • +
  • 2215 this.apply(ret, args);
  • +
  • 2215 return ret;
  • },
  • /**
  • @@ -10545,7 +10554,7 @@
  • * @property table the table this all column expression represents.
  • */
  • constructor:function (table) {
  • -
  • 20 this.table = table;
  • +
  • 346 this.table = table;
  • },
  • /**
  • @@ -10557,9 +10566,9 @@
  • * @return String the SQL columnAll expression fragment.
  • */
  • toString:function (ds) {
  • -
  • 19 !Dataset && (Dataset = require("./dataset"));
  • -
  • 19 ds = ds || new Dataset();
  • -
  • 19 return ds.columnAllSql(this);
  • +
  • 345 !Dataset && (Dataset = require("./dataset"));
  • +
  • 345 ds = ds || new Dataset();
  • +
  • 345 return ds.columnAllSql(this);
  • }
  • }
  • }).as(sql, "ColumnAll");
  • @@ -11156,7 +11165,7 @@
  • * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
  • */
  • constructor:function (value) {
  • -
  • 16802 this.__value = value;
  • +
  • 16412 this.__value = value;
  • },
  • /**
  • @@ -11176,7 +11185,7 @@
  • /**@ignore*/
  • getters:{
  • value:function () {
  • -
  • 25154 return this.__value;
  • +
  • 25480 return this.__value;
  • }
  • }
  • }
  • @@ -11733,7 +11742,7 @@
  • 1var addStringMethod = function (op) {
  • 24 return function () {
  • -
  • 4294 return this.__str[op].apply(this.__str, arguments);
  • +
  • 3968 return this.__str[op].apply(this.__str, arguments);
  • }
  • };
  • @@ -11756,7 +11765,7 @@
  • * @param {String} str the literal string.
  • */
  • constructor:function (str) {
  • -
  • 3677 this.__str = str;
  • +
  • 3092 this.__str = str;
  • }
  • }
  • }).as(sql, "LiteralString");
  • @@ -12660,14 +12669,14 @@
  • * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
  • */
  • constructor:function (db, opts) {
  • -
  • 29216 this._super(arguments);
  • -
  • 29216 this.db = db;
  • -
  • 29216 this.__opts = {};
  • -
  • 29216 this.__rowCb = null;
  • -
  • 29216 if (db) {
  • -
  • 15039 this.__quoteIdentifiers = db.quoteIdentifiers;
  • -
  • 15039 this.__identifierInputMethod = db.identifierInputMethod;
  • -
  • 15039 this.__identifierOutputMethod = db.identifierOutputMethod;
  • +
  • 29542 this._super(arguments);
  • +
  • 29542 this.db = db;
  • +
  • 29542 this.__opts = {};
  • +
  • 29542 this.__rowCb = null;
  • +
  • 29542 if (db) {
  • +
  • 15365 this.__quoteIdentifiers = db.quoteIdentifiers;
  • +
  • 15365 this.__identifierInputMethod = db.identifierInputMethod;
  • +
  • 15365 this.__identifierOutputMethod = db.identifierOutputMethod;
  • }
  • },
  • @@ -12682,22 +12691,22 @@
  • * @return [patio.Dataset] a cloned dataset with the merged options
  • **/
  • mergeOptions:function (opts) {
  • -
  • 14948 opts = isUndefined(opts) ? {} : opts;
  • -
  • 14948 var ds = new this._static(this.db, {});
  • -
  • 14948 ds.rowCb = this.rowCb;
  • -
  • 14948 this._static.FEATURES.forEach(function (f) {
  • -
  • 209272 ds[f] = this[f];
  • +
  • 15274 opts = isUndefined(opts) ? {} : opts;
  • +
  • 15274 var ds = new this._static(this.db, {});
  • +
  • 15274 ds.rowCb = this.rowCb;
  • +
  • 15274 this._static.FEATURES.forEach(function (f) {
  • +
  • 213836 ds[f] = this[f];
  • }, this);
  • -
  • 14948 ds.__opts = merge({}, this.__opts, opts);
  • -
  • 14948 ds.identifierInputMethod = this.identifierInputMethod;
  • -
  • 14948 ds.identifierOutputMethod = this.identifierOutputMethod;
  • -
  • 14948 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
  • -
  • 14948 if (Object.keys(opts).some(function (o) {
  • -
  • 13536 return columnChangeOpts.indexOf(o) != -1;
  • +
  • 15274 ds.__opts = merge({}, this.__opts, opts);
  • +
  • 15274 ds.identifierInputMethod = this.identifierInputMethod;
  • +
  • 15274 ds.identifierOutputMethod = this.identifierOutputMethod;
  • +
  • 15274 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
  • +
  • 15274 if (Object.keys(opts).some(function (o) {
  • +
  • 13862 return columnChangeOpts.indexOf(o) != -1;
  • })) {
  • -
  • 2456 ds.__opts.columns = null;
  • +
  • 2782 ds.__opts.columns = null;
  • }
  • -
  • 14948 return ds;
  • +
  • 15274 return ds;
  • },
  • @@ -12784,31 +12793,31 @@
  • getters:{
  • rowCb:function () {
  • -
  • 23052 return this.__rowCb;
  • +
  • 23378 return this.__rowCb;
  • },
  • identifierInputMethod:function () {
  • -
  • 14948 return this.__identifierInputMethod;
  • +
  • 15274 return this.__identifierInputMethod;
  • },
  • identifierOutputMethod:function () {
  • -
  • 14948 return this.__identifierOutputMethod;
  • +
  • 15274 return this.__identifierOutputMethod;
  • },
  • firstSourceAlias:function () {
  • -
  • 579 var source = this.__opts.from;
  • -
  • 579 if (isUndefinedOrNull(source) || !source.length) {
  • +
  • 905 var source = this.__opts.from;
  • +
  • 905 if (isUndefinedOrNull(source) || !source.length) {
  • 2 throw new DatasetError("No source specified for the query");
  • }
  • -
  • 577 source = source[0];
  • -
  • 577 if (isInstanceOf(source, AliasedExpression)) {
  • +
  • 903 source = source[0];
  • +
  • 903 if (isInstanceOf(source, AliasedExpression)) {
  • 20 return source.alias;
  • -
  • 557 } else if (isString(source)) {
  • +
  • 883 } else if (isString(source)) {
  • 0 var parts = this._splitString(source);
  • 0 var alias = parts[2];
  • 0 return alias ? alias : source;
  • } else {
  • -
  • 557 return source;
  • +
  • 883 return source;
  • }
  • },
  • @@ -12836,16 +12845,16 @@
  • /**@lends patio.Dataset.prototype*/
  • identifierInputMethod:function (meth) {
  • -
  • 15038 this.__identifierInputMethod = meth;
  • +
  • 15364 this.__identifierInputMethod = meth;
  • },
  • identifierOutputMethod:function (meth) {
  • -
  • 15038 this.__identifierOutputMethod = meth;
  • +
  • 15364 this.__identifierOutputMethod = meth;
  • },
  • rowCb:function (cb) {
  • -
  • 18564 if (isFunction(cb) || isNull(cb)) {
  • -
  • 18559 this.__rowCb = cb;
  • +
  • 18890 if (isFunction(cb) || isNull(cb)) {
  • +
  • 18885 this.__rowCb = cb;
  • } else {
  • 5 throw new DatasetError("rowCb mus be a function");
  • }
  • @@ -13976,9 +13985,9 @@
  • constructor:function () {
  • //We initialize these here because otherwise
  • //the will be blank because of recursive dependencies.
  • -
  • 29216 !patio && (patio = require("../index"));
  • -
  • 29216 !Dataset && (Dataset = patio.Dataset);
  • -
  • 29216 this._super(arguments);
  • +
  • 29542 !patio && (patio = require("../index"));
  • +
  • 29542 !Dataset && (Dataset = patio.Dataset);
  • +
  • 29542 this._super(arguments);
  • },
  • /**
  • @@ -14263,15 +14272,15 @@
  • * @return {String} a literal representation of the value.
  • */
  • literal:function (v) {
  • -
  • 44422 if (isInstanceOf(v, LiteralString)) {
  • +
  • 44748 if (isInstanceOf(v, LiteralString)) {
  • 204 return "" + v;
  • -
  • 44218 } else if (isString(v)) {
  • +
  • 44544 } else if (isString(v)) {
  • 6110 return this._literalString(v);
  • -
  • 38108 } else if (isNumber(v)) {
  • +
  • 38434 } else if (isNumber(v)) {
  • 6997 return this._literalNumber(v);
  • }
  • -
  • 31111 else if (isInstanceOf(v, Expression)) {
  • -
  • 28577 return this._literalExpression(v);
  • +
  • 31437 else if (isInstanceOf(v, Expression)) {
  • +
  • 28903 return this._literalExpression(v);
  • }
  • 2534 else if (isInstanceOf(v, Dataset)) {
  • 104 return this._literalDataset(v);
  • @@ -14583,7 +14592,7 @@
  • * SQL fragment for specifying all columns in a given table
  • **/
  • columnAllSql:function (ca) {
  • -
  • 19 return string.format("%s.*", this.quoteSchemaTable(ca.table));
  • +
  • 345 return string.format("%s.*", this.quoteSchemaTable(ca.table));
  • },
  • /**
  • @@ -14789,18 +14798,18 @@
  • * quote the name with {@link patio.dataset._Sql#_quotedIdentifier}.
  • */
  • quoteIdentifier:function (name) {
  • -
  • 33767 if (isInstanceOf(name, LiteralString)) {
  • +
  • 34093 if (isInstanceOf(name, LiteralString)) {
  • 260 return name;
  • } else {
  • -
  • 33507 if (isInstanceOf(name, Identifier)) {
  • +
  • 33833 if (isInstanceOf(name, Identifier)) {
  • 22237 name = name.value;
  • }
  • -
  • 33507 name = this.inputIdentifier(name);
  • -
  • 33507 if (this.quoteIdentifiers) {
  • -
  • 26311 name = this._quotedIdentifier(name)
  • +
  • 33833 name = this.inputIdentifier(name);
  • +
  • 33833 if (this.quoteIdentifiers) {
  • +
  • 26623 name = this._quotedIdentifier(name)
  • }
  • }
  • -
  • 33507 return name;
  • +
  • 33833 return name;
  • },
  • /**
  • @@ -14810,9 +14819,9 @@
  • * identifierOutputMethod.
  • */
  • inputIdentifier:function (v) {
  • -
  • 33693 var i = this.__identifierInputMethod;
  • -
  • 33693 v = v.toString(this);
  • -
  • 33693 return !isUndefinedOrNull(i) ?
  • +
  • 34019 var i = this.__identifierInputMethod;
  • +
  • 34019 v = v.toString(this);
  • +
  • 34019 return !isUndefinedOrNull(i) ?
  • isFunction(v[i]) ?
  • v[i]() :
  • isFunction(comb[i]) ?
  • @@ -14828,9 +14837,9 @@
  • * identifierOutputMethod.
  • */
  • outputIdentifier:function (v) {
  • -
  • 19885 (v == '' && (v = 'untitled'));
  • -
  • 19885 var i = this.__identifierOutputMethod;
  • -
  • 19885 return !isUndefinedOrNull(i) ?
  • +
  • 19523 (v == '' && (v = 'untitled'));
  • +
  • 19523 var i = this.__identifierOutputMethod;
  • +
  • 19523 return !isUndefinedOrNull(i) ?
  • isFunction(v[i]) ?
  • v[i]() :
  • isFunction(comb[i]) ?
  • @@ -14846,10 +14855,10 @@
  • * quoted (if quoting identifiers)
  • */
  • quoteSchemaTable:function (table) {
  • -
  • 2231 var parts = this.schemaAndTable(table);
  • -
  • 2231 var schema = parts[0];
  • -
  • 2231 table = parts[1];
  • -
  • 2231 return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));
  • +
  • 2557 var parts = this.schemaAndTable(table);
  • +
  • 2557 var schema = parts[0];
  • +
  • 2557 table = parts[1];
  • +
  • 2557 return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));
  • },
  • @@ -14858,15 +14867,15 @@
  • * Split the schema information from the table
  • */
  • schemaAndTable:function (tableName) {
  • -
  • 2475 var sch = this.db ? this.db.defaultSchema || null : null;
  • -
  • 2475 if (isString(tableName)) {
  • +
  • 2801 var sch = this.db ? this.db.defaultSchema || null : null;
  • +
  • 2801 if (isString(tableName)) {
  • 1008 var parts = this._splitString(tableName);
  • 1008 var s = parts[0], table = parts[1];
  • 1008 return [s || sch, table];
  • -
  • 1467 } else if (isInstanceOf(tableName, QualifiedIdentifier)) {
  • +
  • 1793 } else if (isInstanceOf(tableName, QualifiedIdentifier)) {
  • 3 return [tableName.table, tableName.column]
  • -
  • 1464 } else if (isInstanceOf(tableName, Identifier)) {
  • -
  • 1464 return [null, tableName.value];
  • +
  • 1790 } else if (isInstanceOf(tableName, Identifier)) {
  • +
  • 1790 return [null, tableName.value];
  • } else {
  • 0 throw new QueryError("table should be a QualifiedIdentifier, Identifier, or String");
  • }
  • @@ -14915,7 +14924,7 @@
  • * expressions.
  • */
  • __expressionList:function (columns) {
  • -
  • 4284 return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);
  • +
  • 4610 return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);
  • },
  • //Format the timestamp based on the default_timestamp_format, with a couple
  • @@ -15037,7 +15046,7 @@
  • * @return SQL fragment for SQL::Expression, result depends on the specific type of expression.
  • * */
  • _literalExpression:function (v) {
  • -
  • 28587 return v.toString(this);
  • +
  • 28913 return v.toString(this);
  • },
  • /**
  • @@ -15703,7 +15712,7 @@
  • * @type String
  • */
  • identifierInputMethod:function () {
  • -
  • 15038 return this.__identifierInputMethod;
  • +
  • 15364 return this.__identifierInputMethod;
  • },
  • /**
  • @@ -15715,7 +15724,7 @@
  • * @type String
  • */
  • identifierOutputMethod:function () {
  • -
  • 15038 return this.__identifierOutputMethod;
  • +
  • 15364 return this.__identifierOutputMethod;
  • },
  • /**
  • @@ -15728,7 +15737,7 @@
  • * @default true
  • */
  • quoteIdentifiers:function () {
  • -
  • 15004 return this.__quoteIdentifiers;
  • +
  • 15330 return this.__quoteIdentifiers;
  • }
  • },
  • @@ -15871,10 +15880,10 @@
  • /**@ignore*/
  • constructor:function () {
  • -
  • 29216 if (!Dataset) {
  • +
  • 29542 if (!Dataset) {
  • 1 Dataset = require("../index").Dataset;
  • }
  • -
  • 29216 this._super(arguments);
  • +
  • 29542 this._super(arguments);
  • },
  • @@ -15904,7 +15913,7 @@
  • 2734 var a = [];
  • 2734 var ret = new Promise().classic(cb);
  • 2734 this.forEach(hitch(this, function (r) {
  • -
  • 2936 a.push(r);
  • +
  • 2934 a.push(r);
  • })).then(hitch(this, function () {
  • 2731 this.postLoad(a);
  • 2731 if (block) {
  • @@ -17150,10 +17159,10 @@
  • *
  • */
  • constructor:function () {
  • -
  • 3218 if (comb.isUndefinedOrNull(this.__associations)) {
  • -
  • 3055 this.__associations = {};
  • +
  • 3111 if (comb.isUndefinedOrNull(this.__associations)) {
  • +
  • 3002 this.__associations = {};
  • }
  • -
  • 3218 this._super(arguments);
  • +
  • 3111 this._super(arguments);
  • },
  • reload:function () {
  • @@ -18392,17 +18401,17 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 29216 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 29216 this._super(arguments);
  • -
  • 29216 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
  • -
  • 204512 if (!this[type + "Join"]) {
  • -
  • 204512 this[type + "Join"] = conditionedJoin.bind(this, type);
  • +
  • 29542 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 29542 this._super(arguments);
  • +
  • 29542 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
  • +
  • 206794 if (!this[type + "Join"]) {
  • +
  • 206794 this[type + "Join"] = conditionedJoin.bind(this, type);
  • }
  • }, this);
  • -
  • 29216 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
  • -
  • 146080 if (!this[type + "Join"]) {
  • -
  • 146080 this[type + "Join"] = unConditionJoin.bind(this, type);
  • +
  • 29542 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
  • +
  • 147710 if (!this[type + "Join"]) {
  • +
  • 147710 this[type + "Join"] = unConditionJoin.bind(this, type);
  • }
  • }, this);
  • @@ -19814,29 +19823,29 @@
  • * @return {patio.Dataset} a cloned dataset with the columns selected changed.
  • */
  • select:function (args) {
  • -
  • 535 args = flatten(argsToArray(arguments));
  • -
  • 535 var columns = [];
  • -
  • 535 args.forEach(function (c) {
  • -
  • 955 if (isFunction(c)) {
  • +
  • 861 args = flatten(argsToArray(arguments));
  • +
  • 861 var columns = [];
  • +
  • 861 args.forEach(function (c) {
  • +
  • 1281 if (isFunction(c)) {
  • 23 var res = c.apply(sql, [sql]);
  • 23 columns = columns.concat(isArray(res) ? res : [res]);
  • } else {
  • -
  • 932 columns.push(c);
  • +
  • 1258 columns.push(c);
  • }
  • });
  • -
  • 535 var select = [];
  • -
  • 535 columns.forEach(function (c) {
  • -
  • 958 if (isHash(c)) {
  • +
  • 861 var select = [];
  • +
  • 861 columns.forEach(function (c) {
  • +
  • 1284 if (isHash(c)) {
  • 3 for (var i in c) {
  • 4 select.push(new AliasedExpression(new Identifier(i), c[i]));
  • }
  • -
  • 955 } else if (isString(c)) {
  • +
  • 1281 } else if (isString(c)) {
  • 406 select.push(this.stringToIdentifier(c));
  • } else {
  • -
  • 549 select.push(c);
  • +
  • 875 select.push(c);
  • }
  • }, this);
  • -
  • 535 return this.mergeOptions({select:select});
  • +
  • 861 return this.mergeOptions({select:select});
  • },
  • @@ -21117,7 +21126,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(){
  • -
  • 48455 return this.__quoteIdentifiers;
  • +
  • 49107 return this.__quoteIdentifiers;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21125,20 +21134,20 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(){
  • -
  • 14948 return this.__providesAccurateRowsMatched;
  • +
  • 15274 return this.__providesAccurateRowsMatched;
  • },
  • //Whether the dataset requires SQL standard datetimes (false by default,
  • // as most allow strings with ISO 8601 format).
  • /**@ignore*/
  • requiresSqlStandardDateTimes:function(){
  • -
  • 14955 return this.__requiresSqlStandardDateTimes;
  • +
  • 15281 return this.__requiresSqlStandardDateTimes;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(){
  • -
  • 14961 return this.__supportsCte;
  • +
  • 15287 return this.__supportsCte;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • @@ -21150,25 +21159,25 @@
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(){
  • -
  • 14984 return this.__supportsIntersectExcept;
  • +
  • 15310 return this.__supportsIntersectExcept;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(){
  • -
  • 14960 return this.__supportsIntersectExceptAll;
  • +
  • 15286 return this.__supportsIntersectExceptAll;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(){
  • -
  • 15217 return this.__supportsIsTrue;
  • +
  • 15543 return this.__supportsIsTrue;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(){
  • -
  • 14959 return this.__supportsJoinUsing;
  • +
  • 15285 return this.__supportsJoinUsing;
  • },
  • //Whether modifying joined datasets is supported.
  • @@ -21180,7 +21189,7 @@
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(){
  • -
  • 14952 return this.__supportsMultipleColumnIn;
  • +
  • 15278 return this.__supportsMultipleColumnIn;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • @@ -21192,13 +21201,13 @@
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(){
  • -
  • 14948 return this.__supportsTimestampUsecs;
  • +
  • 15274 return this.__supportsTimestampUsecs;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(){
  • -
  • 14948 return this.__supportsWindowFunctions;
  • +
  • 15274 return this.__supportsWindowFunctions;
  • }
  • },
  • @@ -21209,7 +21218,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(val){
  • -
  • 14960 this.__quoteIdentifiers = val;
  • +
  • 15286 this.__quoteIdentifiers = val;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21217,20 +21226,20 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(val){
  • -
  • 14948 this.__providesAccurateRowsMatched = val;
  • +
  • 15274 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){
  • -
  • 14948 this.__requiresSqlStandardDateTimes = val;
  • +
  • 15274 this.__requiresSqlStandardDateTimes = val;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(val){
  • -
  • 14949 this.__supportsCte = val;
  • +
  • 15275 this.__supportsCte = val;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • @@ -21242,25 +21251,25 @@
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(val){
  • -
  • 14950 this.__supportsIntersectExcept = val;
  • +
  • 15276 this.__supportsIntersectExcept = val;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(val){
  • -
  • 14950 this.__supportsIntersectExceptAll = val;
  • +
  • 15276 this.__supportsIntersectExceptAll = val;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(val){
  • -
  • 14948 this.__supportsIsTrue = val;
  • +
  • 15274 this.__supportsIsTrue = val;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(val){
  • -
  • 14950 this.__supportsJoinUsing = val;
  • +
  • 15276 this.__supportsJoinUsing = val;
  • },
  • //Whether modifying joined datasets is supported.
  • @@ -21272,7 +21281,7 @@
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(val){
  • -
  • 14948 this.__supportsMultipleColumnIn = val;
  • +
  • 15274 this.__supportsMultipleColumnIn = val;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • @@ -21284,13 +21293,13 @@
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(val){
  • -
  • 14948 this.__supportsTimestampUsecs = val;
  • +
  • 15274 this.__supportsTimestampUsecs = val;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(val){
  • -
  • 14948 this.__supportsWindowFunctions = val;
  • +
  • 15274 this.__supportsWindowFunctions = val;
  • }
  • }
  • @@ -21384,8 +21393,8 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 29216 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 29216 this._super(arguments);
  • +
  • 29542 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 29542 this._super(arguments);
  • },
  • /**
  • diff --git a/docs/patio.html b/docs/patio.html index cb1c74bb..9625917a 100644 --- a/docs/patio.html +++ b/docs/patio.html @@ -959,8 +959,10 @@

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

    Actions

    }firstSourceAliasString

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

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

    firstSourceTableString

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

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

    +hasSelectSourceBoolean

    true if this dataset already has a select sources.

    identifierInputMethodString

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

    identifierOutputMethodString

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

    isSimpleSelectAllBoolean

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

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

    +joinSourceListpatio.sql.Identifier[][]

    a list of join sources

    providesAccurateRowsMatchedbooleantrue

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

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

    Actions

    requiresSqlStandardDatebooleanfalse

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

    rowCbFunction

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

    +sourceListpatio.sql.Identifier[][]

    a list of sources for this dataset.

    supportsCtebooleantrue

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

    supportsDistinctOnbooleanfalse

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

    supportsIntersectExceptbooleantrue

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    + +

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

    + +
    +Defined dataset/query.js + +

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

    +

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

      columns to select if there is not already select sources.

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

      a cloned dataset with the appropriate select sources.

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

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

    Filtering With Objects

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

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

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

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

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

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

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

    -

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

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

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

    Array of Two Element Arrays

    +

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

    -

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

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

    Filter Blocks

    +

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

    -

    // SELECT * FROM user WHERE id > 5 +

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

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

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

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

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

    Strings

    +

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

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

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

    -

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

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

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

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

    Expressions

    +

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

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

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

    -

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

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

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

    -

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

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

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

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

    You can combine these expression operators with functions: + +

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

    -

    Strings with Placeholders

    -

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

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

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

    +

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

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

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

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

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

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

    Literal Strings

    -

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

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

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

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

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

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

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

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

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

    +

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

    -

    Inverting filters

    -

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

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

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

    -

    NOTE: the invert method inverts the entire filter! +

    +

    //OR

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

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

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

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

    -

    Excluding filters

    -

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

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

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

    +

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

    -

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

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

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

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

    Removing

    -

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

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

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

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

    Ordering

    -

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

    Or to use the NOT LIKE operator:
    +

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

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

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

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

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

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

    Note: order replaces any existing order +

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

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

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

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

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

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

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

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

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

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

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

    Reversing

    -

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

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

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

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

    You can also use the desc method. - +

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

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

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

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

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

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

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

    Removing

    -

    To remove ordering use the unordered method: - - +

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

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

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

    Selecting columns

    -

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

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

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

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

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

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

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

    -

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

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

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

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

    select replaces any columns previously selected. +

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

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

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

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

    
    +select replaces any columns previously selected.
    +

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

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

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

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

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

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

    Distinct

    -

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

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

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

    -

    Note: DISTINCT is separate from the select clause, +

    
    +##Distinct
     
    -

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

    Limit and Offset

    -

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

    +

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

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

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

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

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

    -

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

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

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

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

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

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

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

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

    Grouping

    -

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

    
     
    -

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

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

    +

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

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

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

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

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

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

    Having

    -

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

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

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

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

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

    
    +##Having
     
    -

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

    Where

    -

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

    +

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

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

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

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

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

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

    Joins

    -

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

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

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

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

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

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

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

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

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

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

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

    -

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

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

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

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

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

    -

    Table/Dataset to Join

    -

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

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

    Join Conditions

    -

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

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

    +
    
     
    +###Table/Dataset to Join
     
    -

    -

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

    +

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

    -

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

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

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

    -

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

    
    +###Join Conditions
     
    -

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

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

    +

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

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

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

    -

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

    -

    Implicit qualification is not always correct: - +

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

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

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

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

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

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

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

    +

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

    +

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

    -

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

    +

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

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

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

    -

    USING Joins

    -

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

    +

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

    -

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

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

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

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

    NATURAL Joins

    -

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

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

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

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

    Join Blocks

    -

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

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

    -

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

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

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

    -

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

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

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

    -

    From

    -

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

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

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

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

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

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

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

    +

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

    -

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

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

    -

    Subselects

    -

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

    +

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

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

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

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

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

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

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

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

    -

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

    -

    Locking for Update

    -

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

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

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

    Locking for Update

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

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

    -

    Custom SQL

    -

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

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

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

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

    You can also use the withSql dataset method. +

    
    +You can also use the withSql dataset method.
    +

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

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

    You can also use placeholders: +

    
    +You can also use placeholders:
    +

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

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

    Checking for Records

    -

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

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

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

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

    Checking for Records

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

    Aggregate Calculations

    -

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    + }); +```


    diff --git a/lib/dataset/index.js b/lib/dataset/index.js index c979f199..27e586e4 100755 --- a/lib/dataset/index.js +++ b/lib/dataset/index.js @@ -234,6 +234,9 @@ define([actions, graph, features, query, sql], { * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions. + * @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset. + * @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources + * @property {Boolean} hasSelectSource true if this dataset already has a select sources. */ constructor:function (db, opts) { this._super(arguments); @@ -402,6 +405,21 @@ define([actions, graph, features, query, sql], { } else { return source; } + }, + + sourceList:function () { + return (this.__opts.from || []).map(this.stringToIdentifier, this); + }, + + joinSourceList:function () { + return (this.__opts.join || []).map(function (join) { + return this.stringToIdentifier(join.tableAlias || join.table); + }, this); + }, + + hasSelectSource:function () { + var select = this.__opts.select; + return !(isUndefinedOrNull(select) || select.length === 0); } }, diff --git a/lib/dataset/query.js b/lib/dataset/query.js index 897511e4..e0bc583e 100755 --- a/lib/dataset/query.js +++ b/lib/dataset/query.js @@ -1515,6 +1515,30 @@ define(null, { return this.mergeOptions({select:null}); }, + /** + * Selects the columns if only if there is not already select sources. + * + * @example + * + * var ds = DB.from("items"); //SELECT * FROM items + * + * ds.select("a"); //SELECT a FROM items; + * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items; + * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items; + * + * @param cols columns to select if there is not already select sources. + * @return {patio.Dataset} a cloned dataset with the appropriate select sources. + */ + selectIfNoSource:function (cols) { + var ret; + if (!this.hasSelectSource) { + ret = this.select.apply(this, arguments); + } else { + ret = this.mergeOptions(); + } + return ret; + }, + /** * Returns a copy of the dataset with the given columns added * to the existing selected columns. If no columns are currently selected, diff --git a/lib/dataset/sql.js b/lib/dataset/sql.js index bb627e9f..6fbcdaf1 100755 --- a/lib/dataset/sql.js +++ b/lib/dataset/sql.js @@ -1,42 +1,43 @@ var comb = require("comb"), - define = comb.define, - array = comb.array, - intersect = array.intersect, - compact = array.compact, - string = comb.string, - format = string.format, - argsToArray = comb.argsToArray, - isInstanceOf = comb.isInstanceOf, - isArray = comb.isArray, - isNumber = comb.isNumber, - isDate = comb.isDate, - isNull = comb.isNull, - isBoolean = comb.isBoolean, - isFunction = comb.isFunction, - isUndefined = comb.isUndefined, - isObject = comb.isObject, - isHash = comb.isHash, - merge = comb.merge, - isUndefinedOrNull = comb.isUndefinedOrNull, - isString = comb.isString, - sql = require("../sql").sql, - Expression = sql.Expression, - ComplexExpression = sql.ComplexExpression, - AliasedExpression = sql.AliasedExpression, - Identifier = sql.Identifier, - QualifiedIdentifier = sql.QualifiedIdentifier, - OrderedExpression = sql.OrderedExpression, - CaseExpression = sql.CaseExpression, - SubScript = sql.SubScript, - NumericExpression = sql.NumericExpression, - ColumnAll = sql.ColumnAll, - Cast = sql.Cast, - StringExpression = sql.StringExpression, - BooleanExpression = sql.BooleanExpression, - SQLFunction = sql.SQLFunction, - LiteralString = sql.LiteralString, - PlaceHolderLiteralString = sql.PlaceHolderLiteralString, - QueryError = require("../errors").QueryError, patio; + define = comb.define, + array = comb.array, + intersect = array.intersect, + compact = array.compact, + string = comb.string, + format = string.format, + argsToArray = comb.argsToArray, + isInstanceOf = comb.isInstanceOf, + isArray = comb.isArray, + isNumber = comb.isNumber, + isDate = comb.isDate, + isNull = comb.isNull, + isBoolean = comb.isBoolean, + isFunction = comb.isFunction, + isUndefined = comb.isUndefined, + isObject = comb.isObject, + isHash = comb.isHash, + isEmpty = comb.isEmpty, + merge = comb.merge, + isUndefinedOrNull = comb.isUndefinedOrNull, + isString = comb.isString, + sql = require("../sql").sql, + Expression = sql.Expression, + ComplexExpression = sql.ComplexExpression, + AliasedExpression = sql.AliasedExpression, + Identifier = sql.Identifier, + QualifiedIdentifier = sql.QualifiedIdentifier, + OrderedExpression = sql.OrderedExpression, + CaseExpression = sql.CaseExpression, + SubScript = sql.SubScript, + NumericExpression = sql.NumericExpression, + ColumnAll = sql.ColumnAll, + Cast = sql.Cast, + StringExpression = sql.StringExpression, + BooleanExpression = sql.BooleanExpression, + SQLFunction = sql.SQLFunction, + LiteralString = sql.LiteralString, + PlaceHolderLiteralString = sql.PlaceHolderLiteralString, + QueryError = require("../errors").QueryError, patio; var Dataset; @@ -447,7 +448,7 @@ define(null, { return new QualifiedIdentifier(table, e); } else if (isInstanceOf(e, OrderedExpression)) { return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending, - {nulls:e.nulls}); + {nulls:e.nulls}); } else if (isInstanceOf(e, AliasedExpression)) { return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias); } else if (isInstanceOf(e, CaseExpression)) { @@ -615,9 +616,9 @@ define(null, { var columns = this.__opts.columns, ret = ""; if (columns && columns.length) { ret = " (" + columns.map( - function (c) { - return c.toString(this); - }, this).join(this._static.COMMA_SEPARATOR) + ")"; + function (c) { + return c.toString(this); + }, this).join(this._static.COMMA_SEPARATOR) + ")"; } return ret; }, @@ -729,8 +730,8 @@ define(null, { return this.complexExpressionSql("EQ", args); } else { return this.complexExpressionSql("OR", - [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0], - null)]); + [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0], + null)]); } } else if (["IN", "NOTIN"].indexOf(op) != -1) { @@ -761,8 +762,8 @@ define(null, { //literal so that if values is an array of two element arrays, it //will be treated as a value list instead of a condition specifier. return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols), - ComplexExpression.IN_OPERATORS[op], - valArray ? this._arraySql(vals) : this.literal(vals)); + ComplexExpression.IN_OPERATORS[op], + valArray ? this._arraySql(vals) : this.literal(vals)); } } else { @@ -776,13 +777,13 @@ define(null, { } } else { return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols), - ComplexExpression.IN_OPERATORS[op], this.literal(vals)); + ComplexExpression.IN_OPERATORS[op], this.literal(vals)); } } } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) { var l = args[0]; return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp, - this.literal(args[1])); + this.literal(args[1])); } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) { return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " ")); } else if (op == "NOT") { @@ -825,7 +826,7 @@ define(null, { } var tref = this.__tableRef(table); return string.format(" %s %s", this._joinTypeSql(jc.joinType), - tableAlias ? this.__asSql(tref, tableAlias) : tref); + tableAlias ? this.__asSql(tref, tableAlias) : tref); }, /** @@ -898,11 +899,11 @@ define(null, { */ qualifiedIdentifierSql:function (qcr) { return [qcr.table, qcr.column].map( - function (x) { - return [QualifiedIdentifier, Identifier, String].some(function (c) { - return x instanceof c - }) ? this.literal(x) : this.quoteIdentifier(x) - }, this).join('.'); + function (x) { + return [QualifiedIdentifier, Identifier, String].some(function (c) { + return x instanceof c + }) ? this.literal(x) : this.quoteIdentifier(x) + }, this).join('.'); }, /** @@ -937,12 +938,12 @@ define(null, { var i = this.__identifierInputMethod; v = v.toString(this); return !isUndefinedOrNull(i) ? - isFunction(v[i]) ? - v[i]() : - isFunction(comb[i]) ? - comb[i](v) - : v - : v; + isFunction(v[i]) ? + v[i]() : + isFunction(comb[i]) ? + comb[i](v) + : v + : v; }, /** @@ -955,12 +956,12 @@ define(null, { (v == '' && (v = 'untitled')); var i = this.__identifierOutputMethod; return !isUndefinedOrNull(i) ? - isFunction(v[i]) ? - v[i]() : - isFunction(comb[i]) ? - comb[i](v) - : v - : v; + isFunction(v[i]) ? + v[i]() : + isFunction(comb[i]) ? + comb[i](v) + : v + : v; }, /** @@ -1056,9 +1057,9 @@ define(null, { */ _joinTypeSql:function (joinType) { return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g, - function (m) { - return m.toUpperCase() + " "; - } + function (m) { + return m.toUpperCase() + " "; + } ).trimRight() + " JOIN"; }, @@ -1184,10 +1185,10 @@ define(null, { var table = parts[0], column = parts[1], alias = parts[2]; if (!alias) { return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'" - + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'" + + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'" } else { return this.literal(new AliasedExpression(column - && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias)); + && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias)); } }, @@ -1480,9 +1481,9 @@ define(null, { throw new QueryError("No source specified for the query"); } return " " + source.map( - function (s) { - return this.__tableRef(s); - }, this).join(this._static.COMMA_SEPARATOR); + function (s) { + return this.__tableRef(s); + }, this).join(this._static.COMMA_SEPARATOR); }, /** diff --git a/lib/model.js b/lib/model.js index 45f6b5fc..b32ceacd 100644 --- a/lib/model.js +++ b/lib/model.js @@ -229,10 +229,15 @@ var Model = define([QueryPlugin, Middleware], { __setFromDb:function (values, ignore) { values = values || {}; this.__ignore = ignore === true; + var schema = this.schema; Object.keys(values).forEach(function (column) { var value = values[column]; // Typecast value retrieved from db - this.__values[column] = this._typeCastValue(column, value, ignore); + if (schema.hasOwnProperty(column)) { + this.__values[column] = this._typeCastValue(column, value, ignore); + } else { + this[column] = value; + } }, this); this.__ignore = false; diff --git a/test/model.test.js b/test/model.test.js index 64f0c149..1cc8166a 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -10,7 +10,7 @@ var it = require('it'), var gender = ["M", "F"]; -it.describe("A model with properites", function (it) { +it.describe("A model with properites",function (it) { var Employee; it.beforeAll(function () { @@ -194,11 +194,13 @@ it.describe("A model with properites", function (it) { }); it.should("support the filtering of models", function (next) { - var id = sql.identifier("id"), ids = emps.map(function(emp){return emp.id;}); + var id = sql.identifier("id"), ids = emps.map(function (emp) { + return emp.id; + }); comb.executeInOrder(Employee, function (Employee) { var ret = {}; - ret.query1 = Employee.filter({id:ids.slice(0,6)}).all(); + ret.query1 = Employee.filter({id:ids.slice(0, 6)}).all(); ret.query2 = Employee.filter(id.gt(ids[0]), id.lt(ids[5])).order("id").last(); ret.query3 = Employee.filter(function () { return this.firstname.like(/first1[1|2]*$/); @@ -231,7 +233,7 @@ it.describe("A model with properites", function (it) { assert.deepEqual(query4.map(function (e) { assert.instanceOf(e, Employee); return e.id; - }), ids.slice(0,6)); + }), ids.slice(0, 6)); assert.deepEqual(query5.map(function (e) { assert.instanceOf(e, Employee); return e.id; @@ -239,7 +241,7 @@ it.describe("A model with properites", function (it) { next(); }, next); }); - + it.should("support custom query methods", function (next) { Employee.findByGender("F").then(function (emps) { emps.forEach(function (emp) { @@ -272,7 +274,9 @@ it.describe("A model with properites", function (it) { it.should("support map", function (next) { - var ids = emps.map(function(emp){return emp.id;}); + var ids = emps.map(function (emp) { + return emp.id; + }); comb.executeInOrder(Employee, function (Employee) { var ret = {}; @@ -315,7 +319,9 @@ it.describe("A model with properites", function (it) { }); it.should("support first", function (next) { - var id = sql.identifier("id"),ids = emps.map(function(emp){return emp.id;}); + var id = sql.identifier("id"), ids = emps.map(function (emp) { + return emp.id; + }); Employee.first(id.gt(ids[5]), id.lt(ids[11])).then(function (emp) { assert.instanceOf(emp, Employee); assert.equal(emp.id, ids[6]) @@ -401,7 +407,12 @@ it.describe("A model with properites", function (it) { }, next); }); + }); + it.should("allow values when initalizing that are not in the schema", function () { + var m = new Employee({otherVal:"otherVal", firstname:"dougie"}, true); + assert.equal(m.otherVal, "otherVal"); + assert.equal(m.firstname, "dougie"); }); it.afterAll(function () { @@ -409,7 +420,6 @@ it.describe("A model with properites", function (it) { }); - }).as(module); diff --git a/test/plugins/columnMapper.test.js b/test/plugins/columnMapper.test.js index f2abfa14..cb77c587 100644 --- a/test/plugins/columnMapper.test.js +++ b/test/plugins/columnMapper.test.js @@ -10,7 +10,6 @@ var it = require('it'), var gender = ["M", "F"]; - it.describe("patio.plugins.ColumnMapper", function (it) { var Works, Employee; it.beforeAll(function () { From f580dbd2148b86880ab6dc1fd3ee2c1b4bfe01c0 Mon Sep 17 00:00:00 2001 From: Doug Martin Date: Wed, 29 Aug 2012 11:43:25 -0500 Subject: [PATCH 2/3] updated coverage docs --- docs-md/coverage.html | 7535 +++++++++++++++++++++-------------------- docs/coverage.html | 7535 +++++++++++++++++++++-------------------- 2 files changed, 7576 insertions(+), 7494 deletions(-) diff --git a/docs-md/coverage.html b/docs-md/coverage.html index 70d5468b..21fb1ef8 100644 --- a/docs-md/coverage.html +++ b/docs-md/coverage.html @@ -256,10 +256,10 @@
    - Coverage89.21 - SLOC21921 - LOC5357 - Missed578 + Coverage89.04 + SLOC21962 + LOC5365 + Missed588
    @@ -2521,7 +2521,7 @@
  • },
  • _quotedIdentifier:function (c) {
  • -
  • 26239 return format('"%s"', c);
  • +
  • 25927 return format('"%s"', c);
  • },
  • __fullTextStringJoin:function (cols) {
  • @@ -2575,12 +2575,12 @@
  • 3048 var cols = [];
  • 3048 if (rows && rows.length) {
  • 2543 cols = this.__columns = fields && fields.length ? fields.map(function (f) {
  • -
  • 18560 return f.name;
  • +
  • 18922 return f.name;
  • }) : Object.keys(rows[0]);
  • //the pg driver does auto type coercion
  • 2543 cols = cols.map(function (c) {
  • -
  • 18560 return [oi(c), function (o) {
  • -
  • 27740 return o;
  • +
  • 18922 return [oi(c), function (o) {
  • +
  • 28420 return o;
  • }, c];
  • }, this);
  • }
  • @@ -2607,7 +2607,7 @@
  • 3654 var h = {};
  • 3654 var row = rows[index];
  • 3654 cols.forEach(function (col) {
  • -
  • 27740 h[col[0]] = col[1](row[col[2]]);
  • +
  • 28420 h[col[0]] = col[1](row[col[2]]);
  • }, this);
  • 3654 when(cb(h)).then(function (val) {
  • @@ -2659,15 +2659,15 @@
  • },
  • supportsDistinctOn:function () {
  • -
  • 12996 return true;
  • +
  • 12670 return true;
  • },
  • supportsModifyingJoins:function () {
  • -
  • 15440 return true;
  • +
  • 15114 return true;
  • },
  • supportsTimestampTimezones:function () {
  • -
  • 12994 return true;
  • +
  • 12668 return true;
  • }
  • }
  • @@ -5735,3296 +5735,3769 @@
  • -
    +
    -
    associations/_Association.js
    +
    dataset/index.js
    - Coverage87.18 - SLOC515 - LOC156 - Missed20 + Coverage86.11 + SLOC457 + LOC72 + Missed10
    -
    1. 1var comb = require("comb-proxy"),
    2. -
    3. define = comb.define,
    4. +
      1. 1var comb = require("comb"),
      2. +
      3. hitch = comb.hitch,
      4. +
      5. logging = comb.logging,
      6. +
      7. Logger = logging.Logger,
      8. +
      9. errors = require("../errors"),
      10. +
      11. QueryError = errors.QueryError,
      12. +
      13. DatasetError = errors.DatasetError,
      14. +
      15. Promise = comb.Promise,
      16. +
      17. PromiseList = comb.PromiseList,
      18. isUndefined = comb.isUndefined,
      19. isUndefinedOrNull = comb.isUndefinedOrNull,
      20. -
      21. isBoolean = comb.isBoolean,
      22. isString = comb.isString,
      23. -
      24. isHash = comb.isHash,
      25. -
      26. isFunction = comb.isFunction,
      27. isInstanceOf = comb.isInstanceOf,
      28. -
      29. Promise = comb.Promise,
      30. -
      31. PromiseList = comb.PromiseList,
      32. -
      33. hitch = comb.hitch,
      34. -
      35. array = comb.array,
      36. -
      37. isArray = comb.isArray,
      38. -
      39. Middleware = comb.plugins.Middleware,
      40. -
      41. PatioError = require("../errors").PatioError;
      42. -
      43. -
      44. 1var fetch = {
      45. -
      46. LAZY:"lazy",
      47. -
      48. EAGER:"eager"
      49. -
      50. };
      51. +
      52. isString = comb.isString,
      53. +
      54. isFunction = comb.isFunction,
      55. +
      56. isNull = comb.isNull,
      57. +
      58. merge = comb.merge,
      59. +
      60. define = comb.define,
      61. +
      62. graph = require("./graph"),
      63. +
      64. actions = require("./actions"),
      65. +
      66. features = require("./features"),
      67. +
      68. query = require("./query"),
      69. +
      70. sql = require("./sql"),
      71. +
      72. SQL = require("../sql").sql,
      73. +
      74. AliasedExpression = SQL.AliasedExpression,
      75. +
      76. Identifier = SQL.Identifier,
      77. +
      78. QualifiedIdentifier = SQL.QualifiedIdentifier;
      79. -
      80. /**
      81. -
      82. * @class
      83. -
      84. * Base class for all associations.
      85. -
      86. *
      87. -
      88. * </br>
      89. -
      90. * <b>NOT to be instantiated directly</b>
      91. -
      92. * Its just documented for reference.
      93. -
      94. *
      95. -
      96. * @constructs
      97. -
      98. * @param {Object} options
      99. -
      100. * @param {String} options.model a string to look up the model that we are associated with
      101. -
      102. * @param {Function} options.filter a callback to find association if a filter is defined then
      103. -
      104. * the association is read only
      105. -
      106. * @param {Object} options.key object with left key and right key
      107. -
      108. * @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
      109. -
      110. * @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
      111. -
      112. * the associations are automatically filled, if fetch.Lazy is supplied
      113. -
      114. * then a promise is returned and is called back with the loaded models
      115. -
      116. * @property {Model} model the model associatied with this association.
      117. -
      118. * @name Association
      119. -
      120. * @memberOf patio.associations
      121. -
      122. * */
      123. -
      124. 1define(Middleware, {
      125. +
      126. 1var LOGGER = comb.logger("patio.Dataset");
      127. +
      128. 1define([actions, graph, features, query, sql], {
      129. instance:{
      130. -
      131. /**@lends patio.associations.Association.prototype*/
      132. -
      133. -
      134. type:"",
      135. -
      136. -
      137. //Our associated model
      138. -
      139. _model:null,
      140. -
      141. -
      142. /**
      143. -
      144. * Fetch type
      145. -
      146. */
      147. -
      148. fetchType:fetch.LAZY,
      149. -
      150. -
      151. /**how to order our association*/
      152. -
      153. orderBy:null,
      154. -
      155. -
      156. /**Our filter method*/
      157. -
      158. filter:null,
      159. -
      160. -
      161. __hooks:null,
      162. -
      163. -
      164. isOwner:true,
      165. -
      166. -
      167. createSetter:true,
      168. -
      169. isCascading:false,
      170. -
      171. -
      172. supportsStringKey:true,
      173. -
      174. -
      175. supportsHashKey:true,
      176. -
      177. -
      178. supportsCompositeKey:true,
      179. -
      180. -
      181. supportsLeftAndRightKey:true,
      182. +
      183. /**@lends patio.Dataset.prototype*/
      184. /**
      185. +
      186. * Class that is used for querying/retirving datasets from a database.
      187. *
      188. -
      189. *Method to call to look up association,
      190. -
      191. *called after the model has been filtered
      192. -
      193. **/
      194. -
      195. _fetchMethod:"all",
      196. -
      197. -
      198. -
      199. constructor:function (options, patio, filter) {
      200. -
      201. 55 options = options || {};
      202. -
      203. 55 if (!options.model) {
      204. -
      205. 0 throw new Error("Model is required for " + this.type + " association");
      206. -
      207. }
      208. -
      209. 55 this._model = options.model;
      210. -
      211. 55 this.patio = patio;
      212. -
      213. 55 this.__opts = options;
      214. -
      215. 55 !isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
      216. -
      217. 55 this.filter = filter;
      218. -
      219. 55 this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
      220. -
      221. 55 this.__hooks =
      222. -
      223. {before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
      224. -
      225. 55 var hooks = ["Add", "Remove", "Set", "Load"];
      226. -
      227. 55 ["before", "after"].forEach(function (h) {
      228. -
      229. 110 hooks.forEach(function (a) {
      230. -
      231. 440 var hookName = h + a, hook;
      232. -
      233. 440 if (isFunction((hook = options[hookName]))) {
      234. -
      235. 0 this.__hooks[h][a.toLowerCase()] = hook;
      236. -
      237. }
      238. -
      239. }, this);
      240. -
      241. }, this);
      242. -
      243. 55 this.fetchType = options.fetchType || fetch.LAZY;
      244. -
      245. },
      246. -
      247. -
      248. _callHook:function (hook, action, args) {
      249. -
      250. 0 var func = this.__hooks[hook][action], ret;
      251. -
      252. 0 if (isFunction(func)) {
      253. -
      254. 0 ret = func.apply(this, args);
      255. -
      256. }
      257. -
      258. 0 return ret;
      259. -
      260. },
      261. -
      262. -
      263. _clearAssociations:function (model) {
      264. -
      265. 125 if (!this.readOnly) {
      266. -
      267. 125 delete model.__associations[this.name];
      268. -
      269. }
      270. -
      271. },
      272. -
      273. -
      274. _forceReloadAssociations:function (model) {
      275. -
      276. 243 if (!this.readOnly) {
      277. -
      278. 243 delete model.__associations[this.name];
      279. -
      280. 243 return model[this.name];
      281. -
      282. }
      283. -
      284. },
      285. -
      286. -
      287. /**
      288. -
      289. * @return {Boolean} true if the association is eager.
      290. -
      291. */
      292. -
      293. isEager:function () {
      294. -
      295. 2114 return this.fetchType == fetch.EAGER;
      296. -
      297. },
      298. -
      299. -
      300. _checkAssociationKey:function (parent) {
      301. -
      302. 785 var q = {};
      303. -
      304. 785 this._setAssociationKeys(parent, q);
      305. -
      306. 785 return Object.keys(q).every(function (k) {
      307. -
      308. 785 return q[k] != null
      309. -
      310. });
      311. -
      312. },
      313. -
      314. -
      315. _getAssociationKey:function () {
      316. -
      317. 5205 var options = this.__opts, key, ret = [], lk, rk;
      318. -
      319. 5205 if (!isUndefinedOrNull((key = options.key))) {
      320. -
      321. 722 if (this.supportsStringKey && isString(key)) {
      322. -
      323. //normalize the key first!
      324. -
      325. 356 ret = [
      326. -
      327. [this.isOwner ? this.defaultLeftKey : key],
      328. -
      329. [this.isOwner ? key : this.defaultRightKey]
      330. -
      331. ];
      332. -
      333. 366 } else if (this.supportsHashKey && isHash(key)) {
      334. -
      335. 366 var leftKey = Object.keys(key)[0];
      336. -
      337. 366 var rightKey = key[leftKey];
      338. -
      339. 366 ret = [
      340. -
      341. [leftKey],
      342. -
      343. [rightKey]
      344. -
      345. ];
      346. -
      347. 0 } else if (this.supportsCompositeKey && isArray(key)) {
      348. -
      349. 0 ret = [
      350. -
      351. [key],
      352. -
      353. null
      354. -
      355. ];
      356. -
      357. }
      358. -
      359. 4483 } else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
      360. -
      361. && !isUndefinedOrNull((rk = options.rightKey)))) {
      362. -
      363. 140 ret = [
      364. -
      365. array.toArray(lk), array.toArray(rk)
      366. -
      367. ];
      368. -
      369. } else {
      370. -
      371. //todo handle composite primary keys
      372. -
      373. 4343 ret = [
      374. -
      375. [this.defaultLeftKey],
      376. -
      377. [this.defaultRightKey]
      378. -
      379. ];
      380. -
      381. }
      382. -
      383. 5205 return ret;
      384. -
      385. },
      386. -
      387. -
      388. -
      389. _setAssociationKeys:function (parent, model, val) {
      390. -
      391. 1573 var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
      392. -
      393. 1573 if (leftKey && rightKey) {
      394. -
      395. 1573 for (var i = 0; i < leftKey.length; i++) {
      396. -
      397. 1573 model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
      398. -
      399. }
      400. -
      401. } else {
      402. -
      403. 0 leftKey.forEach(function (k) {
      404. -
      405. 0 model[k] = !isUndefined(val) ? val : parent[k];
      406. -
      407. });
      408. -
      409. }
      410. -
      411. },
      412. -
      413. -
      414. _setDatasetOptions:function (ds) {
      415. -
      416. 974 var options = this.__opts || {};
      417. -
      418. 974 var order, limit, distinct, select, query;
      419. -
      420. //allow for multi key ordering
      421. -
      422. 974 if (!isUndefined((select = this.select))) {
      423. -
      424. 0 ds = ds.select.apply(ds, array.toArray(select));
      425. -
      426. }
      427. -
      428. 974 if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
      429. -
      430. 0 ds = ds.filter(query);
      431. -
      432. }
      433. -
      434. 974 if (isFunction(this.filter)) {
      435. -
      436. 334 var ret = this.filter.apply(this, [ds]);
      437. -
      438. 334 if (isInstanceOf(ret, ds._static)) {
      439. -
      440. 334 ds = ret;
      441. -
      442. }
      443. -
      444. }
      445. -
      446. 974 if (!isUndefined((distinct = options.distinct))) {
      447. -
      448. 0 ds = ds.limit.apply(ds, array.toArray(distinct));
      449. -
      450. }
      451. -
      452. 974 if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
      453. -
      454. 0 ds = ds.order.apply(ds, array.toArray(order));
      455. -
      456. }
      457. -
      458. 974 if (!isUndefined((limit = options.limit))) {
      459. -
      460. 0 ds = ds.limit.apply(ds, array.toArray(limit));
      461. -
      462. }
      463. -
      464. 974 return ds;
      465. -
      466. -
      467. },
      468. -
      469. -
      470. /**
      471. -
      472. *Filters our associated dataset to load our association.
      473. +
      474. * <p> Dynamically genertated methods include
      475. +
      476. * <ul>
      477. +
      478. * <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
      479. +
      480. * {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
      481. +
      482. * to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
      483. +
      484. * {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
      485. +
      486. * <ul>
      487. +
      488. * <li>Conditioned join types that accept conditions.
      489. +
      490. * <ul>
      491. +
      492. * <li>inner - INNER JOIN</li>
      493. +
      494. * <li>fullOuter - FULL OUTER</li>
      495. +
      496. * <li>rightOuter - RIGHT OUTER JOIN</li>
      497. +
      498. * <li>leftOuter - LEFT OUTER JOIN</li>
      499. +
      500. * <li>full - FULL JOIN</li>
      501. +
      502. * <li>right - RIGHT JOIN</li>
      503. +
      504. * <li>left - LEFT JOIN</li>
      505. +
      506. * </ul>
      507. +
      508. * </li>
      509. +
      510. * <li>Unconditioned join types that do not accept join conditions
      511. +
      512. * <ul>
      513. +
      514. * <li>natural - NATURAL JOIN</li>
      515. +
      516. * <li>naturalLeft - NATURAL LEFT JOIN</li>
      517. +
      518. * <li>naturalRight - NATURAL RIGHT JOIN</li>
      519. +
      520. * <li>naturalFull - NATURA FULLL JOIN</li>
      521. +
      522. * <li>cross - CROSS JOIN</li>
      523. +
      524. * </ul>
      525. +
      526. * </li>
      527. +
      528. * </ul>
      529. +
      530. * </li>
      531. +
      532. * </li>
      533. +
      534. * </ul>
      535. *
      536. -
      537. *@return {Dataset} the dataset with all filters applied.
      538. -
      539. **/
      540. -
      541. _filter:function (parent) {
      542. -
      543. 648 var options = this.__opts || {};
      544. -
      545. 648 var ds;
      546. -
      547. 648 if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {
      548. -
      549. 0 ds = ds.apply(parent, [parent]);
      550. -
      551. }
      552. -
      553. 648 if (!ds) {
      554. -
      555. 648 var q = {};
      556. -
      557. 648 this._setAssociationKeys(parent, q);
      558. -
      559. 648 ds = this.model.dataset.naked().filter(q);
      560. -
      561. 648 var recip = this.model._findAssociation(this);
      562. -
      563. 648 recip && (recip = recip[1]);
      564. -
      565. 648 ds.rowCb = hitch(this, function (item) {
      566. -
      567. 489 var ret = new Promise();
      568. -
      569. 489 var model = this._toModel(item, true);
      570. -
      571. 489 recip && recip.__setValue(model, parent);
      572. -
      573. //call hook to finish other model associations
      574. -
      575. 489 model._hook("post", "load").then(hitch(this, function () {
      576. -
      577. 489 ret.callback(model);
      578. -
      579. }), ret);
      580. -
      581. 489 return ret.promise();
      582. -
      583. });
      584. +
      585. * <p>
      586. +
      587. * <h4>Features:</h4>
      588. +
      589. * <p>
      590. +
      591. * Features that a particular {@link patio.Dataset} supports are shown in the example below.
      592. +
      593. * If you wish to implement an adapter please override these values depending on the database that
      594. +
      595. * you are developing the adapter for.
      596. +
      597. * </p>
      598. +
      599. * <pre class="code">
      600. +
      601. * var ds = DB.from("test");
      602. +
      603. *
      604. +
      605. * //The default values returned
      606. +
      607. *
      608. +
      609. * //Whether this dataset quotes identifiers.
      610. +
      611. * //Whether this dataset quotes identifiers.
      612. +
      613. * ds.quoteIdentifiers //=>true
      614. +
      615. *
      616. +
      617. * //Whether this dataset will provide accurate number of rows matched for
      618. +
      619. * //delete and update statements. Accurate in this case is the number of
      620. +
      621. * //rows matched by the dataset's filter.
      622. +
      623. * ds.providesAccurateRowsMatched; //=>true
      624. +
      625. *
      626. +
      627. * //Times Whether the dataset requires SQL standard datetimes (false by default,
      628. +
      629. * // as most allow strings with ISO 8601 format).
      630. +
      631. * ds.requiresSqlStandardDate; //=>false
      632. +
      633. *
      634. +
      635. * //Whether the dataset supports common table expressions (the WITH clause).
      636. +
      637. * ds.supportsCte; //=>true
      638. +
      639. *
      640. +
      641. * //Whether the dataset supports the DISTINCT ON clause, false by default.
      642. +
      643. * ds.supportsDistinctOn; //=>false
      644. +
      645. *
      646. +
      647. * //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      648. +
      649. * ds.supportsIntersectExcept; //=>true
      650. +
      651. *
      652. +
      653. * //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
      654. +
      655. * ds.supportsIntersectExceptAll; //=>true
      656. +
      657. *
      658. +
      659. * //Whether the dataset supports the IS TRUE syntax.
      660. +
      661. * ds.supportsIsTrue; //=>true
      662. +
      663. *
      664. +
      665. * //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      666. +
      667. * ds.supportsJoinUsing; //=>true
      668. +
      669. *
      670. +
      671. * //Whether modifying joined datasets is supported.
      672. +
      673. * ds.supportsModifyingJoin; //=>false
      674. +
      675. *
      676. +
      677. * //Whether the IN/NOT IN operators support multiple columns when an
      678. +
      679. * ds.supportsMultipleColumnIn; //=>true
      680. +
      681. *
      682. +
      683. * //Whether the dataset supports timezones in literal timestamps
      684. +
      685. * ds.supportsTimestampTimezone; //=>false
      686. +
      687. *
      688. +
      689. * //Whether the dataset supports fractional seconds in literal timestamps
      690. +
      691. * ds.supportsTimestampUsecs; //=>true
      692. +
      693. *
      694. +
      695. * //Whether the dataset supports window functions.
      696. +
      697. * ds.supportsWindowFunctions; //=>false
      698. +
      699. * </pre>
      700. +
      701. * <p>
      702. +
      703. * <p>
      704. +
      705. * <h4>Actions</h4>
      706. +
      707. * <p>
      708. +
      709. * Each dataset does not actually send any query to the database until an action method has
      710. +
      711. * been called upon it(with the exception of {@link patio.Dataset#graph} because columns
      712. +
      713. * from the other table might need retrived in order to set up the graph). Each action
      714. +
      715. * returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
      716. +
      717. * that you account for errors otherwise it can be difficult to track down issues.
      718. +
      719. * The list of action methods is:
      720. +
      721. * <ul>
      722. +
      723. * <li>{@link patio.Dataset#all}</li>
      724. +
      725. * <li>{@link patio.Dataset#one}</li>
      726. +
      727. * <li>{@link patio.Dataset#avg}</li>
      728. +
      729. * <li>{@link patio.Dataset#count}</li>
      730. +
      731. * <li>{@link patio.Dataset#columns}</li>
      732. +
      733. * <li>{@link patio.Dataset#remove}</li>
      734. +
      735. * <li>{@link patio.Dataset#forEach}</li>
      736. +
      737. * <li>{@link patio.Dataset#empty}</li>
      738. +
      739. * <li>{@link patio.Dataset#first}</li>
      740. +
      741. * <li>{@link patio.Dataset#get}</li>
      742. +
      743. * <li>{@link patio.Dataset#import}</li>
      744. +
      745. * <li>{@link patio.Dataset#insert}</li>
      746. +
      747. * <li>{@link patio.Dataset#save}</li>
      748. +
      749. * <li>{@link patio.Dataset#insertMultiple}</li>
      750. +
      751. * <li>{@link patio.Dataset#saveMultiple}</li>
      752. +
      753. * <li>{@link patio.Dataset#interval}</li>
      754. +
      755. * <li>{@link patio.Dataset#last}</li>
      756. +
      757. * <li>{@link patio.Dataset#map}</li>
      758. +
      759. * <li>{@link patio.Dataset#max}</li>
      760. +
      761. * <li>{@link patio.Dataset#min}</li>
      762. +
      763. * <li>{@link patio.Dataset#multiInsert}</li>
      764. +
      765. * <li>{@link patio.Dataset#range}</li>
      766. +
      767. * <li>{@link patio.Dataset#selectHash}</li>
      768. +
      769. * <li>{@link patio.Dataset#selectMap}</li>
      770. +
      771. * <li>{@link patio.Dataset#selectOrderMap}</li>
      772. +
      773. * <li>{@link patio.Dataset#set}</li>
      774. +
      775. * <li>{@link patio.Dataset#singleRecord}</li>
      776. +
      777. * <li>{@link patio.Dataset#singleValue}</li>
      778. +
      779. * <li>{@link patio.Dataset#sum}</li>
      780. +
      781. * <li>{@link patio.Dataset#toCsv}</li>
      782. +
      783. * <li>{@link patio.Dataset#toHash}</li>
      784. +
      785. * <li>{@link patio.Dataset#truncate}</li>
      786. +
      787. * <li>{@link patio.Dataset#update}</li>
      788. +
      789. * </ul>
      790. +
      791. *
      792. +
      793. * </p>
      794. +
      795. * </p>
      796. +
      797. *
      798. +
      799. * @constructs
      800. +
      801. *
      802. +
      803. *
      804. +
      805. * @param {patio.Database} db the database this dataset should use when querying for data.
      806. +
      807. * @param {Object} opts options to set on this dataset instance
      808. +
      809. *
      810. +
      811. * @property {Function} rowCb callback to be invoked for each row returned from the database.
      812. +
      813. * the return value will be used as the result of query. The rowCb can also return a promise,
      814. +
      815. * The resolved value of the promise will be used as result.
      816. +
      817. *
      818. +
      819. * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
      820. +
      821. * This value will be defaulted to whatever the identifierInputMethod
      822. +
      823. * is on the database used in initialization.
      824. +
      825. *
      826. +
      827. * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
      828. +
      829. * This value will be defaulted to whatever the identifierOutputMethod
      830. +
      831. * is on the database used in initialization.
      832. +
      833. * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
      834. +
      835. * throws a {patio.DatasetError} tf the dataset doesn't have a table.
      836. +
      837. * <pre class="code">
      838. +
      839. * DB.from("table").firstSourceAlias;
      840. +
      841. * //=> "table"
      842. +
      843. *
      844. +
      845. * DB.from("table___t").firstSourceAlias;
      846. +
      847. * //=> "t"
      848. +
      849. * </pre>
      850. +
      851. *
      852. +
      853. * @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
      854. +
      855. * have a table, raises a {@link patio.erros.DatasetError}.
      856. +
      857. *<pre class="code">
      858. +
      859. *
      860. +
      861. * DB.from("table").firstSourceTable;
      862. +
      863. * //=> "table"
      864. +
      865. *
      866. +
      867. * DB.from("table___t").firstSourceTable;
      868. +
      869. * //=> "t"
      870. +
      871. * </pre>
      872. +
      873. * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
      874. +
      875. * <pre class="code">
      876. +
      877. * DB.from("items").isSimpleSelectAll; //=> true
      878. +
      879. * DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
      880. +
      881. * </pre>
      882. +
      883. * @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
      884. +
      885. * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
      886. +
      887. * delete and update statements. Accurate in this case is the number of
      888. +
      889. * rows matched by the dataset's filter.
      890. +
      891. * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
      892. +
      893. * as most allow strings with ISO 8601 format).
      894. +
      895. * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
      896. +
      897. * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
      898. +
      899. * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      900. +
      901. * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
      902. +
      903. * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
      904. +
      905. * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      906. +
      907. * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
      908. +
      909. * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
      910. +
      911. * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
      912. +
      913. * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
      914. +
      915. * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
      916. +
      917. * @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset.
      918. +
      919. * @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources
      920. +
      921. * @property {Boolean} hasSelectSource true if this dataset already has a select sources.
      922. +
      923. */
      924. +
      925. constructor:function (db, opts) {
      926. +
      927. 29216 this._super(arguments);
      928. +
      929. 29216 this.db = db;
      930. +
      931. 29216 this.__opts = {};
      932. +
      933. 29216 this.__rowCb = null;
      934. +
      935. 29216 if (db) {
      936. +
      937. 15039 this.__quoteIdentifiers = db.quoteIdentifiers;
      938. +
      939. 15039 this.__identifierInputMethod = db.identifierInputMethod;
      940. +
      941. 15039 this.__identifierOutputMethod = db.identifierOutputMethod;
      942. }
      943. -
      944. -
      945. 648 return this._setDatasetOptions(ds);
      946. },
      947. -
      948. __setValue:function (parent, model) {
      949. -
      950. 2061 parent.__associations[this.name] = this._fetchMethod
      951. -
      952. == "all" ? !isArray(model) ? [model] : model : isArray(model) ? model[0] : model;
      953. -
      954. 2061 return parent.__associations[this.name];
      955. -
      956. },
      957. -
      958. fetch:function (parent) {
      959. -
      960. 785 var ret = new Promise();
      961. -
      962. 785 if (this._checkAssociationKey(parent)) {
      963. -
      964. 733 this._filter(parent)[this._fetchMethod]().then(hitch(this, function (result) {
      965. -
      966. 733 this.__setValue(parent, result);
      967. -
      968. 733 ret.callback(result);
      969. -
      970. 733 parent = null;
      971. -
      972. }), ret);
      973. -
      974. } else {
      975. -
      976. 52 this.__setValue(parent, null);
      977. -
      978. 52 ret.callback(null);
      979. +
      980. /**
      981. +
      982. * Returns a new clone of the dataset with with the given options merged into the current datasets options.
      983. +
      984. * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
      985. +
      986. * columns are deleted. This method should generally not be called
      987. +
      988. * directly by user code.
      989. +
      990. *
      991. +
      992. * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
      993. +
      994. * @return [patio.Dataset] a cloned dataset with the merged options
      995. +
      996. **/
      997. +
      998. mergeOptions:function (opts) {
      999. +
      1000. 14948 opts = isUndefined(opts) ? {} : opts;
      1001. +
      1002. 14948 var ds = new this._static(this.db, {});
      1003. +
      1004. 14948 ds.rowCb = this.rowCb;
      1005. +
      1006. 14948 this._static.FEATURES.forEach(function (f) {
      1007. +
      1008. 209272 ds[f] = this[f];
      1009. +
      1010. }, this);
      1011. +
      1012. 14948 ds.__opts = merge({}, this.__opts, opts);
      1013. +
      1014. 14948 ds.identifierInputMethod = this.identifierInputMethod;
      1015. +
      1016. 14948 ds.identifierOutputMethod = this.identifierOutputMethod;
      1017. +
      1018. 14948 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
      1019. +
      1020. 14948 if (Object.keys(opts).some(function (o) {
      1021. +
      1022. 13536 return columnChangeOpts.indexOf(o) != -1;
      1023. +
      1024. })) {
      1025. +
      1026. 2456 ds.__opts.columns = null;
      1027. }
      1028. -
      1029. 785 return ret;
      1030. +
      1031. 14948 return ds;
      1032. },
      1033. +
      1034. /**
      1035. -
      1036. * Middleware called before a model is removed.
      1037. -
      1038. * </br>
      1039. -
      1040. * <b> This is called in the scope of the model</b>
      1041. -
      1042. * @param {Function} next function to pass control up the middleware stack.
      1043. -
      1044. * @param {_Association} self reference to the Association that is being acted up.
      1045. +
      1046. * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      1047. +
      1048. * or {@link patio.sql.AliasedExpression}, depending on the format:
      1049. +
      1050. *
      1051. +
      1052. * <ul>
      1053. +
      1054. * <li>For columns : table__column___alias.</li>
      1055. +
      1056. * <li>For tables : schema__table___alias.</li>
      1057. +
      1058. * </ul>
      1059. +
      1060. * each portion of the identifier is optional. See example below
      1061. +
      1062. *
      1063. +
      1064. * @example
      1065. +
      1066. *
      1067. +
      1068. * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
      1069. +
      1070. * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
      1071. +
      1072. * ds.stringToIdentifier("table__column___alias");
      1073. +
      1074. * //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
      1075. +
      1076. *
      1077. +
      1078. * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      1079. +
      1080. * or {@link patio.sql.AliasedExpression}.
      1081. +
      1082. *
      1083. +
      1084. * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
      1085. */
      1086. -
      1087. _preRemove:function (next, model) {
      1088. -
      1089. 223 if (this.isOwner && !this.isCascading) {
      1090. -
      1091. 46 var q = {};
      1092. -
      1093. 46 this._setAssociationKeys(model, q, null);
      1094. -
      1095. 46 model[this.associatedDatasetName].update(q).classic(next);
      1096. +
      1097. stringToIdentifier:function (name) {
      1098. +
      1099. 15093 if (isString(name)) {
      1100. +
      1101. 10138 var parts = this._splitString(name);
      1102. +
      1103. 10138 var schema = parts[0], table = parts[1], alias = parts[2];
      1104. +
      1105. 10138 return (schema && table && alias
      1106. +
      1107. ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
      1108. +
      1109. : (schema && table
      1110. +
      1111. ? new QualifiedIdentifier(schema, table)
      1112. +
      1113. : (table && alias
      1114. +
      1115. ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
      1116. } else {
      1117. -
      1118. 177 next();
      1119. +
      1120. 4955 return name;
      1121. }
      1122. },
      1123. /**
      1124. -
      1125. * Middleware called aft era model is removed.
      1126. -
      1127. * </br>
      1128. -
      1129. * <b> This is called in the scope of the model</b>
      1130. -
      1131. * @param {Function} next function to pass control up the middleware stack.
      1132. -
      1133. * @param {_Association} self reference to the Association that is being called.
      1134. +
      1135. * Can either be a string or null.
      1136. +
      1137. *
      1138. +
      1139. *
      1140. +
      1141. * @example
      1142. +
      1143. * //columns
      1144. +
      1145. * table__column___alias //=> table.column as alias
      1146. +
      1147. * table__column //=> table.column
      1148. +
      1149. * //tables
      1150. +
      1151. * schema__table___alias //=> schema.table as alias
      1152. +
      1153. * schema__table //=> schema.table
      1154. +
      1155. *
      1156. +
      1157. * //name and alias
      1158. +
      1159. * columnOrTable___alias //=> columnOrTable as alias
      1160. +
      1161. *
      1162. +
      1163. *
      1164. +
      1165. *
      1166. +
      1167. * @return {String[]} an array with the elements being:
      1168. +
      1169. * <ul>
      1170. +
      1171. * <li>For columns :[table, column, alias].</li>
      1172. +
      1173. * <li>For tables : [schema, table, alias].</li>
      1174. +
      1175. * </ul>
      1176. */
      1177. -
      1178. _postRemove:function (next, model) {
      1179. -
      1180. 514 next();
      1181. +
      1182. _splitString:function (s) {
      1183. +
      1184. 19662 var ret, m;
      1185. +
      1186. 19662 if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
      1187. +
      1188. 189 ret = m.slice(1);
      1189. +
      1190. }
      1191. +
      1192. 19473 else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
      1193. +
      1194. 28 ret = [null, m[1], m[2]];
      1195. +
      1196. }
      1197. +
      1198. 19445 else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
      1199. +
      1200. 2079 ret = [m[1], m[2], null];
      1201. +
      1202. }
      1203. +
      1204. else {
      1205. +
      1206. 17366 ret = [null, s, null];
      1207. +
      1208. }
      1209. +
      1210. 19662 return ret;
      1211. },
      1212. /**
      1213. -
      1214. * Middleware called before a model is saved.
      1215. -
      1216. * </br>
      1217. -
      1218. * <b> This is called in the scope of the model</b>
      1219. -
      1220. * @param {Function} next function to pass control up the middleware stack.
      1221. -
      1222. * @param {_Association} self reference to the Association that is being called.
      1223. -
      1224. */
      1225. -
      1226. _preSave:function (next, model) {
      1227. -
      1228. 324 next();
      1229. -
      1230. },
      1231. +
      1232. * @ignore
      1233. +
      1234. **/
      1235. +
      1236. getters:{
      1237. -
      1238. /**
      1239. -
      1240. * Middleware called after a model is saved.
      1241. -
      1242. * </br>
      1243. -
      1244. * <b> This is called in the scope of the model</b>
      1245. -
      1246. * @param {Function} next function to pass control up the middleware stack.
      1247. -
      1248. * @param {_Association} self reference to the Association that is being called.
      1249. -
      1250. */
      1251. -
      1252. _postSave:function (next, model) {
      1253. -
      1254. 198 next();
      1255. -
      1256. },
      1257. +
      1258. rowCb:function () {
      1259. +
      1260. 23052 return this.__rowCb;
      1261. +
      1262. },
      1263. -
      1264. /**
      1265. -
      1266. * Middleware called before a model is updated.
      1267. -
      1268. * </br>
      1269. -
      1270. * <b> This is called in the scope of the model</b>
      1271. -
      1272. * @param {Function} next function to pass control up the middleware stack.
      1273. -
      1274. * @param {_Association} self reference to the Association that is being called.
      1275. -
      1276. */
      1277. -
      1278. _preUpdate:function (next, model) {
      1279. -
      1280. 2 next();
      1281. -
      1282. },
      1283. +
      1284. identifierInputMethod:function () {
      1285. +
      1286. 14948 return this.__identifierInputMethod;
      1287. +
      1288. },
      1289. -
      1290. /**
      1291. -
      1292. * Middleware called before a model is updated.
      1293. -
      1294. * </br>
      1295. -
      1296. * <b> This is called in the scope of the model</b>
      1297. -
      1298. * @param {Function} next function to pass control up the middleware stack.
      1299. -
      1300. * @param {_Association} self reference to the Association that is being called.
      1301. -
      1302. */
      1303. -
      1304. _postUpdate:function (next, model) {
      1305. -
      1306. 127 next();
      1307. -
      1308. },
      1309. +
      1310. identifierOutputMethod:function () {
      1311. +
      1312. 14948 return this.__identifierOutputMethod;
      1313. +
      1314. },
      1315. -
      1316. /**
      1317. -
      1318. * Middleware called before a model is loaded.
      1319. -
      1320. * </br>
      1321. -
      1322. * <b> This is called in the scope of the model</b>
      1323. -
      1324. * @param {Function} next function to pass control up the middleware stack.
      1325. -
      1326. * @param {_Association} self reference to the Association that is being called.
      1327. -
      1328. */
      1329. -
      1330. _preLoad:function (next, model) {
      1331. -
      1332. 1570 next();
      1333. -
      1334. },
      1335. +
      1336. firstSourceAlias:function () {
      1337. +
      1338. 579 var source = this.__opts.from;
      1339. +
      1340. 579 if (isUndefinedOrNull(source) || !source.length) {
      1341. +
      1342. 2 throw new DatasetError("No source specified for the query");
      1343. +
      1344. }
      1345. +
      1346. 577 source = source[0];
      1347. +
      1348. 577 if (isInstanceOf(source, AliasedExpression)) {
      1349. +
      1350. 20 return source.alias;
      1351. +
      1352. 557 } else if (isString(source)) {
      1353. +
      1354. 0 var parts = this._splitString(source);
      1355. +
      1356. 0 var alias = parts[2];
      1357. +
      1358. 0 return alias ? alias : source;
      1359. +
      1360. } else {
      1361. +
      1362. 557 return source;
      1363. +
      1364. }
      1365. +
      1366. },
      1367. -
      1368. /**
      1369. -
      1370. * Middleware called after a model is loaded.
      1371. -
      1372. * </br>
      1373. -
      1374. * <b> This is called in the scope of the model</b>
      1375. -
      1376. * @param {Function} next function to pass control up the middleware stack.
      1377. -
      1378. * @param {_Association} self reference to the Association that is being called.
      1379. -
      1380. */
      1381. -
      1382. _postLoad:function (next, model) {
      1383. -
      1384. 0 next();
      1385. -
      1386. },
      1387. +
      1388. firstSourceTable:function () {
      1389. +
      1390. 15 var source = this.__opts.from;
      1391. +
      1392. 15 if (isUndefinedOrNull(source) || !source.length) {
      1393. +
      1394. 1 throw new QueryError("No source specified for the query");
      1395. +
      1396. }
      1397. +
      1398. 14 var source = source[0];
      1399. +
      1400. 14 if (isInstanceOf(source, AliasedExpression)) {
      1401. +
      1402. 3 return source.expression;
      1403. +
      1404. 11 } else if (isString(source)) {
      1405. +
      1406. 0 var parts = this._splitString(source);
      1407. +
      1408. 0 return source;
      1409. +
      1410. } else {
      1411. +
      1412. 11 return source;
      1413. +
      1414. }
      1415. +
      1416. },
      1417. -
      1418. /**
      1419. -
      1420. * Alias used to explicitly set an association on a model.
      1421. -
      1422. * @param {*} val the value to set the association to
      1423. -
      1424. * @param {_Association} self reference to the Association that is being called.
      1425. -
      1426. */
      1427. -
      1428. _setter:function (val, model) {
      1429. -
      1430. 0 model.__associations[this.name] = val;
      1431. -
      1432. },
      1433. +
      1434. sourceList:function () {
      1435. +
      1436. 0 return (this.__opts.from || []).map(this.stringToIdentifier, this);
      1437. +
      1438. },
      1439. -
      1440. associationLoaded:function (model) {
      1441. -
      1442. 2707 return model.__associations.hasOwnProperty(this.name);
      1443. -
      1444. },
      1445. +
      1446. joinSourceList:function () {
      1447. +
      1448. 0 return (this.__opts.join || []).map(function (join) {
      1449. +
      1450. 0 return this.stringToIdentifier(join.tableAlias || join.table);
      1451. +
      1452. }, this);
      1453. +
      1454. },
      1455. -
      1456. getAssociation:function (model) {
      1457. -
      1458. 885 return model.__associations[this.name];
      1459. +
      1460. hasSelectSource:function () {
      1461. +
      1462. 0 var select = this.__opts.select;
      1463. +
      1464. 0 return !(isUndefinedOrNull(select) || select.length === 0);
      1465. +
      1466. }
      1467. },
      1468. /**
      1469. -
      1470. * Alias used to explicitly get an association on a model.
      1471. -
      1472. * @param {_Association} self reference to the Association that is being called.
      1473. -
      1474. */
      1475. -
      1476. _getter:function (model) {
      1477. -
      1478. //if we have them return them;
      1479. -
      1480. 612 if (this.associationLoaded(model)) {
      1481. -
      1482. 220 var assoc = this.getAssociation(model);
      1483. -
      1484. 220 return this.isEager() ? assoc : new Promise().callback(assoc).promise();
      1485. -
      1486. 392 } else if (model.isNew) {
      1487. -
      1488. 0 return null;
      1489. -
      1490. } else {
      1491. -
      1492. 392 return this.fetch(model);
      1493. -
      1494. }
      1495. -
      1496. },
      1497. +
      1498. * @ignore
      1499. +
      1500. **/
      1501. +
      1502. setters:{
      1503. +
      1504. /**@lends patio.Dataset.prototype*/
      1505. -
      1506. _toModel:function (val, fromDb) {
      1507. -
      1508. 1412 var Model = this.model;
      1509. -
      1510. 1412 if (!isUndefinedOrNull(Model)) {
      1511. -
      1512. 1412 if (!isInstanceOf(val, Model)) {
      1513. -
      1514. 1167 val = new this.model(val, fromDb);
      1515. -
      1516. }
      1517. -
      1518. } else {
      1519. -
      1520. 0 throw new PatioError("Invalid model " + this.name);
      1521. -
      1522. }
      1523. -
      1524. 1412 return val;
      1525. -
      1526. },
      1527. +
      1528. identifierInputMethod:function (meth) {
      1529. +
      1530. 15038 this.__identifierInputMethod = meth;
      1531. +
      1532. },
      1533. -
      1534. /**
      1535. -
      1536. * Method to inject functionality into a model. This method alters the model
      1537. -
      1538. * to prepare it for associations, and initializes all required middleware calls
      1539. -
      1540. * to fulfill requirements needed to loaded the associations.
      1541. -
      1542. *
      1543. -
      1544. * @param {Model} parent the model that is having an associtaion set on it.
      1545. -
      1546. * @param {String} name the name of the association.
      1547. -
      1548. */
      1549. -
      1550. inject:function (parent, name) {
      1551. -
      1552. 55 this.name = name;
      1553. -
      1554. 55 var self = this;
      1555. -
      1556. 55 this.parent = parent;
      1557. -
      1558. 55 parent.prototype.__defineGetter__(name, function () {
      1559. -
      1560. 612 return self._getter(this);
      1561. -
      1562. });
      1563. -
      1564. 55 parent.prototype.__defineGetter__(this.associatedDatasetName, function () {
      1565. -
      1566. 146 return self._filter(this);
      1567. -
      1568. });
      1569. +
      1570. identifierOutputMethod:function (meth) {
      1571. +
      1572. 15038 this.__identifierOutputMethod = meth;
      1573. +
      1574. },
      1575. -
      1576. 55 if (!this.readOnly && this.createSetter) {
      1577. -
      1578. //define a setter because we arent read only
      1579. -
      1580. 55 parent.prototype.__defineSetter__(name, function (vals) {
      1581. -
      1582. 139 self._setter(vals, this);
      1583. -
      1584. });
      1585. +
      1586. rowCb:function (cb) {
      1587. +
      1588. 18564 if (isFunction(cb) || isNull(cb)) {
      1589. +
      1590. 18559 this.__rowCb = cb;
      1591. +
      1592. } else {
      1593. +
      1594. 5 throw new DatasetError("rowCb mus be a function");
      1595. +
      1596. }
      1597. }
      1598. -
      1599. -
      1600. //set up all callbacks
      1601. -
      1602. 55 ["pre", "post"].forEach(function (op) {
      1603. -
      1604. 110 ["save", "update", "remove", "load"].forEach(function (type) {
      1605. -
      1606. 440 parent[op](type, function (next) {
      1607. -
      1608. 5598 return self["_" + op + type.charAt(0).toUpperCase() + type.slice(1)](next, this);
      1609. -
      1610. });
      1611. -
      1612. }, this);
      1613. -
      1614. }, this);
      1615. -
      1616. },
      1617. -
      1618. -
      1619. getters:{
      1620. -
      1621. -
      1622. select:function () {
      1623. -
      1624. 648 return this.__opts.select;
      1625. -
      1626. },
      1627. -
      1628. -
      1629. defaultLeftKey:function () {
      1630. -
      1631. 2841 var ret = "";
      1632. -
      1633. 2841 if (this.isOwner) {
      1634. -
      1635. 2095 ret = this.__opts.primaryKey || this.parent.primaryKey[0]
      1636. -
      1637. } else {
      1638. -
      1639. 746 ret = this.model.tableName + "Id";
      1640. -
      1641. }
      1642. -
      1643. 2841 return ret;
      1644. -
      1645. },
      1646. -
      1647. -
      1648. defaultRightKey:function () {
      1649. -
      1650. 2721 return this.associatedModelKey;
      1651. -
      1652. },
      1653. -
      1654. -
      1655. //Returns our model
      1656. -
      1657. model:function () {
      1658. -
      1659. 10572 return this.patio.getModel(this._model, this.parent.db);
      1660. -
      1661. },
      1662. -
      1663. -
      1664. associatedModelKey:function () {
      1665. -
      1666. 2721 var ret = "";
      1667. -
      1668. 2721 if (this.isOwner) {
      1669. -
      1670. 1857 ret = this.__opts.primaryKey || this.parent.tableName + "Id";
      1671. -
      1672. } else {
      1673. -
      1674. 864 ret = this.model.primaryKey[0];
      1675. -
      1676. }
      1677. -
      1678. 2721 return ret;
      1679. -
      1680. },
      1681. -
      1682. -
      1683. associatedDatasetName:function () {
      1684. -
      1685. 201 return this.name + "Dataset"
      1686. -
      1687. },
      1688. -
      1689. -
      1690. removeAssociationFlagName:function () {
      1691. -
      1692. 27 return "__remove" + this.name + "association"
      1693. -
      1694. }
      1695. -
      1696. -
      1697. }
      1698. -
      1699. },
      1700. +
      1701. }
      1702. +
      1703. },
      1704. static:{
      1705. -
      1706. /**@lends patio.associations.Association*/
      1707. -
      1708. -
      1709. fetch:{
      1710. -
      1711. -
      1712. LAZY:"lazy",
      1713. -
      1714. -
      1715. EAGER:"eager"
      1716. -
      1717. }
      1718. +
      1719. COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
      1720. +
      1721. COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
      1722. +
      1723. COLUMN_REF_RE3:/^(\w+)__(\w+)$/
      1724. }
      1725. -
      1726. }).as(module);
      +
    5. }).as(module);
    6. +
    7. +
    -
    +
    -
    model.js
    +
    associations/_Association.js
    - Coverage88.51 - SLOC1067 - LOC322 - Missed37 + Coverage87.18 + SLOC515 + LOC156 + Missed20
    -
    1. 1var comb = require("comb"),
    2. -
    3. isFunction = comb.isFunction,
    4. +
      1. 1var comb = require("comb-proxy"),
      2. +
      3. define = comb.define,
      4. isUndefined = comb.isUndefined,
      5. -
      6. isDefined = comb.isDefined,
      7. +
      8. isUndefinedOrNull = comb.isUndefinedOrNull,
      9. isBoolean = comb.isBoolean,
      10. isString = comb.isString,
      11. -
      12. argsToArray = comb.argsToArray,
      13. -
      14. isInstanceOf = comb.isInstanceOf,
      15. -
      16. serial = comb.serial,
      17. isHash = comb.isHash,
      18. -
      19. when = comb.when,
      20. -
      21. merge = comb.merge,
      22. -
      23. toArray = comb.array.toArray,
      24. -
      25. ModelError = require("./errors").ModelError,
      26. -
      27. plugins = require("./plugins"),
      28. -
      29. isUndefinedOrNull = comb.isUndefinedOrNull,
      30. -
      31. AssociationPlugin = plugins.AssociationPlugin,
      32. -
      33. QueryPlugin = plugins.QueryPlugin,
      34. +
      35. isFunction = comb.isFunction,
      36. +
      37. isInstanceOf = comb.isInstanceOf,
      38. Promise = comb.Promise,
      39. PromiseList = comb.PromiseList,
      40. -
      41. HashTable = comb.collections.HashTable,
      42. hitch = comb.hitch,
      43. -
      44. hitchIgnore = comb.hitchIgnore,
      45. +
      46. array = comb.array,
      47. +
      48. isArray = comb.isArray,
      49. Middleware = comb.plugins.Middleware,
      50. -
      51. EventEmitter = require("events").EventEmitter,
      52. -
      53. util = require("util"),
      54. -
      55. define = comb.define,
      56. -
      57. patio;
      58. -
      59. -
      60. -
      61. 1var MODELS = new HashTable();
      62. +
      63. PatioError = require("../errors").PatioError;
      64. -
      65. 1var applyColumnTransformMethod = function (val, meth) {
      66. -
      67. 11464 return !isUndefinedOrNull(meth) ? isFunction(val[meth]) ? val[meth] : isFunction(comb[meth]) ? comb[meth](val) : val : val;
      68. +
      69. 1var fetch = {
      70. +
      71. LAZY:"lazy",
      72. +
      73. EAGER:"eager"
      74. };
      75. -
      76. 1var checkAndTransformName = function (name) {
      77. -
      78. 10693 return isString(name) ? applyColumnTransformMethod(name, Model.camelize === true ? "camelize" : Model.underscore === true ? "underscore" : null) : name;
      79. -
      80. };
      81. -
      82. 1var Model = define([QueryPlugin, Middleware], {
      83. +
      84. /**
      85. +
      86. * @class
      87. +
      88. * Base class for all associations.
      89. +
      90. *
      91. +
      92. * </br>
      93. +
      94. * <b>NOT to be instantiated directly</b>
      95. +
      96. * Its just documented for reference.
      97. +
      98. *
      99. +
      100. * @constructs
      101. +
      102. * @param {Object} options
      103. +
      104. * @param {String} options.model a string to look up the model that we are associated with
      105. +
      106. * @param {Function} options.filter a callback to find association if a filter is defined then
      107. +
      108. * the association is read only
      109. +
      110. * @param {Object} options.key object with left key and right key
      111. +
      112. * @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
      113. +
      114. * @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
      115. +
      116. * the associations are automatically filled, if fetch.Lazy is supplied
      117. +
      118. * then a promise is returned and is called back with the loaded models
      119. +
      120. * @property {Model} model the model associatied with this association.
      121. +
      122. * @name Association
      123. +
      124. * @memberOf patio.associations
      125. +
      126. * */
      127. +
      128. 1define(Middleware, {
      129. instance:{
      130. +
      131. /**@lends patio.associations.Association.prototype*/
      132. +
      133. +
      134. type:"",
      135. +
      136. +
      137. //Our associated model
      138. +
      139. _model:null,
      140. +
      141. /**
      142. -
      143. * @lends patio.Model.prototype
      144. +
      145. * Fetch type
      146. */
      147. +
      148. fetchType:fetch.LAZY,
      149. -
      150. __ignore:false,
      151. +
      152. /**how to order our association*/
      153. +
      154. orderBy:null,
      155. -
      156. __changed:null,
      157. +
      158. /**Our filter method*/
      159. +
      160. filter:null,
      161. -
      162. __values:null,
      163. +
      164. __hooks:null,
      165. -
      166. /**
      167. -
      168. * patio - read only
      169. -
      170. *
      171. -
      172. * @type patio
      173. -
      174. */
      175. -
      176. patio:null,
      177. +
      178. isOwner:true,
      179. +
      180. +
      181. createSetter:true,
      182. +
      183. +
      184. isCascading:false,
      185. +
      186. +
      187. supportsStringKey:true,
      188. +
      189. +
      190. supportsHashKey:true,
      191. +
      192. +
      193. supportsCompositeKey:true,
      194. +
      195. +
      196. supportsLeftAndRightKey:true,
      197. /**
      198. -
      199. * The database type such as mysql
      200. -
      201. *
      202. -
      203. * @type String
      204. *
      205. -
      206. * */
      207. -
      208. type:null,
      209. +
      210. *Method to call to look up association,
      211. +
      212. *called after the model has been filtered
      213. +
      214. **/
      215. +
      216. _fetchMethod:"all",
      217. -
      218. /**
      219. -
      220. * Whether or not this model is new
      221. -
      222. * */
      223. -
      224. __isNew:true,
      225. -
      226. /**
      227. -
      228. * Signifies if the model has changed
      229. -
      230. * */
      231. -
      232. __isChanged:false,
      233. +
      234. constructor:function (options, patio, filter) {
      235. +
      236. 55 options = options || {};
      237. +
      238. 55 if (!options.model) {
      239. +
      240. 0 throw new Error("Model is required for " + this.type + " association");
      241. +
      242. }
      243. +
      244. 55 this._model = options.model;
      245. +
      246. 55 this.patio = patio;
      247. +
      248. 55 this.__opts = options;
      249. +
      250. 55 !isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
      251. +
      252. 55 this.filter = filter;
      253. +
      254. 55 this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
      255. +
      256. 55 this.__hooks =
      257. +
      258. {before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
      259. +
      260. 55 var hooks = ["Add", "Remove", "Set", "Load"];
      261. +
      262. 55 ["before", "after"].forEach(function (h) {
      263. +
      264. 110 hooks.forEach(function (a) {
      265. +
      266. 440 var hookName = h + a, hook;
      267. +
      268. 440 if (isFunction((hook = options[hookName]))) {
      269. +
      270. 0 this.__hooks[h][a.toLowerCase()] = hook;
      271. +
      272. }
      273. +
      274. }, this);
      275. +
      276. }, this);
      277. +
      278. 55 this.fetchType = options.fetchType || fetch.LAZY;
      279. +
      280. },
      281. +
      282. +
      283. _callHook:function (hook, action, args) {
      284. +
      285. 0 var func = this.__hooks[hook][action], ret;
      286. +
      287. 0 if (isFunction(func)) {
      288. +
      289. 0 ret = func.apply(this, args);
      290. +
      291. }
      292. +
      293. 0 return ret;
      294. +
      295. },
      296. +
      297. +
      298. _clearAssociations:function (model) {
      299. +
      300. 125 if (!this.readOnly) {
      301. +
      302. 125 delete model.__associations[this.name];
      303. +
      304. }
      305. +
      306. },
      307. +
      308. +
      309. _forceReloadAssociations:function (model) {
      310. +
      311. 243 if (!this.readOnly) {
      312. +
      313. 243 delete model.__associations[this.name];
      314. +
      315. 243 return model[this.name];
      316. +
      317. }
      318. +
      319. },
      320. /**
      321. -
      322. * Base class for all models.
      323. -
      324. * <p>This is used through {@link patio.addModel}, <b>NOT directly.</b></p>
      325. -
      326. *
      327. -
      328. * @constructs
      329. -
      330. * @augments comb.plugins.Middleware
      331. -
      332. *
      333. -
      334. * @param {Object} columnValues values of each column to be used by this Model.
      335. -
      336. *
      337. -
      338. * @property {patio.Dataset} dataset a dataset to use to retrieve models from the database. The dataset
      339. -
      340. * has the {@link patio.Dataset#rowCb} set to create instances of this model.
      341. -
      342. * @property {String[]} columns a list of columns this models table contains.
      343. -
      344. * @property {Object} schema the schema of this models table.
      345. -
      346. * @property {String} tableName the table name of this models table.
      347. -
      348. * @property {*} primaryKeyValue the value of this models primaryKey
      349. -
      350. * @property {Boolean} isNew true if this model is new and does not exist in the database.
      351. -
      352. * @property {Boolean} isChanged true if the model has been changed and not saved.
      353. -
      354. *
      355. -
      356. * @borrows patio.Dataset#all as all
      357. -
      358. * @borrows patio.Dataset#one as one
      359. -
      360. * @borrows patio.Dataset#avg as avg
      361. -
      362. * @borrows patio.Dataset#count as count
      363. -
      364. * @borrows patio.Dataset#columns as columns
      365. -
      366. * @borrows patio.Dataset#forEach as forEach
      367. -
      368. * @borrows patio.Dataset#isEmpty as empty
      369. -
      370. * @borrows patio.Dataset#first as first
      371. -
      372. * @borrows patio.Dataset#get as get
      373. -
      374. * @borrows patio.Dataset#import as import
      375. -
      376. * @borrows patio.Dataset#insert as insert
      377. -
      378. * @borrows patio.Dataset#insertMultiple as insertMultiple
      379. -
      380. * @borrows patio.Dataset#saveMultiple as saveMultiple
      381. -
      382. * @borrows patio.Dataset#interval as interval
      383. -
      384. * @borrows patio.Dataset#last as last
      385. -
      386. * @borrows patio.Dataset#map as map
      387. -
      388. * @borrows patio.Dataset#max as max
      389. -
      390. * @borrows patio.Dataset#min as min
      391. -
      392. * @borrows patio.Dataset#multiInsert as multiInsert
      393. -
      394. * @borrows patio.Dataset#range as range
      395. -
      396. * @borrows patio.Dataset#selectHash as selectHash
      397. -
      398. * @borrows patio.Dataset#selectMap as selectMap
      399. -
      400. * @borrows patio.Dataset#selectOrderMap as selectOrderMap
      401. -
      402. * @borrows patio.Dataset#set as set
      403. -
      404. * @borrows patio.Dataset#singleRecord as singleRecord
      405. -
      406. * @borrows patio.Dataset#singleValue as singleValue
      407. -
      408. * @borrows patio.Dataset#sum as sum
      409. -
      410. * @borrows patio.Dataset#toCsv as toCsv
      411. -
      412. * @borrows patio.Dataset#toHash as toHash
      413. -
      414. * @borrows patio.Dataset#truncate as truncate
      415. -
      416. * @borrows patio.Dataset#addGraphAliases as addGraphAliases
      417. -
      418. * @borrows patio.Dataset#and as and
      419. -
      420. * @borrows patio.Dataset#distinct as distinct
      421. -
      422. * @borrows patio.Dataset#except as except
      423. -
      424. * @borrows patio.Dataset#exclude as exclude
      425. -
      426. * @borrows patio.Dataset#is as is
      427. -
      428. * @borrows patio.Dataset#isNot as isNot
      429. -
      430. * @borrows patio.Dataset#eq as eq
      431. -
      432. * @borrows patio.Dataset#neq as neq
      433. -
      434. * @borrows patio.Dataset#lt as lt
      435. -
      436. * @borrows patio.Dataset#lte as lte
      437. -
      438. * @borrows patio.Dataset#gt as gt
      439. -
      440. * @borrows patio.Dataset#gte as gte
      441. -
      442. * @borrows patio.Dataset#forUpdate as forUpdate
      443. -
      444. * @borrows patio.Dataset#from as from
      445. -
      446. * @borrows patio.Dataset#fromSelf as fromSelf
      447. -
      448. * @borrows patio.Dataset#graph as graph
      449. -
      450. * @borrows patio.Dataset#grep as grep
      451. -
      452. * @borrows patio.Dataset#group as group
      453. -
      454. * @borrows patio.Dataset#groupAndCount as groupAndCount
      455. -
      456. * @borrows patio.Dataset#groupBy as groupBy
      457. -
      458. * @borrows patio.Dataset#having as having
      459. -
      460. * @borrows patio.Dataset#intersect as intersect
      461. -
      462. * @borrows patio.Dataset#invert as invert
      463. -
      464. * @borrows patio.Dataset#limit as limit
      465. -
      466. * @borrows patio.Dataset#lockStyle as lockStyle
      467. -
      468. * @borrows patio.Dataset#naked as naked
      469. -
      470. * @borrows patio.Dataset#or as or
      471. -
      472. * @borrows patio.Dataset#order as order
      473. -
      474. * @borrows patio.Dataset#orderAppend as orderAppend
      475. -
      476. * @borrows patio.Dataset#orderBy as orderBy
      477. -
      478. * @borrows patio.Dataset#orderMore as orderMore
      479. -
      480. * @borrows patio.Dataset#orderPrepend as orderPrepend
      481. -
      482. * @borrows patio.Dataset#qualify as qualify
      483. -
      484. * @borrows patio.Dataset#reverse as reverse
      485. -
      486. * @borrows patio.Dataset#reverseOrder as reverseOrder
      487. -
      488. * @borrows patio.Dataset#select as select
      489. -
      490. * @borrows patio.Dataset#selectAll as selectAll
      491. -
      492. * @borrows patio.Dataset#selectAppend as selectAppend
      493. -
      494. * @borrows patio.Dataset#selectMore as selectMore
      495. -
      496. * @borrows patio.Dataset#setDefaults as setDefaults
      497. -
      498. * @borrows patio.Dataset#setGraphAliases as setGraphAliases
      499. -
      500. * @borrows patio.Dataset#setOverrides as setOverrides
      501. -
      502. * @borrows patio.Dataset#unfiltered as unfiltered
      503. -
      504. * @borrows patio.Dataset#ungraphed as ungraphed
      505. -
      506. * @borrows patio.Dataset#ungrouped as ungrouped
      507. -
      508. * @borrows patio.Dataset#union as union
      509. -
      510. * @borrows patio.Dataset#unlimited as unlimited
      511. -
      512. * @borrows patio.Dataset#unordered as unordered
      513. -
      514. * @borrows patio.Dataset#where as where
      515. -
      516. * @borrows patio.Dataset#with as with
      517. -
      518. * @borrows patio.Dataset#withRecursive as withRecursive
      519. -
      520. * @borrows patio.Dataset#withSql as withSql
      521. -
      522. * @borrows patio.Dataset#naturalJoin as naturalJoin
      523. -
      524. * @borrows patio.Dataset#naturalLeftJoin as naturalLeftJoin
      525. -
      526. * @borrows patio.Dataset#naturalRightJoin as naturalRightJoin
      527. -
      528. * @borrows patio.Dataset#naturalFullJoin as naturalFullJoin
      529. -
      530. * @borrows patio.Dataset#crossJoin as crossJoin
      531. -
      532. * @borrows patio.Dataset#innerJoin as innerJoin
      533. -
      534. * @borrows patio.Dataset#fullOuterJoin as fullOuterJoin
      535. -
      536. * @borrows patio.Dataset#rightOuterJoin as rightOuterJoin
      537. -
      538. * @borrows patio.Dataset#leftOuterJoin as leftOuterJoin
      539. -
      540. * @borrows patio.Dataset#fullJoin as fullJoin
      541. -
      542. * @borrows patio.Dataset#rightJoin as rightJoin
      543. -
      544. * @borrows patio.Dataset#leftJoin as leftJoin
      545. -
      546. * */
      547. -
      548. constructor:function (options, fromDb) {
      549. -
      550. 3111 if (this.synced) {
      551. -
      552. 3111 this.__emitter = new EventEmitter();
      553. -
      554. 3111 this._super(arguments);
      555. -
      556. 3111 this.patio = patio || require("./index");
      557. -
      558. 3111 fromDb = isBoolean(fromDb) ? fromDb : false;
      559. -
      560. 3111 this.__changed = {};
      561. -
      562. 3111 this.__values = {};
      563. -
      564. 3111 if (fromDb) {
      565. -
      566. 1754 this._hook("pre", "load");
      567. -
      568. 1754 this.__isNew = false;
      569. -
      570. 1754 this.__setFromDb(options, true);
      571. -
      572. 1754 if (this._static.emitOnLoad) {
      573. -
      574. 1754 this.emit("load", this);
      575. -
      576. 1754 this._static.emit("load", this);
      577. -
      578. }
      579. -
      580. } else {
      581. -
      582. 1357 this.__isNew = true;
      583. -
      584. 1357 this.__set(options);
      585. -
      586. }
      587. -
      588. } else {
      589. -
      590. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
      591. -
      592. }
      593. +
      594. * @return {Boolean} true if the association is eager.
      595. +
      596. */
      597. +
      598. isEager:function () {
      599. +
      600. 2114 return this.fetchType == fetch.EAGER;
      601. },
      602. -
      603. __set:function (values, ignore) {
      604. -
      605. 1479 values = values || {};
      606. -
      607. 1479 this.__ignore = ignore === true;
      608. -
      609. 1479 Object.keys(values).forEach(function (attribute) {
      610. -
      611. 6632 var value = values[attribute];
      612. -
      613. //check if the column is a constrained value and is allowed to be set
      614. -
      615. 6632 !ignore && this._checkIfColumnIsConstrained(attribute);
      616. -
      617. 6632 this[attribute] = value;
      618. -
      619. }, this);
      620. -
      621. 1479 this.__ignore = false;
      622. +
      623. _checkAssociationKey:function (parent) {
      624. +
      625. 785 var q = {};
      626. +
      627. 785 this._setAssociationKeys(parent, q);
      628. +
      629. 785 return Object.keys(q).every(function (k) {
      630. +
      631. 785 return q[k] != null
      632. +
      633. });
      634. },
      635. -
      636. __setFromDb:function (values, ignore) {
      637. -
      638. 3057 values = values || {};
      639. -
      640. 3057 this.__ignore = ignore === true;
      641. -
      642. 3057 var schema = this.schema;
      643. -
      644. 3057 Object.keys(values).forEach(function (column) {
      645. -
      646. 25150 var value = values[column];
      647. -
      648. // Typecast value retrieved from db
      649. -
      650. 25150 if (schema.hasOwnProperty(column)) {
      651. -
      652. 25149 this.__values[column] = this._typeCastValue(column, value, ignore);
      653. -
      654. } else {
      655. -
      656. 1 console.log(column);
      657. -
      658. 1 this[column] = value;
      659. +
      660. _getAssociationKey:function () {
      661. +
      662. 5205 var options = this.__opts, key, ret = [], lk, rk;
      663. +
      664. 5205 if (!isUndefinedOrNull((key = options.key))) {
      665. +
      666. 722 if (this.supportsStringKey && isString(key)) {
      667. +
      668. //normalize the key first!
      669. +
      670. 356 ret = [
      671. +
      672. [this.isOwner ? this.defaultLeftKey : key],
      673. +
      674. [this.isOwner ? key : this.defaultRightKey]
      675. +
      676. ];
      677. +
      678. 366 } else if (this.supportsHashKey && isHash(key)) {
      679. +
      680. 366 var leftKey = Object.keys(key)[0];
      681. +
      682. 366 var rightKey = key[leftKey];
      683. +
      684. 366 ret = [
      685. +
      686. [leftKey],
      687. +
      688. [rightKey]
      689. +
      690. ];
      691. +
      692. 0 } else if (this.supportsCompositeKey && isArray(key)) {
      693. +
      694. 0 ret = [
      695. +
      696. [key],
      697. +
      698. null
      699. +
      700. ];
      701. }
      702. -
      703. }, this);
      704. -
      705. 3057 this.__ignore = false;
      706. -
      707. +
      708. 4483 } else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
      709. +
      710. && !isUndefinedOrNull((rk = options.rightKey)))) {
      711. +
      712. 140 ret = [
      713. +
      714. array.toArray(lk), array.toArray(rk)
      715. +
      716. ];
      717. +
      718. } else {
      719. +
      720. //todo handle composite primary keys
      721. +
      722. 4343 ret = [
      723. +
      724. [this.defaultLeftKey],
      725. +
      726. [this.defaultRightKey]
      727. +
      728. ];
      729. +
      730. }
      731. +
      732. 5205 return ret;
      733. },
      734. -
      735. /**
      736. -
      737. * Set multiple values at once. Useful if you have a hash of properties that you want to set.
      738. -
      739. *
      740. -
      741. * <b>NOTE:</b> This method will use the static restrictedColumns property of the model.
      742. -
      743. *
      744. -
      745. * @example
      746. -
      747. *
      748. -
      749. * myModel.setValues({firstName : "Bob", lastName : "yukon"});
      750. -
      751. *
      752. -
      753. * //this will throw an error by default, assuming id is a pk.
      754. -
      755. * myModel.setValues({id : 1, firstName : "Bob", lastName : "yukon"});
      756. -
      757. *
      758. -
      759. * @param {Object} values value to set on the model.
      760. -
      761. *
      762. -
      763. * @return {patio.Model} return this for chaining.
      764. -
      765. */
      766. -
      767. setValues:function (values) {
      768. -
      769. 17 this.__set(values, false);
      770. -
      771. 17 return this;
      772. -
      773. },
      774. -
      775. _toObject:function () {
      776. -
      777. 1124 if (this.synced) {
      778. -
      779. 1124 var columns = this._static.columns, ret = {};
      780. -
      781. 1124 for (var i in columns) {
      782. -
      783. 10220 var col = columns[i];
      784. -
      785. 10220 var val = this.__values[col];
      786. -
      787. 10220 if (!isUndefined(val)) {
      788. -
      789. 6169 ret[col] = val;
      790. -
      791. }
      792. +
      793. _setAssociationKeys:function (parent, model, val) {
      794. +
      795. 1573 var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
      796. +
      797. 1573 if (leftKey && rightKey) {
      798. +
      799. 1573 for (var i = 0; i < leftKey.length; i++) {
      800. +
      801. 1573 model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
      802. }
      803. -
      804. 1124 return ret;
      805. } else {
      806. -
      807. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
      808. +
      809. 0 leftKey.forEach(function (k) {
      810. +
      811. 0 model[k] = !isUndefined(val) ? val : parent[k];
      812. +
      813. });
      814. }
      815. },
      816. -
      817. _addColumnToIsChanged:function (name, val) {
      818. -
      819. 7556 if (!this.isNew && !this.__ignore) {
      820. -
      821. 165 this.__isChanged = true;
      822. -
      823. 165 this.__changed[name] = val;
      824. +
      825. _setDatasetOptions:function (ds) {
      826. +
      827. 974 var options = this.__opts || {};
      828. +
      829. 974 var order, limit, distinct, select, query;
      830. +
      831. //allow for multi key ordering
      832. +
      833. 974 if (!isUndefined((select = this.select))) {
      834. +
      835. 0 ds = ds.select.apply(ds, array.toArray(select));
      836. }
      837. -
      838. },
      839. -
      840. -
      841. _checkIfColumnIsConstrained:function (name) {
      842. -
      843. 6632 if (this.synced && !this.__ignore) {
      844. -
      845. 6632 var col = this.schema[name], restrictedCols = this._static.restrictedColumns || [];
      846. -
      847. 6632 if (!isUndefined(col) && (col.primaryKey && this._static.isRestrictedPrimaryKey) || restrictedCols.indexOf(name) != -1) {
      848. -
      849. 0 throw new ModelError("Cannot set primary key of model " + this._static.tableName);
      850. +
      851. 974 if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
      852. +
      853. 0 ds = ds.filter(query);
      854. +
      855. }
      856. +
      857. 974 if (isFunction(this.filter)) {
      858. +
      859. 334 var ret = this.filter.apply(this, [ds]);
      860. +
      861. 334 if (isInstanceOf(ret, ds._static)) {
      862. +
      863. 334 ds = ret;
      864. }
      865. }
      866. -
      867. },
      868. -
      869. -
      870. _getColumnValue:function (name) {
      871. -
      872. 5427 var val = this.__values[name];
      873. -
      874. 5427 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
      875. -
      876. 5427 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
      877. +
      878. 974 if (!isUndefined((distinct = options.distinct))) {
      879. +
      880. 0 ds = ds.limit.apply(ds, array.toArray(distinct));
      881. +
      882. }
      883. +
      884. 974 if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
      885. +
      886. 0 ds = ds.order.apply(ds, array.toArray(order));
      887. +
      888. }
      889. +
      890. 974 if (!isUndefined((limit = options.limit))) {
      891. +
      892. 0 ds = ds.limit.apply(ds, array.toArray(limit));
      893. +
      894. }
      895. +
      896. 974 return ds;
      897. -
      898. 5427 return columnValue;
      899. },
      900. -
      901. _setColumnValue:function (name, val) {
      902. -
      903. 7556 var ignore = this.__ignore;
      904. -
      905. 7556 val = this._typeCastValue(name, val, ignore);
      906. -
      907. 7556 this._addColumnToIsChanged(name, val);
      908. -
      909. 7556 var setterFunc = this["_set" + name.charAt(0).toUpperCase() + name.substr(1)];
      910. -
      911. 7556 var columnValue = isFunction(setterFunc) ? setterFunc.call(this, val, ignore) : val;
      912. -
      913. 7556 this.__values[name] = columnValue;
      914. -
      915. 7556 if (this._static.emitOnColumnSet) {
      916. -
      917. 7556 this.emit("column", name, columnValue, ignore);
      918. +
      919. /**
      920. +
      921. *Filters our associated dataset to load our association.
      922. +
      923. *
      924. +
      925. *@return {Dataset} the dataset with all filters applied.
      926. +
      927. **/
      928. +
      929. _filter:function (parent) {
      930. +
      931. 648 var options = this.__opts || {};
      932. +
      933. 648 var ds;
      934. +
      935. 648 if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {
      936. +
      937. 0 ds = ds.apply(parent, [parent]);
      938. +
      939. }
      940. +
      941. 648 if (!ds) {
      942. +
      943. 648 var q = {};
      944. +
      945. 648 this._setAssociationKeys(parent, q);
      946. +
      947. 648 ds = this.model.dataset.naked().filter(q);
      948. +
      949. 648 var recip = this.model._findAssociation(this);
      950. +
      951. 648 recip && (recip = recip[1]);
      952. +
      953. 648 ds.rowCb = hitch(this, function (item) {
      954. +
      955. 489 var ret = new Promise();
      956. +
      957. 489 var model = this._toModel(item, true);
      958. +
      959. 489 recip && recip.__setValue(model, parent);
      960. +
      961. //call hook to finish other model associations
      962. +
      963. 489 model._hook("post", "load").then(hitch(this, function () {
      964. +
      965. 489 ret.callback(model);
      966. +
      967. }), ret);
      968. +
      969. 489 return ret.promise();
      970. +
      971. });
      972. }
      973. +
      974. +
      975. 648 return this._setDatasetOptions(ds);
      976. },
      977. +
      978. __setValue:function (parent, model) {
      979. +
      980. 2061 parent.__associations[this.name] = this._fetchMethod
      981. +
      982. == "all" ? !isArray(model) ? [model] : model : isArray(model) ? model[0] : model;
      983. +
      984. 2061 return parent.__associations[this.name];
      985. +
      986. },
      987. -
      988. //Typecast the value to the column's type if typecasting. Calls the database's
      989. -
      990. //typecast_value method, so database adapters can override/augment the handling
      991. -
      992. //for database specific column types.
      993. -
      994. _typeCastValue:function (column, value, fromDatabase) {
      995. -
      996. 32705 var colSchema, clazz = this._static;
      997. -
      998. 32705 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
      999. -
      1000. 32705 var type = colSchema.type;
      1001. -
      1002. 32705 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
      1003. -
      1004. 3 value = null;
      1005. -
      1006. }
      1007. -
      1008. 32705 var raiseOnError = clazz.raiseOnTypecastError;
      1009. -
      1010. 32705 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
      1011. -
      1012. 0 throw new ModelError("null is not allowed for the " + column + " column.");
      1013. -
      1014. }
      1015. -
      1016. 32705 try {
      1017. -
      1018. 32705 value = clazz.db.typecastValue(type, value);
      1019. -
      1020. } catch (e) {
      1021. -
      1022. 0 if (raiseOnError === true) {
      1023. -
      1024. 0 throw e;
      1025. -
      1026. }
      1027. -
      1028. }
      1029. +
      1030. fetch:function (parent) {
      1031. +
      1032. 785 var ret = new Promise();
      1033. +
      1034. 785 if (this._checkAssociationKey(parent)) {
      1035. +
      1036. 733 this._filter(parent)[this._fetchMethod]().then(hitch(this, function (result) {
      1037. +
      1038. 733 this.__setValue(parent, result);
      1039. +
      1040. 733 ret.callback(result);
      1041. +
      1042. 733 parent = null;
      1043. +
      1044. }), ret);
      1045. +
      1046. } else {
      1047. +
      1048. 52 this.__setValue(parent, null);
      1049. +
      1050. 52 ret.callback(null);
      1051. }
      1052. -
      1053. 32705 return value;
      1054. +
      1055. 785 return ret;
      1056. },
      1057. /**
      1058. -
      1059. * Convert this model to an object, containing column, value pairs.
      1060. -
      1061. *
      1062. -
      1063. * @return {Object} the object version of this model.
      1064. -
      1065. **/
      1066. -
      1067. toObject:function () {
      1068. -
      1069. 0 return this._toObject(false);
      1070. +
      1071. * Middleware called before a model is removed.
      1072. +
      1073. * </br>
      1074. +
      1075. * <b> This is called in the scope of the model</b>
      1076. +
      1077. * @param {Function} next function to pass control up the middleware stack.
      1078. +
      1079. * @param {_Association} self reference to the Association that is being acted up.
      1080. +
      1081. */
      1082. +
      1083. _preRemove:function (next, model) {
      1084. +
      1085. 223 if (this.isOwner && !this.isCascading) {
      1086. +
      1087. 46 var q = {};
      1088. +
      1089. 46 this._setAssociationKeys(model, q, null);
      1090. +
      1091. 46 model[this.associatedDatasetName].update(q).classic(next);
      1092. +
      1093. } else {
      1094. +
      1095. 177 next();
      1096. +
      1097. }
      1098. },
      1099. /**
      1100. -
      1101. * Convert this model to JSON, containing column, value pairs.
      1102. -
      1103. *
      1104. -
      1105. * @return {JSON} the JSON version of this model.
      1106. -
      1107. **/
      1108. -
      1109. toJSON:function () {
      1110. -
      1111. 0 return this.toObject();
      1112. +
      1113. * Middleware called aft era model is removed.
      1114. +
      1115. * </br>
      1116. +
      1117. * <b> This is called in the scope of the model</b>
      1118. +
      1119. * @param {Function} next function to pass control up the middleware stack.
      1120. +
      1121. * @param {_Association} self reference to the Association that is being called.
      1122. +
      1123. */
      1124. +
      1125. _postRemove:function (next, model) {
      1126. +
      1127. 514 next();
      1128. },
      1129. /**
      1130. -
      1131. * Convert this model to a string, containing column, value pairs.
      1132. -
      1133. *
      1134. -
      1135. * @return {String} the string version of this model.
      1136. -
      1137. **/
      1138. -
      1139. toString:function () {
      1140. -
      1141. 0 return JSON.stringify(this.toObject(), null, 4);
      1142. +
      1143. * Middleware called before a model is saved.
      1144. +
      1145. * </br>
      1146. +
      1147. * <b> This is called in the scope of the model</b>
      1148. +
      1149. * @param {Function} next function to pass control up the middleware stack.
      1150. +
      1151. * @param {_Association} self reference to the Association that is being called.
      1152. +
      1153. */
      1154. +
      1155. _preSave:function (next, model) {
      1156. +
      1157. 324 next();
      1158. },
      1159. /**
      1160. -
      1161. * Convert this model to a string, containing column, value pairs.
      1162. -
      1163. *
      1164. -
      1165. * @return {String} the string version of this model.
      1166. -
      1167. **/
      1168. -
      1169. valueOf:function () {
      1170. -
      1171. 0 return this.toObject();
      1172. +
      1173. * Middleware called after a model is saved.
      1174. +
      1175. * </br>
      1176. +
      1177. * <b> This is called in the scope of the model</b>
      1178. +
      1179. * @param {Function} next function to pass control up the middleware stack.
      1180. +
      1181. * @param {_Association} self reference to the Association that is being called.
      1182. +
      1183. */
      1184. +
      1185. _postSave:function (next, model) {
      1186. +
      1187. 198 next();
      1188. },
      1189. -
      1190. _checkTransaction:function (options, cb) {
      1191. -
      1192. 2493 return this._static._checkTransaction(options, cb);
      1193. +
      1194. /**
      1195. +
      1196. * Middleware called before a model is updated.
      1197. +
      1198. * </br>
      1199. +
      1200. * <b> This is called in the scope of the model</b>
      1201. +
      1202. * @param {Function} next function to pass control up the middleware stack.
      1203. +
      1204. * @param {_Association} self reference to the Association that is being called.
      1205. +
      1206. */
      1207. +
      1208. _preUpdate:function (next, model) {
      1209. +
      1210. 2 next();
      1211. },
      1212. -
      1213. addListener:function () {
      1214. -
      1215. 0 var emitter = this.__emitter;
      1216. -
      1217. 0 return emitter.addListener.apply(emitter, arguments);
      1218. -
      1219. },
      1220. -
      1221. on:function () {
      1222. -
      1223. 6 var emitter = this.__emitter;
      1224. -
      1225. 6 return emitter.on.apply(emitter, arguments);
      1226. -
      1227. },
      1228. -
      1229. once:function () {
      1230. -
      1231. 0 var emitter = this.__emitter;
      1232. -
      1233. 0 return emitter.once.apply(emitter, arguments);
      1234. -
      1235. },
      1236. -
      1237. removeListener:function () {
      1238. -
      1239. 6 var emitter = this.__emitter;
      1240. -
      1241. 6 return emitter.removeListener.apply(emitter, arguments);
      1242. -
      1243. },
      1244. -
      1245. removeAllListeners:function () {
      1246. -
      1247. 0 var emitter = this.__emitter;
      1248. -
      1249. 0 return emitter.removeAllListeners.apply(emitter, arguments);
      1250. +
      1251. /**
      1252. +
      1253. * Middleware called before a model is updated.
      1254. +
      1255. * </br>
      1256. +
      1257. * <b> This is called in the scope of the model</b>
      1258. +
      1259. * @param {Function} next function to pass control up the middleware stack.
      1260. +
      1261. * @param {_Association} self reference to the Association that is being called.
      1262. +
      1263. */
      1264. +
      1265. _postUpdate:function (next, model) {
      1266. +
      1267. 127 next();
      1268. },
      1269. -
      1270. setMaxListeners:function () {
      1271. -
      1272. 0 var emitter = this.__emitter;
      1273. -
      1274. 0 return emitter.setMaxListeners.apply(emitter, arguments);
      1275. +
      1276. +
      1277. /**
      1278. +
      1279. * Middleware called before a model is loaded.
      1280. +
      1281. * </br>
      1282. +
      1283. * <b> This is called in the scope of the model</b>
      1284. +
      1285. * @param {Function} next function to pass control up the middleware stack.
      1286. +
      1287. * @param {_Association} self reference to the Association that is being called.
      1288. +
      1289. */
      1290. +
      1291. _preLoad:function (next, model) {
      1292. +
      1293. 1570 next();
      1294. },
      1295. -
      1296. listeners:function () {
      1297. -
      1298. 0 var emitter = this.__emitter;
      1299. -
      1300. 0 return emitter.listeners.apply(emitter, arguments);
      1301. +
      1302. +
      1303. /**
      1304. +
      1305. * Middleware called after a model is loaded.
      1306. +
      1307. * </br>
      1308. +
      1309. * <b> This is called in the scope of the model</b>
      1310. +
      1311. * @param {Function} next function to pass control up the middleware stack.
      1312. +
      1313. * @param {_Association} self reference to the Association that is being called.
      1314. +
      1315. */
      1316. +
      1317. _postLoad:function (next, model) {
      1318. +
      1319. 0 next();
      1320. },
      1321. -
      1322. emit:function () {
      1323. -
      1324. 11120 var emitter = this.__emitter;
      1325. -
      1326. 11120 return emitter.emit.apply(emitter, arguments);
      1327. +
      1328. +
      1329. /**
      1330. +
      1331. * Alias used to explicitly set an association on a model.
      1332. +
      1333. * @param {*} val the value to set the association to
      1334. +
      1335. * @param {_Association} self reference to the Association that is being called.
      1336. +
      1337. */
      1338. +
      1339. _setter:function (val, model) {
      1340. +
      1341. 0 model.__associations[this.name] = val;
      1342. },
      1343. +
      1344. associationLoaded:function (model) {
      1345. +
      1346. 2707 return model.__associations.hasOwnProperty(this.name);
      1347. +
      1348. },
      1349. -
      1350. getters:{
      1351. -
      1352. /**@lends patio.Model.prototype*/
      1353. +
      1354. getAssociation:function (model) {
      1355. +
      1356. 885 return model.__associations[this.name];
      1357. +
      1358. },
      1359. -
      1360. /*Returns my actual primary key value*/
      1361. -
      1362. primaryKeyValue:function () {
      1363. -
      1364. 47 return this[this.primaryKey];
      1365. -
      1366. },
      1367. +
      1368. /**
      1369. +
      1370. * Alias used to explicitly get an association on a model.
      1371. +
      1372. * @param {_Association} self reference to the Association that is being called.
      1373. +
      1374. */
      1375. +
      1376. _getter:function (model) {
      1377. +
      1378. //if we have them return them;
      1379. +
      1380. 612 if (this.associationLoaded(model)) {
      1381. +
      1382. 220 var assoc = this.getAssociation(model);
      1383. +
      1384. 220 return this.isEager() ? assoc : new Promise().callback(assoc).promise();
      1385. +
      1386. 392 } else if (model.isNew) {
      1387. +
      1388. 0 return null;
      1389. +
      1390. } else {
      1391. +
      1392. 392 return this.fetch(model);
      1393. +
      1394. }
      1395. +
      1396. },
      1397. -
      1398. /*Return if Im a new object*/
      1399. -
      1400. isNew:function () {
      1401. -
      1402. 9644 return this.__isNew;
      1403. -
      1404. },
      1405. +
      1406. _toModel:function (val, fromDb) {
      1407. +
      1408. 1412 var Model = this.model;
      1409. +
      1410. 1412 if (!isUndefinedOrNull(Model)) {
      1411. +
      1412. 1412 if (!isInstanceOf(val, Model)) {
      1413. +
      1414. 1167 val = new this.model(val, fromDb);
      1415. +
      1416. }
      1417. +
      1418. } else {
      1419. +
      1420. 0 throw new PatioError("Invalid model " + this.name);
      1421. +
      1422. }
      1423. +
      1424. 1412 return val;
      1425. +
      1426. },
      1427. -
      1428. /*Return if Im changed*/
      1429. -
      1430. isChanged:function () {
      1431. -
      1432. 0 return this.__isChanged;
      1433. -
      1434. },
      1435. +
      1436. /**
      1437. +
      1438. * Method to inject functionality into a model. This method alters the model
      1439. +
      1440. * to prepare it for associations, and initializes all required middleware calls
      1441. +
      1442. * to fulfill requirements needed to loaded the associations.
      1443. +
      1444. *
      1445. +
      1446. * @param {Model} parent the model that is having an associtaion set on it.
      1447. +
      1448. * @param {String} name the name of the association.
      1449. +
      1450. */
      1451. +
      1452. inject:function (parent, name) {
      1453. +
      1454. 55 this.name = name;
      1455. +
      1456. 55 var self = this;
      1457. +
      1458. 55 this.parent = parent;
      1459. +
      1460. 55 parent.prototype.__defineGetter__(name, function () {
      1461. +
      1462. 612 return self._getter(this);
      1463. +
      1464. });
      1465. +
      1466. 55 parent.prototype.__defineGetter__(this.associatedDatasetName, function () {
      1467. +
      1468. 146 return self._filter(this);
      1469. +
      1470. });
      1471. -
      1472. /**@lends patio.Model.prototype*/
      1473. +
      1474. 55 if (!this.readOnly && this.createSetter) {
      1475. +
      1476. //define a setter because we arent read only
      1477. +
      1478. 55 parent.prototype.__defineSetter__(name, function (vals) {
      1479. +
      1480. 139 self._setter(vals, this);
      1481. +
      1482. });
      1483. +
      1484. }
      1485. -
      1486. primaryKey:function () {
      1487. -
      1488. 2568 return this._static.primaryKey;
      1489. +
      1490. //set up all callbacks
      1491. +
      1492. 55 ["pre", "post"].forEach(function (op) {
      1493. +
      1494. 110 ["save", "update", "remove", "load"].forEach(function (type) {
      1495. +
      1496. 440 parent[op](type, function (next) {
      1497. +
      1498. 5598 return self["_" + op + type.charAt(0).toUpperCase() + type.slice(1)](next, this);
      1499. +
      1500. });
      1501. +
      1502. }, this);
      1503. +
      1504. }, this);
      1505. +
      1506. },
      1507. +
      1508. +
      1509. getters:{
      1510. +
      1511. +
      1512. select:function () {
      1513. +
      1514. 648 return this.__opts.select;
      1515. },
      1516. -
      1517. tableName:function () {
      1518. -
      1519. 40 return this._static.tableName;
      1520. +
      1521. defaultLeftKey:function () {
      1522. +
      1523. 2841 var ret = "";
      1524. +
      1525. 2841 if (this.isOwner) {
      1526. +
      1527. 2095 ret = this.__opts.primaryKey || this.parent.primaryKey[0]
      1528. +
      1529. } else {
      1530. +
      1531. 746 ret = this.model.tableName + "Id";
      1532. +
      1533. }
      1534. +
      1535. 2841 return ret;
      1536. },
      1537. -
      1538. dataset:function () {
      1539. -
      1540. 3085 return this.__dataset || this._static.dataset;
      1541. +
      1542. defaultRightKey:function () {
      1543. +
      1544. 2721 return this.associatedModelKey;
      1545. },
      1546. -
      1547. db:function () {
      1548. -
      1549. 28 return this._static.db;
      1550. +
      1551. //Returns our model
      1552. +
      1553. model:function () {
      1554. +
      1555. 10572 return this.patio.getModel(this._model, this.parent.db);
      1556. },
      1557. -
      1558. schema:function () {
      1559. -
      1560. 75099 return this._static.schema;
      1561. +
      1562. associatedModelKey:function () {
      1563. +
      1564. 2721 var ret = "";
      1565. +
      1566. 2721 if (this.isOwner) {
      1567. +
      1568. 1857 ret = this.__opts.primaryKey || this.parent.tableName + "Id";
      1569. +
      1570. } else {
      1571. +
      1572. 864 ret = this.model.primaryKey[0];
      1573. +
      1574. }
      1575. +
      1576. 2721 return ret;
      1577. },
      1578. -
      1579. columns:function () {
      1580. -
      1581. 0 return this._static.columns;
      1582. +
      1583. associatedDatasetName:function () {
      1584. +
      1585. 201 return this.name + "Dataset"
      1586. },
      1587. -
      1588. synced:function () {
      1589. -
      1590. 12784 return this._static.synced;
      1591. +
      1592. removeAssociationFlagName:function () {
      1593. +
      1594. 27 return "__remove" + this.name + "association"
      1595. }
      1596. }
      1597. },
      1598. static:{
      1599. -
      1600. /**
      1601. -
      1602. * @lends patio.Model
      1603. -
      1604. */
      1605. -
      1606. -
      1607. synced:false,
      1608. +
      1609. /**@lends patio.associations.Association*/
      1610. -
      1611. /**
      1612. -
      1613. * Set to false to prevent the emitting of an event on load
      1614. -
      1615. * @default true
      1616. -
      1617. */
      1618. -
      1619. emitOnLoad:true,
      1620. +
      1621. fetch:{
      1622. -
      1623. /**
      1624. -
      1625. * Set to false to prevent the emitting of an event on the setting of a column value
      1626. -
      1627. * @default true
      1628. -
      1629. */
      1630. -
      1631. emitOnColumnSet:true,
      1632. +
      1633. LAZY:"lazy",
      1634. +
      1635. EAGER:"eager"
      1636. +
      1637. }
      1638. +
      1639. }
      1640. +
      1641. }).as(module);
      +
    + +
    + + + + + +
    +
    model.js
    +
    +
    + Coverage88.47 + SLOC1066 + LOC321 + Missed37 +
    +
    +
    1. 1var comb = require("comb"),
    2. +
    3. isFunction = comb.isFunction,
    4. +
    5. isUndefined = comb.isUndefined,
    6. +
    7. isDefined = comb.isDefined,
    8. +
    9. isBoolean = comb.isBoolean,
    10. +
    11. isString = comb.isString,
    12. +
    13. argsToArray = comb.argsToArray,
    14. +
    15. isInstanceOf = comb.isInstanceOf,
    16. +
    17. serial = comb.serial,
    18. +
    19. isHash = comb.isHash,
    20. +
    21. when = comb.when,
    22. +
    23. merge = comb.merge,
    24. +
    25. toArray = comb.array.toArray,
    26. +
    27. ModelError = require("./errors").ModelError,
    28. +
    29. plugins = require("./plugins"),
    30. +
    31. isUndefinedOrNull = comb.isUndefinedOrNull,
    32. +
    33. AssociationPlugin = plugins.AssociationPlugin,
    34. +
    35. QueryPlugin = plugins.QueryPlugin,
    36. +
    37. Promise = comb.Promise,
    38. +
    39. PromiseList = comb.PromiseList,
    40. +
    41. HashTable = comb.collections.HashTable,
    42. +
    43. hitch = comb.hitch,
    44. +
    45. hitchIgnore = comb.hitchIgnore,
    46. +
    47. Middleware = comb.plugins.Middleware,
    48. +
    49. EventEmitter = require("events").EventEmitter,
    50. +
    51. util = require("util"),
    52. +
    53. define = comb.define,
    54. +
    55. patio;
    56. -
    57. /**
    58. -
    59. * Set to false to prevent empty strings from being type casted to null
    60. -
    61. * @default true
    62. -
    63. */
    64. -
    65. typecastEmptyStringToNull:true,
    66. -
    67. /**
    68. -
    69. * Set to false to prevent properties from being type casted when loaded from the database.
    70. -
    71. * See {@link patio.Database#typecastValue}
    72. -
    73. * @default true
    74. -
    75. */
    76. -
    77. typecastOnLoad:true,
    78. +
    79. 1var MODELS = new HashTable();
    80. -
    81. /**
    82. -
    83. * Set to false to prevent properties from being type casted when manually set.
    84. -
    85. * See {@link patio.Database#typecastValue}
    86. -
    87. * @default true
    88. -
    89. */
    90. -
    91. typecastOnAssignment:true,
    92. +
    93. 1var applyColumnTransformMethod = function (val, meth) {
    94. +
    95. 11464 return !isUndefinedOrNull(meth) ? isFunction(val[meth]) ? val[meth] : isFunction(comb[meth]) ? comb[meth](val) : val : val;
    96. +
    97. };
    98. -
    99. /**
    100. -
    101. * Set to false to prevent errors thrown while type casting a value from being propogated.
    102. -
    103. * @default true
    104. -
    105. */
    106. -
    107. raiseOnTypecastError:true,
    108. +
    109. 1var checkAndTransformName = function (name) {
    110. +
    111. 10693 return isString(name) ? applyColumnTransformMethod(name, Model.camelize === true ? "camelize" : Model.underscore === true ? "underscore" : null) : name;
    112. +
    113. };
    114. +
    115. 1var Model = define([QueryPlugin, Middleware], {
    116. +
    117. instance:{
    118. /**
    119. -
    120. * Set to false to allow the setting of primary keys.
    121. -
    122. * @default false
    123. +
    124. * @lends patio.Model.prototype
    125. */
    126. -
    127. isRestrictedPrimaryKey:true,
    128. -
    129. /**
    130. -
    131. * Set to false to prevent models from using transactions when saving, deleting, or updating.
    132. -
    133. * This applies to the model associations also.
    134. -
    135. */
    136. -
    137. useTransactions:true,
    138. +
    139. __ignore:false,
    140. -
    141. /**
    142. -
    143. * See {@link patio.Dataset#identifierOutputMethod}
    144. -
    145. * @default null
    146. -
    147. */
    148. -
    149. identifierOutputMethod:null,
    150. +
    151. __changed:null,
    152. -
    153. /**
    154. -
    155. * See {@link patio.Dataset#identifierInputMethod}
    156. -
    157. * @default null
    158. -
    159. */
    160. -
    161. identifierInputMethod:null,
    162. +
    163. __values:null,
    164. /**
    165. -
    166. * Set to false to prevent the reload of a model after saving.
    167. -
    168. * @default true
    169. +
    170. * patio - read only
    171. +
    172. *
    173. +
    174. * @type patio
    175. */
    176. -
    177. reloadOnSave:true,
    178. +
    179. patio:null,
    180. /**
    181. -
    182. * Columns that should be restriced when setting values through the {@link patio.Model#set} method.
    183. +
    184. * The database type such as mysql
    185. *
    186. -
    187. */
    188. -
    189. restrictedColumns:null,
    190. +
    191. * @type String
    192. +
    193. *
    194. +
    195. * */
    196. +
    197. type:null,
    198. /**
    199. -
    200. * Set to false to prevent the reload of a model after updating.
    201. -
    202. * @default true
    203. -
    204. */
    205. -
    206. reloadOnUpdate:true,
    207. -
    208. -
    209. -
    210. __camelize:false,
    211. -
    212. -
    213. __underscore:false,
    214. -
    215. -
    216. __columns:null,
    217. -
    218. -
    219. __schema:null,
    220. -
    221. -
    222. __primaryKey:null,
    223. -
    224. -
    225. __dataset:null,
    226. -
    227. -
    228. __db:null,
    229. -
    230. -
    231. __tableName:null,
    232. -
    233. +
    234. * Whether or not this model is new
    235. +
    236. * */
    237. +
    238. __isNew:true,
    239. /**
    240. -
    241. * The table that this Model represents.
    242. -
    243. * <b>READ ONLY</b>
    244. -
    245. */
    246. -
    247. table:null,
    248. +
    249. * Signifies if the model has changed
    250. +
    251. * */
    252. +
    253. __isChanged:false,
    254. /**
    255. -
    256. * patio - read only
    257. +
    258. * Base class for all models.
    259. +
    260. * <p>This is used through {@link patio.addModel}, <b>NOT directly.</b></p>
    261. *
    262. -
    263. * @type patio
    264. -
    265. */
    266. -
    267. patio:null,
    268. -
    269. -
    270. init:function () {
    271. -
    272. 92 var emitter = new EventEmitter();
    273. -
    274. 92 ["addListener", "on", "once", "removeListener",
    275. -
    276. "removeAllListeners", "setMaxListeners", "listeners", "emit"].forEach(function (name) {
    277. -
    278. 736 this[name] = emitter[name].bind(emitter);
    279. -
    280. }, this);
    281. -
    282. 92 if (this.__tableName) {
    283. -
    284. 91 this._setTableName(this.__tableName);
    285. -
    286. }
    287. -
    288. 92 if (this.__db) {
    289. -
    290. 7 this._setDb(this.__db);
    291. -
    292. }
    293. -
    294. },
    295. -
    296. -
    297. -
    298. sync:function (cb) {
    299. -
    300. 3968 var ret = new Promise();
    301. -
    302. 3968 if (!this.synced) {
    303. -
    304. 93 var db = this.db, tableName = this.tableName, supers = this.__supers;
    305. -
    306. 93 db.schema(tableName).then(hitch(this, function (schema) {
    307. -
    308. 93 if (!this.synced && schema) {
    309. -
    310. 93 this._setSchema(schema);
    311. -
    312. 93 if (supers && supers.length) {
    313. -
    314. 3 new PromiseList(supers.map(function (sup) {
    315. -
    316. 3 return sup.sync();
    317. -
    318. })).then(hitch(this, function () {
    319. -
    320. 3 this.synced = true;
    321. -
    322. 3 supers.forEach(this.inherits, this);
    323. -
    324. 3 ret.callback(this);
    325. -
    326. }), ret);
    327. -
    328. } else {
    329. -
    330. 90 this.synced = true;
    331. -
    332. 90 ret.callback(this);
    333. -
    334. }
    335. -
    336. 93 this.emit("sync", this);
    337. -
    338. } else {
    339. -
    340. 0 var error = new ModelError("Unable to find schema for " + tableName);
    341. -
    342. 0 this.emit("error", error);
    343. -
    344. 0 ret.errback(error);
    345. +
    346. * @constructs
    347. +
    348. * @augments comb.plugins.Middleware
    349. +
    350. *
    351. +
    352. * @param {Object} columnValues values of each column to be used by this Model.
    353. +
    354. *
    355. +
    356. * @property {patio.Dataset} dataset a dataset to use to retrieve models from the database. The dataset
    357. +
    358. * has the {@link patio.Dataset#rowCb} set to create instances of this model.
    359. +
    360. * @property {String[]} columns a list of columns this models table contains.
    361. +
    362. * @property {Object} schema the schema of this models table.
    363. +
    364. * @property {String} tableName the table name of this models table.
    365. +
    366. * @property {*} primaryKeyValue the value of this models primaryKey
    367. +
    368. * @property {Boolean} isNew true if this model is new and does not exist in the database.
    369. +
    370. * @property {Boolean} isChanged true if the model has been changed and not saved.
    371. +
    372. *
    373. +
    374. * @borrows patio.Dataset#all as all
    375. +
    376. * @borrows patio.Dataset#one as one
    377. +
    378. * @borrows patio.Dataset#avg as avg
    379. +
    380. * @borrows patio.Dataset#count as count
    381. +
    382. * @borrows patio.Dataset#columns as columns
    383. +
    384. * @borrows patio.Dataset#forEach as forEach
    385. +
    386. * @borrows patio.Dataset#isEmpty as empty
    387. +
    388. * @borrows patio.Dataset#first as first
    389. +
    390. * @borrows patio.Dataset#get as get
    391. +
    392. * @borrows patio.Dataset#import as import
    393. +
    394. * @borrows patio.Dataset#insert as insert
    395. +
    396. * @borrows patio.Dataset#insertMultiple as insertMultiple
    397. +
    398. * @borrows patio.Dataset#saveMultiple as saveMultiple
    399. +
    400. * @borrows patio.Dataset#interval as interval
    401. +
    402. * @borrows patio.Dataset#last as last
    403. +
    404. * @borrows patio.Dataset#map as map
    405. +
    406. * @borrows patio.Dataset#max as max
    407. +
    408. * @borrows patio.Dataset#min as min
    409. +
    410. * @borrows patio.Dataset#multiInsert as multiInsert
    411. +
    412. * @borrows patio.Dataset#range as range
    413. +
    414. * @borrows patio.Dataset#selectHash as selectHash
    415. +
    416. * @borrows patio.Dataset#selectMap as selectMap
    417. +
    418. * @borrows patio.Dataset#selectOrderMap as selectOrderMap
    419. +
    420. * @borrows patio.Dataset#set as set
    421. +
    422. * @borrows patio.Dataset#singleRecord as singleRecord
    423. +
    424. * @borrows patio.Dataset#singleValue as singleValue
    425. +
    426. * @borrows patio.Dataset#sum as sum
    427. +
    428. * @borrows patio.Dataset#toCsv as toCsv
    429. +
    430. * @borrows patio.Dataset#toHash as toHash
    431. +
    432. * @borrows patio.Dataset#truncate as truncate
    433. +
    434. * @borrows patio.Dataset#addGraphAliases as addGraphAliases
    435. +
    436. * @borrows patio.Dataset#and as and
    437. +
    438. * @borrows patio.Dataset#distinct as distinct
    439. +
    440. * @borrows patio.Dataset#except as except
    441. +
    442. * @borrows patio.Dataset#exclude as exclude
    443. +
    444. * @borrows patio.Dataset#is as is
    445. +
    446. * @borrows patio.Dataset#isNot as isNot
    447. +
    448. * @borrows patio.Dataset#eq as eq
    449. +
    450. * @borrows patio.Dataset#neq as neq
    451. +
    452. * @borrows patio.Dataset#lt as lt
    453. +
    454. * @borrows patio.Dataset#lte as lte
    455. +
    456. * @borrows patio.Dataset#gt as gt
    457. +
    458. * @borrows patio.Dataset#gte as gte
    459. +
    460. * @borrows patio.Dataset#forUpdate as forUpdate
    461. +
    462. * @borrows patio.Dataset#from as from
    463. +
    464. * @borrows patio.Dataset#fromSelf as fromSelf
    465. +
    466. * @borrows patio.Dataset#graph as graph
    467. +
    468. * @borrows patio.Dataset#grep as grep
    469. +
    470. * @borrows patio.Dataset#group as group
    471. +
    472. * @borrows patio.Dataset#groupAndCount as groupAndCount
    473. +
    474. * @borrows patio.Dataset#groupBy as groupBy
    475. +
    476. * @borrows patio.Dataset#having as having
    477. +
    478. * @borrows patio.Dataset#intersect as intersect
    479. +
    480. * @borrows patio.Dataset#invert as invert
    481. +
    482. * @borrows patio.Dataset#limit as limit
    483. +
    484. * @borrows patio.Dataset#lockStyle as lockStyle
    485. +
    486. * @borrows patio.Dataset#naked as naked
    487. +
    488. * @borrows patio.Dataset#or as or
    489. +
    490. * @borrows patio.Dataset#order as order
    491. +
    492. * @borrows patio.Dataset#orderAppend as orderAppend
    493. +
    494. * @borrows patio.Dataset#orderBy as orderBy
    495. +
    496. * @borrows patio.Dataset#orderMore as orderMore
    497. +
    498. * @borrows patio.Dataset#orderPrepend as orderPrepend
    499. +
    500. * @borrows patio.Dataset#qualify as qualify
    501. +
    502. * @borrows patio.Dataset#reverse as reverse
    503. +
    504. * @borrows patio.Dataset#reverseOrder as reverseOrder
    505. +
    506. * @borrows patio.Dataset#select as select
    507. +
    508. * @borrows patio.Dataset#selectAll as selectAll
    509. +
    510. * @borrows patio.Dataset#selectAppend as selectAppend
    511. +
    512. * @borrows patio.Dataset#selectMore as selectMore
    513. +
    514. * @borrows patio.Dataset#setDefaults as setDefaults
    515. +
    516. * @borrows patio.Dataset#setGraphAliases as setGraphAliases
    517. +
    518. * @borrows patio.Dataset#setOverrides as setOverrides
    519. +
    520. * @borrows patio.Dataset#unfiltered as unfiltered
    521. +
    522. * @borrows patio.Dataset#ungraphed as ungraphed
    523. +
    524. * @borrows patio.Dataset#ungrouped as ungrouped
    525. +
    526. * @borrows patio.Dataset#union as union
    527. +
    528. * @borrows patio.Dataset#unlimited as unlimited
    529. +
    530. * @borrows patio.Dataset#unordered as unordered
    531. +
    532. * @borrows patio.Dataset#where as where
    533. +
    534. * @borrows patio.Dataset#with as with
    535. +
    536. * @borrows patio.Dataset#withRecursive as withRecursive
    537. +
    538. * @borrows patio.Dataset#withSql as withSql
    539. +
    540. * @borrows patio.Dataset#naturalJoin as naturalJoin
    541. +
    542. * @borrows patio.Dataset#naturalLeftJoin as naturalLeftJoin
    543. +
    544. * @borrows patio.Dataset#naturalRightJoin as naturalRightJoin
    545. +
    546. * @borrows patio.Dataset#naturalFullJoin as naturalFullJoin
    547. +
    548. * @borrows patio.Dataset#crossJoin as crossJoin
    549. +
    550. * @borrows patio.Dataset#innerJoin as innerJoin
    551. +
    552. * @borrows patio.Dataset#fullOuterJoin as fullOuterJoin
    553. +
    554. * @borrows patio.Dataset#rightOuterJoin as rightOuterJoin
    555. +
    556. * @borrows patio.Dataset#leftOuterJoin as leftOuterJoin
    557. +
    558. * @borrows patio.Dataset#fullJoin as fullJoin
    559. +
    560. * @borrows patio.Dataset#rightJoin as rightJoin
    561. +
    562. * @borrows patio.Dataset#leftJoin as leftJoin
    563. +
    564. * */
    565. +
    566. constructor:function (options, fromDb) {
    567. +
    568. 3107 if (this.synced) {
    569. +
    570. 3107 this.__emitter = new EventEmitter();
    571. +
    572. 3107 this._super(arguments);
    573. +
    574. 3107 this.patio = patio || require("./index");
    575. +
    576. 3107 fromDb = isBoolean(fromDb) ? fromDb : false;
    577. +
    578. 3107 this.__changed = {};
    579. +
    580. 3107 this.__values = {};
    581. +
    582. 3107 if (fromDb) {
    583. +
    584. 1754 this._hook("pre", "load");
    585. +
    586. 1754 this.__isNew = false;
    587. +
    588. 1754 this.__setFromDb(options, true);
    589. +
    590. 1754 if (this._static.emitOnLoad) {
    591. +
    592. 1754 this.emit("load", this);
    593. +
    594. 1754 this._static.emit("load", this);
    595. }
    596. -
    597. }), ret);
    598. +
    599. } else {
    600. +
    601. 1353 this.__isNew = true;
    602. +
    603. 1353 this.__set(options);
    604. +
    605. }
    606. } else {
    607. -
    608. 3875 ret.callback(this);
    609. -
    610. }
    611. -
    612. 3968 if (isFunction(cb)) {
    613. -
    614. 0 ret.classic(cb);
    615. +
    616. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
    617. }
    618. -
    619. 3968 return ret.promise();
    620. },
    621. -
    622. /**
    623. -
    624. * Stub for plugins to notified of model inheritance
    625. -
    626. *
    627. -
    628. * @param {patio.Model} model a model class to inherit from
    629. -
    630. */
    631. -
    632. inherits:function (model) {
    633. +
    634. __set:function (values, ignore) {
    635. +
    636. 1475 values = values || {};
    637. +
    638. 1475 this.__ignore = ignore === true;
    639. +
    640. 1475 Object.keys(values).forEach(function (attribute) {
    641. +
    642. 6632 var value = values[attribute];
    643. +
    644. //check if the column is a constrained value and is allowed to be set
    645. +
    646. 6632 !ignore && this._checkIfColumnIsConstrained(attribute);
    647. +
    648. 6632 this[attribute] = value;
    649. +
    650. }, this);
    651. +
    652. 1475 this.__ignore = false;
    653. },
    654. +
    655. __setFromDb:function (values, ignore) {
    656. +
    657. 3057 values = values || {};
    658. +
    659. 3057 this.__ignore = ignore === true;
    660. +
    661. 3057 var schema = this.schema;
    662. +
    663. 3057 Object.keys(values).forEach(function (column) {
    664. +
    665. 25830 var value = values[column];
    666. +
    667. // Typecast value retrieved from db
    668. +
    669. 25830 if (schema.hasOwnProperty(column)) {
    670. +
    671. 25149 this.__values[column] = this._typeCastValue(column, value, ignore);
    672. +
    673. } else {
    674. +
    675. 681 this[column] = value;
    676. +
    677. }
    678. +
    679. }, this);
    680. +
    681. 3057 this.__ignore = false;
    682. +
    683. +
    684. },
    685. /**
    686. -
    687. * Create a new model initialized with the specified values.
    688. +
    689. * Set multiple values at once. Useful if you have a hash of properties that you want to set.
    690. *
    691. -
    692. * @param {Object} values the values to initialize the model with.
    693. +
    694. * <b>NOTE:</b> This method will use the static restrictedColumns property of the model.
    695. *
    696. -
    697. * @returns {Model} instantiated model initialized with the values passed in.
    698. +
    699. * @example
    700. +
    701. *
    702. +
    703. * myModel.setValues({firstName : "Bob", lastName : "yukon"});
    704. +
    705. *
    706. +
    707. * //this will throw an error by default, assuming id is a pk.
    708. +
    709. * myModel.setValues({id : 1, firstName : "Bob", lastName : "yukon"});
    710. +
    711. *
    712. +
    713. * @param {Object} values value to set on the model.
    714. +
    715. *
    716. +
    717. * @return {patio.Model} return this for chaining.
    718. */
    719. -
    720. create:function (values) {
    721. -
    722. //load an object from an object
    723. -
    724. 0 return new this(values, false);
    725. +
    726. setValues:function (values) {
    727. +
    728. 17 this.__set(values, false);
    729. +
    730. 17 return this;
    731. },
    732. -
    733. load:function (vals) {
    734. -
    735. 924 var ret = new Promise();
    736. -
    737. //sync our model
    738. -
    739. 924 this.sync().then(hitch(this, function () {
    740. -
    741. 924 var m = new this(vals, true);
    742. -
    743. //call the hooks!
    744. -
    745. 924 m._hook("post", "load").then(function () {
    746. -
    747. 924 ret.callback(m);
    748. -
    749. }, ret);
    750. -
    751. }), ret);
    752. -
    753. 924 return ret.promise();
    754. +
    755. _toObject:function () {
    756. +
    757. 1124 if (this.synced) {
    758. +
    759. 1124 var columns = this._static.columns, ret = {};
    760. +
    761. 1124 for (var i in columns) {
    762. +
    763. 10220 var col = columns[i];
    764. +
    765. 10220 var val = this.__values[col];
    766. +
    767. 10220 if (!isUndefined(val)) {
    768. +
    769. 6169 ret[col] = val;
    770. +
    771. }
    772. +
    773. }
    774. +
    775. 1124 return ret;
    776. +
    777. } else {
    778. +
    779. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
    780. +
    781. }
    782. },
    783. -
    784. _checkTransaction:function (opts, cb) {
    785. -
    786. 2892 if (isFunction(opts)) {
    787. -
    788. 639 cb = opts;
    789. -
    790. 639 opts = {};
    791. -
    792. } else {
    793. -
    794. 2253 opts = opts || {};
    795. +
    796. _addColumnToIsChanged:function (name, val) {
    797. +
    798. 7556 if (!this.isNew && !this.__ignore) {
    799. +
    800. 165 this.__isChanged = true;
    801. +
    802. 165 this.__changed[name] = val;
    803. }
    804. -
    805. 2892 var ret = new Promise(), retVal = null, errored = false;
    806. -
    807. 2892 this.sync().then(hitch(this, function () {
    808. -
    809. 2892 if (this.useTransaction(opts)) {
    810. -
    811. 2892 this.db.transaction(opts, hitch(this, function () {
    812. -
    813. 2892 return when(cb()).then(function () {
    814. -
    815. 2848 retVal = argsToArray(arguments);
    816. -
    817. }, function () {
    818. -
    819. 44 retVal = argsToArray(arguments);
    820. -
    821. 44 errored = true;
    822. -
    823. });
    824. -
    825. })).then(hitch(this, function () {
    826. -
    827. 2848 ret[errored ? "errback" : "callback"].apply(ret, retVal);
    828. -
    829. }), hitch(this, function () {
    830. -
    831. 44 if (errored) {
    832. -
    833. 44 ret.errback.apply(ret, retVal);
    834. -
    835. } else {
    836. -
    837. 0 ret.errback.apply(ret, arguments);
    838. -
    839. }
    840. -
    841. }));
    842. -
    843. } else {
    844. -
    845. 0 when(cb()).then(ret);
    846. +
    847. },
    848. +
    849. +
    850. _checkIfColumnIsConstrained:function (name) {
    851. +
    852. 6632 if (this.synced && !this.__ignore) {
    853. +
    854. 6632 var col = this.schema[name], restrictedCols = this._static.restrictedColumns || [];
    855. +
    856. 6632 if (!isUndefined(col) && (col.primaryKey && this._static.isRestrictedPrimaryKey) || restrictedCols.indexOf(name) != -1) {
    857. +
    858. 0 throw new ModelError("Cannot set primary key of model " + this._static.tableName);
    859. }
    860. -
    861. }), ret);
    862. -
    863. 2892 return ret.promise();
    864. +
    865. }
    866. },
    867. -
    868. /**
    869. -
    870. * @private
    871. -
    872. * Returns a boolean indicating whether or not to use a transaction.
    873. -
    874. * @param {Object} [opts] set a transaction property to override the {@link patio.Model#useTransaction}.
    875. -
    876. */
    877. -
    878. useTransaction:function (opts) {
    879. -
    880. 2892 opts = opts || {};
    881. -
    882. 2892 return isBoolean(opts.transaction) ? opts.transaction === true : this.useTransactions === true;
    883. +
    884. _getColumnValue:function (name) {
    885. +
    886. 5427 var val = this.__values[name];
    887. +
    888. 5427 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
    889. +
    890. 5427 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
    891. +
    892. +
    893. 5427 return columnValue;
    894. },
    895. -
    896. _setDataset:function (ds) {
    897. -
    898. 3 this.__dataset = ds;
    899. -
    900. 3 if (ds.db) {
    901. -
    902. 3 this._setDb(ds.db);
    903. +
    904. _setColumnValue:function (name, val) {
    905. +
    906. 7556 var ignore = this.__ignore;
    907. +
    908. 7556 val = this._typeCastValue(name, val, ignore);
    909. +
    910. 7556 this._addColumnToIsChanged(name, val);
    911. +
    912. 7556 var setterFunc = this["_set" + name.charAt(0).toUpperCase() + name.substr(1)];
    913. +
    914. 7556 var columnValue = isFunction(setterFunc) ? setterFunc.call(this, val, ignore) : val;
    915. +
    916. 7556 this.__values[name] = columnValue;
    917. +
    918. 7556 if (this._static.emitOnColumnSet) {
    919. +
    920. 7556 this.emit("column", name, columnValue, ignore);
    921. }
    922. },
    923. -
    924. _setDb:function (db) {
    925. -
    926. 10 this.__db = db;
    927. +
    928. +
    929. //Typecast the value to the column's type if typecasting. Calls the database's
    930. +
    931. //typecast_value method, so database adapters can override/augment the handling
    932. +
    933. //for database specific column types.
    934. +
    935. _typeCastValue:function (column, value, fromDatabase) {
    936. +
    937. 32705 var colSchema, clazz = this._static;
    938. +
    939. 32705 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
    940. +
    941. 32705 var type = colSchema.type;
    942. +
    943. 32705 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
    944. +
    945. 3 value = null;
    946. +
    947. }
    948. +
    949. 32705 var raiseOnError = clazz.raiseOnTypecastError;
    950. +
    951. 32705 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
    952. +
    953. 0 throw new ModelError("null is not allowed for the " + column + " column.");
    954. +
    955. }
    956. +
    957. 32705 try {
    958. +
    959. 32705 value = clazz.db.typecastValue(type, value);
    960. +
    961. } catch (e) {
    962. +
    963. 0 if (raiseOnError === true) {
    964. +
    965. 0 throw e;
    966. +
    967. }
    968. +
    969. }
    970. +
    971. }
    972. +
    973. 32705 return value;
    974. },
    975. -
    976. _setTableName:function (name) {
    977. -
    978. 91 this.__tableName = name;
    979. +
    980. /**
    981. +
    982. * Convert this model to an object, containing column, value pairs.
    983. +
    984. *
    985. +
    986. * @return {Object} the object version of this model.
    987. +
    988. **/
    989. +
    990. toObject:function () {
    991. +
    992. 0 return this._toObject(false);
    993. },
    994. -
    995. _setColumns:function (cols) {
    996. -
    997. 96 var proto = this.prototype;
    998. -
    999. 96 if (this.__columns) {
    1000. -
    1001. 6 this.__columns.forEach(function (name) {
    1002. -
    1003. 16 delete proto[name];
    1004. -
    1005. });
    1006. -
    1007. }
    1008. -
    1009. 96 this.__columns = cols;
    1010. -
    1011. 96 cols.forEach(function (name) {
    1012. -
    1013. 779 this._defineColumnSetter(name);
    1014. -
    1015. 779 this._defineColumnGetter(name);
    1016. -
    1017. }, this);
    1018. +
    1019. /**
    1020. +
    1021. * Convert this model to JSON, containing column, value pairs.
    1022. +
    1023. *
    1024. +
    1025. * @return {JSON} the JSON version of this model.
    1026. +
    1027. **/
    1028. +
    1029. toJSON:function () {
    1030. +
    1031. 0 return this.toObject();
    1032. },
    1033. -
    1034. _setPrimaryKey:function (pks) {
    1035. -
    1036. 99 this.__primaryKey = pks || [];
    1037. +
    1038. /**
    1039. +
    1040. * Convert this model to a string, containing column, value pairs.
    1041. +
    1042. *
    1043. +
    1044. * @return {String} the string version of this model.
    1045. +
    1046. **/
    1047. +
    1048. toString:function () {
    1049. +
    1050. 0 return JSON.stringify(this.toObject(), null, 4);
    1051. },
    1052. -
    1053. _setSchema:function (schema) {
    1054. -
    1055. 96 var columns = [];
    1056. -
    1057. 96 var pks = [];
    1058. -
    1059. 96 for (var i in schema) {
    1060. -
    1061. 779 var col = schema[i];
    1062. -
    1063. 779 var name = applyColumnTransformMethod(i, this.identifierOutputMethod);
    1064. -
    1065. 779 schema[name] = col;
    1066. -
    1067. 779 columns.push(name);
    1068. -
    1069. 779 col.primaryKey && pks.push(name);
    1070. -
    1071. }
    1072. -
    1073. 96 this.__schema = schema;
    1074. -
    1075. 96 this._setPrimaryKey(pks);
    1076. -
    1077. 96 this._setColumns(columns);
    1078. +
    1079. /**
    1080. +
    1081. * Convert this model to a string, containing column, value pairs.
    1082. +
    1083. *
    1084. +
    1085. * @return {String} the string version of this model.
    1086. +
    1087. **/
    1088. +
    1089. valueOf:function () {
    1090. +
    1091. 0 return this.toObject();
    1092. },
    1093. -
    1094. _defineColumnSetter:function (name) {
    1095. -
    1096. /*Adds a setter to an object*/
    1097. -
    1098. 779 this.prototype.__defineSetter__(name, function (val) {
    1099. -
    1100. 7556 this._setColumnValue(name, val);
    1101. -
    1102. });
    1103. +
    1104. _checkTransaction:function (options, cb) {
    1105. +
    1106. 2493 return this._static._checkTransaction(options, cb);
    1107. },
    1108. -
    1109. _defineColumnGetter:function (name) {
    1110. -
    1111. 779 this.prototype.__defineGetter__(name, function () {
    1112. -
    1113. 5427 return this._getColumnValue(name);
    1114. -
    1115. });
    1116. +
    1117. addListener:function () {
    1118. +
    1119. 0 var emitter = this.__emitter;
    1120. +
    1121. 0 return emitter.addListener.apply(emitter, arguments);
    1122. +
    1123. },
    1124. +
    1125. on:function () {
    1126. +
    1127. 6 var emitter = this.__emitter;
    1128. +
    1129. 6 return emitter.on.apply(emitter, arguments);
    1130. +
    1131. },
    1132. +
    1133. once:function () {
    1134. +
    1135. 0 var emitter = this.__emitter;
    1136. +
    1137. 0 return emitter.once.apply(emitter, arguments);
    1138. +
    1139. },
    1140. +
    1141. removeListener:function () {
    1142. +
    1143. 6 var emitter = this.__emitter;
    1144. +
    1145. 6 return emitter.removeListener.apply(emitter, arguments);
    1146. +
    1147. },
    1148. +
    1149. removeAllListeners:function () {
    1150. +
    1151. 0 var emitter = this.__emitter;
    1152. +
    1153. 0 return emitter.removeAllListeners.apply(emitter, arguments);
    1154. +
    1155. },
    1156. +
    1157. setMaxListeners:function () {
    1158. +
    1159. 0 var emitter = this.__emitter;
    1160. +
    1161. 0 return emitter.setMaxListeners.apply(emitter, arguments);
    1162. +
    1163. },
    1164. +
    1165. listeners:function () {
    1166. +
    1167. 0 var emitter = this.__emitter;
    1168. +
    1169. 0 return emitter.listeners.apply(emitter, arguments);
    1170. +
    1171. },
    1172. +
    1173. emit:function () {
    1174. +
    1175. 11120 var emitter = this.__emitter;
    1176. +
    1177. 11120 return emitter.emit.apply(emitter, arguments);
    1178. },
    1179. -
    1180. /**
    1181. -
    1182. * @ignore
    1183. -
    1184. */
    1185. getters:{
    1186. -
    1187. /**@lends patio.Model*/
    1188. -
    1189. -
    1190. /**
    1191. -
    1192. * Set to true if this models column names should be use the "underscore" method when sending
    1193. -
    1194. * keys to the database and to "camelize" method on columns returned from the database. If set to false see
    1195. -
    1196. * {@link patio.Model#underscore}.
    1197. -
    1198. * @field
    1199. -
    1200. * @default false
    1201. -
    1202. * @type {Boolean}
    1203. -
    1204. */
    1205. -
    1206. camelize:function (camelize) {
    1207. -
    1208. 10686 return this.__camelize;
    1209. +
    1210. /**@lends patio.Model.prototype*/
    1211. +
    1212. /*Returns my actual primary key value*/
    1213. +
    1214. primaryKeyValue:function () {
    1215. +
    1216. 47 return this[this.primaryKey];
    1217. },
    1218. -
    1219. /**
    1220. -
    1221. * Set to true if this models column names should be use the "camelize" method when sending
    1222. -
    1223. * keys to the database and to "underscore" method on columns returned from the database. If set to false see
    1224. -
    1225. * {@link patio.Model#underscore}.
    1226. -
    1227. * @field
    1228. -
    1229. * @default false
    1230. -
    1231. * @type {Boolean}
    1232. -
    1233. */
    1234. -
    1235. underscore:function (underscore) {
    1236. -
    1237. 36 return this.__underscore;
    1238. +
    1239. /*Return if Im a new object*/
    1240. +
    1241. isNew:function () {
    1242. +
    1243. 9644 return this.__isNew;
    1244. +
    1245. },
    1246. +
    1247. /*Return if Im changed*/
    1248. +
    1249. isChanged:function () {
    1250. +
    1251. 0 return this.__isChanged;
    1252. },
    1253. -
    1254. /**@lends patio.Model*/
    1255. +
    1256. /**@lends patio.Model.prototype*/
    1257. -
    1258. /**
    1259. -
    1260. * The name of the table all instances of the this {@link patio.Model} use.
    1261. -
    1262. * @field
    1263. -
    1264. * @ignoreCode
    1265. -
    1266. * @type String
    1267. -
    1268. */
    1269. -
    1270. tableName:function () {
    1271. -
    1272. 6318 return this.__tableName;
    1273. +
    1274. primaryKey:function () {
    1275. +
    1276. 2568 return this._static.primaryKey;
    1277. },
    1278. -
    1279. /**
    1280. -
    1281. * The database all instances of this {@link patio.Model} use.
    1282. -
    1283. * @field
    1284. -
    1285. * @ignoreCode
    1286. -
    1287. * @type patio.Database
    1288. -
    1289. */
    1290. -
    1291. db:function () {
    1292. -
    1293. 46374 var db = this.__db;
    1294. -
    1295. 46374 if (!db) {
    1296. -
    1297. 86 db = this.__db = patio.defaultDatabase;
    1298. -
    1299. }
    1300. -
    1301. 46374 if (!db) {
    1302. -
    1303. 0 throw new ModelError("patio has not been connected to a database");
    1304. -
    1305. }
    1306. -
    1307. 46374 return db;
    1308. +
    1309. tableName:function () {
    1310. +
    1311. 40 return this._static.tableName;
    1312. },
    1313. -
    1314. /**
    1315. -
    1316. * A dataset to use to retrieve instances of this {@link patio.Model{ from the database. The dataset
    1317. -
    1318. * has the {@link patio.Dataset#rowCb} set to create instances of this model.
    1319. -
    1320. * @field
    1321. -
    1322. * @ignoreCode
    1323. -
    1324. * @type patio.Dataset
    1325. -
    1326. */
    1327. dataset:function () {
    1328. -
    1329. 4836 var ds = this.__dataset;
    1330. -
    1331. 4836 if (!ds) {
    1332. -
    1333. 84 ds = this.db.from(this.tableName);
    1334. -
    1335. 84 ds.rowCb = hitch(this, function (vals) {
    1336. -
    1337. 873 return this.load(vals);
    1338. -
    1339. });
    1340. -
    1341. 84 this.identifierInputMethod && (ds.identifierInputMethod = this.identifierInputMethod);
    1342. -
    1343. 84 this.identifierOutputMethod && (ds.identifierOutputMethod = this.identifierOutputMethod);
    1344. -
    1345. 84 this.__dataset = ds;
    1346. -
    1347. 4752 } else if (!ds.rowCb) {
    1348. -
    1349. 6 ds.rowCb = hitch(this, function rowCb(vals) {
    1350. -
    1351. 25 return this.load(vals);
    1352. -
    1353. });
    1354. -
    1355. }
    1356. -
    1357. 4836 return ds;
    1358. +
    1359. 3085 return this.__dataset || this._static.dataset;
    1360. },
    1361. -
    1362. /**
    1363. -
    1364. * A list of columns this models table contains.
    1365. -
    1366. * @field
    1367. -
    1368. * @ignoreCode
    1369. -
    1370. * @type String[]
    1371. -
    1372. -
    1373. */
    1374. -
    1375. columns:function () {
    1376. -
    1377. 1128 return this.__columns;
    1378. +
    1379. db:function () {
    1380. +
    1381. 28 return this._static.db;
    1382. },
    1383. -
    1384. /**
    1385. -
    1386. * The schema of this {@link patio.Model}'s table. See {@link patio.Database#schema} for details
    1387. -
    1388. * on the schema object.
    1389. -
    1390. * @field
    1391. -
    1392. * @ignoreCode
    1393. -
    1394. * @type Object
    1395. -
    1396. */
    1397. schema:function () {
    1398. -
    1399. 75103 if (this.synced) {
    1400. -
    1401. 75103 return this.__schema;
    1402. -
    1403. } else {
    1404. -
    1405. 0 throw new ModelError("Model has not been synced yet");
    1406. -
    1407. }
    1408. +
    1409. 75099 return this._static.schema;
    1410. },
    1411. -
    1412. /**
    1413. -
    1414. * The primaryKey column/s of this {@link patio.Model}
    1415. -
    1416. * @field
    1417. -
    1418. * @ignoreCode
    1419. -
    1420. */
    1421. -
    1422. primaryKey:function () {
    1423. -
    1424. 8221 if (this.synced) {
    1425. -
    1426. 8221 return this.__primaryKey.slice(0);
    1427. -
    1428. } else {
    1429. -
    1430. 0 throw new ModelError("Model has not been synced yet");
    1431. -
    1432. }
    1433. +
    1434. columns:function () {
    1435. +
    1436. 0 return this._static.columns;
    1437. },
    1438. -
    1439. /**
    1440. -
    1441. * A reference to the global {@link patio}.
    1442. -
    1443. * @field
    1444. -
    1445. * @ignoreCode
    1446. -
    1447. */
    1448. -
    1449. patio:function () {
    1450. -
    1451. 81 return patio || require("./index");
    1452. +
    1453. synced:function () {
    1454. +
    1455. 12780 return this._static.synced;
    1456. }
    1457. -
    1458. },
    1459. -
    1460. /**@ignore*/
    1461. -
    1462. setters:{
    1463. -
    1464. /**@lends patio.Model*/
    1465. -
    1466. /**@ignore*/
    1467. -
    1468. camelize:function (camelize) {
    1469. -
    1470. 26 camelize = camelize === true;
    1471. -
    1472. 26 if (camelize) {
    1473. -
    1474. 26 this.identifierOutputMethod = "camelize";
    1475. -
    1476. 26 this.identifierInputMethod = "underscore";
    1477. -
    1478. }
    1479. -
    1480. 26 this.__camelize = camelize;
    1481. -
    1482. 26 this.__underscore = !camelize;
    1483. +
    1484. }
    1485. +
    1486. },
    1487. -
    1488. },
    1489. -
    1490. /**@ignore*/
    1491. -
    1492. underscore:function (underscore) {
    1493. -
    1494. 1 underscore = underscore === true;
    1495. -
    1496. 1 if (underscore) {
    1497. -
    1498. 1 this.identifierOutputMethod = "underscore";
    1499. -
    1500. 1 this.identifierInputMethod = "camelize";
    1501. -
    1502. }
    1503. -
    1504. 1 this.__underscore = underscore;
    1505. -
    1506. 1 this.__camelize = !underscore;
    1507. +
    1508. static:{
    1509. +
    1510. /**
    1511. +
    1512. * @lends patio.Model
    1513. +
    1514. */
    1515. -
    1516. }
    1517. -
    1518. }
    1519. -
    1520. }
    1521. +
    1522. synced:false,
    1523. -
    1524. }).as(exports, "Model");
    1525. +
    1526. /**
    1527. +
    1528. * Set to false to prevent the emitting of an event on load
    1529. +
    1530. * @default true
    1531. +
    1532. */
    1533. +
    1534. emitOnLoad:true,
    1535. +
    1536. /**
    1537. +
    1538. * Set to false to prevent the emitting of an event on the setting of a column value
    1539. +
    1540. * @default true
    1541. +
    1542. */
    1543. +
    1544. emitOnColumnSet:true,
    1545. -
    1546. 1function checkAndAddDBToTable(db, table) {
    1547. -
    1548. 93 if (!table.contains(db)) {
    1549. -
    1550. 34 table.set(db, new HashTable());
    1551. -
    1552. }
    1553. -
    1554. }
    1555. -
    1556. /**@ignore*/
    1557. -
    1558. 1exports.create = function (name, supers, modelOptions) {
    1559. -
    1560. 93 if (!patio) {
    1561. -
    1562. 1 (patio = require("./index"));
    1563. -
    1564. 1 patio.on("disconnect", function () {
    1565. -
    1566. 40 MODELS.clear();
    1567. -
    1568. });
    1569. -
    1570. }
    1571. -
    1572. 93 var db, ds, tableName;
    1573. -
    1574. 93 var key, modelKey;
    1575. -
    1576. 93 if (isString(name)) {
    1577. -
    1578. 87 tableName = name;
    1579. -
    1580. 87 key = db = patio.defaultDatabase || "default";
    1581. -
    1582. 6 } else if (isInstanceOf(name, patio.Dataset)) {
    1583. -
    1584. 6 ds = name;
    1585. -
    1586. 6 tableName = ds.firstSourceAlias;
    1587. -
    1588. 6 key = db = ds.db;
    1589. -
    1590. }
    1591. -
    1592. 93 var hasSuper = false;
    1593. -
    1594. 93 if (isHash(supers) || isUndefinedOrNull(supers)) {
    1595. -
    1596. 90 modelOptions = supers;
    1597. -
    1598. 90 supers = [Model];
    1599. -
    1600. } else {
    1601. -
    1602. 3 supers = toArray(supers);
    1603. -
    1604. 3 supers = supers.map(function (sup) {
    1605. -
    1606. 3 return exports.getModel(sup, db);
    1607. -
    1608. });
    1609. -
    1610. 3 hasSuper = true;
    1611. -
    1612. }
    1613. +
    1614. /**
    1615. +
    1616. * Set to false to prevent empty strings from being type casted to null
    1617. +
    1618. * @default true
    1619. +
    1620. */
    1621. +
    1622. typecastEmptyStringToNull:true,
    1623. -
    1624. 93 var model;
    1625. -
    1626. 93 checkAndAddDBToTable(key, MODELS);
    1627. +
    1628. /**
    1629. +
    1630. * Set to false to prevent properties from being type casted when loaded from the database.
    1631. +
    1632. * See {@link patio.Database#typecastValue}
    1633. +
    1634. * @default true
    1635. +
    1636. */
    1637. +
    1638. typecastOnLoad:true,
    1639. -
    1640. 93 var DEFAULT_PROTO = {instance:{}, "static":{}};
    1641. -
    1642. 93 modelOptions = merge(DEFAULT_PROTO, modelOptions || {});
    1643. -
    1644. 93 modelOptions.instance._hooks = ["save", "update", "remove", "load"];
    1645. -
    1646. 93 modelOptions.instance.__hooks = {pre:{}, post:{}};
    1647. -
    1648. //Mixin the column setter/getters
    1649. -
    1650. 93 modelOptions["static"].synced = false;
    1651. -
    1652. 93 modelOptions["static"].__tableName = tableName;
    1653. -
    1654. 93 modelOptions["static"].__db = (db === "default" ? null : db);
    1655. -
    1656. 93 modelOptions["static"].__supers = hasSuper ? supers : [];
    1657. -
    1658. 93 modelOptions["static"].__dataset = ds;
    1659. -
    1660. 93 model = define(supers.concat(modelOptions.plugins || []).concat([AssociationPlugin]), modelOptions);
    1661. -
    1662. 93 ["pre", "post"].forEach(function (op) {
    1663. -
    1664. 186 var optionsOp = modelOptions[op];
    1665. -
    1666. 186 if (optionsOp) {
    1667. -
    1668. 0 for (var i in optionsOp) {
    1669. -
    1670. 0 model[op](i, optionsOp[i]);
    1671. -
    1672. }
    1673. -
    1674. }
    1675. -
    1676. });
    1677. -
    1678. 93 if (!(MODELS.get(key).contains(checkAndTransformName(name)))) {
    1679. -
    1680. 61 MODELS.get(key).set(name, model);
    1681. -
    1682. }
    1683. -
    1684. 93 return model;
    1685. -
    1686. };
    1687. +
    1688. /**
    1689. +
    1690. * Set to false to prevent properties from being type casted when manually set.
    1691. +
    1692. * See {@link patio.Database#typecastValue}
    1693. +
    1694. * @default true
    1695. +
    1696. */
    1697. +
    1698. typecastOnAssignment:true,
    1699. -
    1700. 1exports.syncModels = function (cb) {
    1701. -
    1702. 35 var ret = new Promise();
    1703. -
    1704. 35 serial(MODELS.entrySet.map(function (entry) {
    1705. -
    1706. 34 return function () {
    1707. -
    1708. 34 var value = entry.value;
    1709. -
    1710. 34 return serial(value.entrySet.map(function (m) {
    1711. -
    1712. 61 return hitch(m.value, "sync");
    1713. -
    1714. }));
    1715. -
    1716. };
    1717. -
    1718. })).then(hitchIgnore(ret, "callback", true), ret);
    1719. -
    1720. 35 if (isFunction(cb)) {
    1721. -
    1722. 0 ret.classic(cb);
    1723. -
    1724. }
    1725. -
    1726. 35 return ret.promise();
    1727. -
    1728. };
    1729. +
    1730. /**
    1731. +
    1732. * Set to false to prevent errors thrown while type casting a value from being propogated.
    1733. +
    1734. * @default true
    1735. +
    1736. */
    1737. +
    1738. raiseOnTypecastError:true,
    1739. -
    1740. 1var checkAndGetModel = function (db, name) {
    1741. -
    1742. 21198 var ret;
    1743. -
    1744. 21198 if (MODELS.contains(db)) {
    1745. -
    1746. 10600 ret = MODELS.get(db).get(checkAndTransformName(name));
    1747. -
    1748. }
    1749. -
    1750. 21198 return ret;
    1751. -
    1752. };
    1753. +
    1754. /**
    1755. +
    1756. * Set to false to allow the setting of primary keys.
    1757. +
    1758. * @default false
    1759. +
    1760. */
    1761. +
    1762. isRestrictedPrimaryKey:true,
    1763. +
    1764. /**
    1765. +
    1766. * Set to false to prevent models from using transactions when saving, deleting, or updating.
    1767. +
    1768. * This applies to the model associations also.
    1769. +
    1770. */
    1771. +
    1772. useTransactions:true,
    1773. -
    1774. 1exports.getModel = function (name, db) {
    1775. -
    1776. 10603 var ret = null;
    1777. -
    1778. 10603 if (isDefined(name)) {
    1779. -
    1780. 10603 !patio && (patio = require("./index"));
    1781. -
    1782. 10603 if (isFunction(name)) {
    1783. -
    1784. 3 ret = name;
    1785. -
    1786. } else {
    1787. -
    1788. 10600 if (!db && isInstanceOf(name, patio.Dataset)) {
    1789. -
    1790. 2 db = name.db;
    1791. -
    1792. }
    1793. -
    1794. 10600 var defaultDb = patio.defaultDatabase;
    1795. -
    1796. 10600 if (db) {
    1797. -
    1798. 10574 ret = checkAndGetModel(db, name);
    1799. -
    1800. 10574 if (!ret && db === defaultDb) {
    1801. -
    1802. 10572 ret = checkAndGetModel("default", name);
    1803. -
    1804. }
    1805. -
    1806. } else {
    1807. -
    1808. 26 db = patio.defaultDatabase;
    1809. -
    1810. 26 ret = checkAndGetModel(db, name);
    1811. -
    1812. 26 if (!ret) {
    1813. -
    1814. 26 ret = checkAndGetModel("default", name);
    1815. -
    1816. }
    1817. -
    1818. }
    1819. -
    1820. }
    1821. -
    1822. } else {
    1823. -
    1824. 0 ret = name;
    1825. -
    1826. }
    1827. -
    1828. 10603 if (isUndefinedOrNull(ret)) {
    1829. -
    1830. 0 throw new ModelError("Model " + name + " has not been registered with patio");
    1831. -
    1832. }
    1833. -
    1834. 10603 return ret;
    1835. -
    1836. };
    1837. +
    1838. /**
    1839. +
    1840. * See {@link patio.Dataset#identifierOutputMethod}
    1841. +
    1842. * @default null
    1843. +
    1844. */
    1845. +
    1846. identifierOutputMethod:null,
    1847. -
    -
    - -
    - - - - - -
    -
    associations/manyToMany.js
    -
    -
    - Coverage89.13 - SLOC309 - LOC138 - Missed15 -
    -
    -
    1. 1var comb = require("comb-proxy"),
    2. -
    3. define = comb.define,
    4. -
    5. isUndefined = comb.isUndefined,
    6. -
    7. isUndefinedOrNull = comb.isUndefinedOrNull,
    8. -
    9. isFunction = comb.isFunction,
    10. -
    11. isInstanceOf = comb.isInstanceOf,
    12. -
    13. sql = require("../sql").sql,
    14. -
    15. hitch = comb.hitch,
    16. -
    17. array = comb.array,
    18. -
    19. isBoolean = comb.isBoolean,
    20. -
    21. serial = comb.serial,
    22. -
    23. when = comb.when,
    24. -
    25. zip = array.zip,
    26. -
    27. Promise = comb.Promise,
    28. -
    29. PromiseList = comb.PromiseList,
    30. -
    31. hitchIgnore = comb.hitchIgnore,
    32. -
    33. OneToMany = require("./oneToMany"),
    34. -
    35. pluralize = comb.pluralize,
    36. -
    37. AssociationError = require("../errors").AssociationError;
    38. +
    39. /**
    40. +
    41. * See {@link patio.Dataset#identifierInputMethod}
    42. +
    43. * @default null
    44. +
    45. */
    46. +
    47. identifierInputMethod:null,
    48. -
    49. 1var LOGGER = comb.logger("comb.associations.ManyToMany");
    50. -
    51. /**
    52. -
    53. * @class Class to define a manyToMany association.
    54. -
    55. *
    56. -
    57. * </br>
    58. -
    59. * <b>NOT to be instantiated directly</b>
    60. -
    61. * Its just documented for reference.
    62. -
    63. *
    64. -
    65. * @name ManyToMany
    66. -
    67. * @augments patio.associations.OneToMany
    68. -
    69. * @memberOf patio.associations
    70. -
    71. *
    72. -
    73. * @param {String} options.joinTable the joinTable of the association.
    74. -
    75. *
    76. -
    77. *
    78. -
    79. * @property {String} joinTable the join table used in the relation.
    80. -
    81. * */
    82. -
    83. 1module.exports = define(OneToMany, {
    84. -
    85. instance:{
    86. -
    87. /**@lends patio.associations.ManyToMany.prototype*/
    88. -
    89. type:"manyToMany",
    90. +
    91. /**
    92. +
    93. * Set to false to prevent the reload of a model after saving.
    94. +
    95. * @default true
    96. +
    97. */
    98. +
    99. reloadOnSave:true,
    100. -
    101. _fetchMethod:"all",
    102. +
    103. /**
    104. +
    105. * Columns that should be restriced when setting values through the {@link patio.Model#set} method.
    106. +
    107. *
    108. +
    109. */
    110. +
    111. restrictedColumns:null,
    112. -
    113. supportsStringKey:false,
    114. +
    115. /**
    116. +
    117. * Set to false to prevent the reload of a model after updating.
    118. +
    119. * @default true
    120. +
    121. */
    122. +
    123. reloadOnUpdate:true,
    124. -
    125. supportsCompositeKey:false,
    126. +
    127. __camelize:false,
    128. +
    129. +
    130. __underscore:false,
    131. -
    132. _filter:function (parent) {
    133. -
    134. 326 var keys = this._getAssociationKey(parent);
    135. -
    136. 326 var options = this.__opts || {};
    137. -
    138. 326 var ds;
    139. -
    140. 326 if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {
    141. -
    142. 0 ds = ds.apply(parent, [parent]);
    143. -
    144. }
    145. -
    146. 326 if (!ds) {
    147. -
    148. 326 ds = this.model.dataset;
    149. -
    150. 326 ds = ds.select(ds.firstSourceAlias.all()).naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {
    151. -
    152. 326 return sql.stringToIdentifier(k);
    153. -
    154. })).concat(zip(keys[0], this.parentPrimaryKey.map(function (k) {
    155. -
    156. 326 return parent[k];
    157. -
    158. }))));
    159. -
    160. 326 var recip = this.model._findAssociation(this);
    161. -
    162. 326 if (recip) {
    163. -
    164. 326 recip = recip[1];
    165. -
    166. }
    167. -
    168. 326 ds.rowCb = hitch(this, function (item) {
    169. -
    170. 340 var ret = new Promise();
    171. -
    172. 340 var model = this._toModel(item, true);
    173. -
    174. 340 if (recip) {
    175. -
    176. 340 recip.__setValue(model, parent);
    177. -
    178. }
    179. -
    180. //call hook to finish other model associations
    181. -
    182. 340 model._hook("post", "load").then(hitch(this, function () {
    183. -
    184. 340 ret.callback(model);
    185. -
    186. }), ret);
    187. -
    188. 340 return ret.promise();
    189. -
    190. });
    191. +
    192. __columns:null,
    193. -
    194. }
    195. +
    196. __schema:null,
    197. -
    198. 326 return this._setDatasetOptions(ds);
    199. -
    200. },
    201. +
    202. __primaryKey:null,
    203. -
    204. _setAssociationKeys:function (parent, model, val) {
    205. -
    206. 534 var keys = this._getAssociationKey(parent),
    207. -
    208. leftKey = keys[0],
    209. -
    210. parentPk = this.parentPrimaryKey;
    211. -
    212. 534 if (!(leftKey && leftKey.length === parentPk.length)) {
    213. -
    214. 0 throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);
    215. +
    216. __dataset:null,
    217. +
    218. +
    219. __db:null,
    220. +
    221. +
    222. __tableName:null,
    223. +
    224. +
    225. +
    226. /**
    227. +
    228. * The table that this Model represents.
    229. +
    230. * <b>READ ONLY</b>
    231. +
    232. */
    233. +
    234. table:null,
    235. +
    236. +
    237. /**
    238. +
    239. * patio - read only
    240. +
    241. *
    242. +
    243. * @type patio
    244. +
    245. */
    246. +
    247. patio:null,
    248. +
    249. +
    250. init:function () {
    251. +
    252. 92 var emitter = new EventEmitter();
    253. +
    254. 92 ["addListener", "on", "once", "removeListener",
    255. +
    256. "removeAllListeners", "setMaxListeners", "listeners", "emit"].forEach(function (name) {
    257. +
    258. 736 this[name] = emitter[name].bind(emitter);
    259. +
    260. }, this);
    261. +
    262. 92 if (this.__tableName) {
    263. +
    264. 91 this._setTableName(this.__tableName);
    265. }
    266. -
    267. 534 for (var i = 0; i < leftKey.length; i++) {
    268. -
    269. 534 model[leftKey[i]] = !isUndefined(val) ? val : parent[parentPk[i]];
    270. +
    271. 92 if (this.__db) {
    272. +
    273. 7 this._setDb(this.__db);
    274. }
    275. },
    276. -
    277. __createJoinTableInsertRemoveQuery:function (model, item) {
    278. -
    279. 176 var q = {};
    280. -
    281. 176 var keys = this._getAssociationKey(model),
    282. -
    283. leftKey = keys[0],
    284. -
    285. rightKey = keys[1],
    286. -
    287. parentPk = this.parentPrimaryKey,
    288. -
    289. modelPk = this.modelPrimaryKey;
    290. -
    291. 176 if (!(leftKey && leftKey.length === parentPk.length)) {
    292. -
    293. 0 throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);
    294. -
    295. }
    296. -
    297. 176 if (!(rightKey && rightKey.length === modelPk.length)) {
    298. -
    299. 0 throw new AssociationError("Invalid rightKey for " + this.name + " : " + rightKey);
    300. -
    301. }
    302. -
    303. 176 for (var i = 0; i < leftKey.length; i++) {
    304. -
    305. 176 q[leftKey[i]] = model[parentPk[i]];
    306. +
    307. sync:function (cb) {
    308. +
    309. 3968 var ret = new Promise();
    310. +
    311. 3968 if (!this.synced) {
    312. +
    313. 93 var db = this.db, tableName = this.tableName, supers = this.__supers;
    314. +
    315. 93 db.schema(tableName).then(hitch(this, function (schema) {
    316. +
    317. 93 if (!this.synced && schema) {
    318. +
    319. 93 this._setSchema(schema);
    320. +
    321. 93 if (supers && supers.length) {
    322. +
    323. 3 new PromiseList(supers.map(function (sup) {
    324. +
    325. 3 return sup.sync();
    326. +
    327. })).then(hitch(this, function () {
    328. +
    329. 3 this.synced = true;
    330. +
    331. 3 supers.forEach(this.inherits, this);
    332. +
    333. 3 ret.callback(this);
    334. +
    335. }), ret);
    336. +
    337. } else {
    338. +
    339. 90 this.synced = true;
    340. +
    341. 90 ret.callback(this);
    342. +
    343. }
    344. +
    345. 93 this.emit("sync", this);
    346. +
    347. } else {
    348. +
    349. 0 var error = new ModelError("Unable to find schema for " + tableName);
    350. +
    351. 0 this.emit("error", error);
    352. +
    353. 0 ret.errback(error);
    354. +
    355. }
    356. +
    357. }), ret);
    358. +
    359. } else {
    360. +
    361. 3875 ret.callback(this);
    362. }
    363. -
    364. 176 for (i = 0; i < rightKey.length; i++) {
    365. -
    366. 176 q[rightKey[i]] = item[modelPk[i]];
    367. +
    368. 3968 if (isFunction(cb)) {
    369. +
    370. 0 ret.classic(cb);
    371. }
    372. -
    373. 176 return q;
    374. +
    375. 3968 return ret.promise();
    376. },
    377. -
    378. _preRemove:function (next, model) {
    379. -
    380. 202 if (this.isOwner && !this.isCascading) {
    381. -
    382. 202 var q = {};
    383. -
    384. 202 this._setAssociationKeys(model, q);
    385. -
    386. 202 this.joinTable.where(q).remove().classic(next);
    387. -
    388. } else {
    389. -
    390. 0 next();
    391. -
    392. }
    393. +
    394. /**
    395. +
    396. * Stub for plugins to notified of model inheritance
    397. +
    398. *
    399. +
    400. * @param {patio.Model} model a model class to inherit from
    401. +
    402. */
    403. +
    404. inherits:function (model) {
    405. },
    406. -
    407. addAssociation:function (item, model, reload) {
    408. -
    409. 260 reload = isBoolean(reload) ? reload : false;
    410. -
    411. 260 var ret = new Promise().callback(model);
    412. -
    413. 260 if (!isUndefinedOrNull(item)) {
    414. -
    415. 260 if (!model.isNew) {
    416. -
    417. 148 item = this._toModel(item);
    418. -
    419. 148 var loaded = this.associationLoaded(model);
    420. -
    421. 148 var recip = this.model._findAssociation(this), save = item.isNew;
    422. -
    423. 148 ret = model._checkTransaction(hitch(this, function () {
    424. -
    425. 148 var joinTable = this.joinTable, ret = new Promise();
    426. -
    427. 148 serial([
    428. -
    429. function () {
    430. -
    431. 148 return save ? item.save() : null;
    432. -
    433. },
    434. -
    435. function () {
    436. -
    437. 148 return joinTable.insert(this.__createJoinTableInsertRemoveQuery(model, item));
    438. -
    439. }.bind(this),
    440. -
    441. function () {
    442. -
    443. 148 if (recip) {
    444. -
    445. 148 recip[1].__setValue(item, [model]);
    446. -
    447. }
    448. -
    449. },
    450. -
    451. function () {
    452. -
    453. 148 if (loaded && reload) {
    454. -
    455. 3 return this.parent._reloadAssociationsForType(this.type, this.model, model);
    456. -
    457. } else {
    458. -
    459. 145 return model;
    460. -
    461. }
    462. -
    463. }.bind(this)
    464. -
    465. ]).then(hitchIgnore(ret, "callback", model), ret);
    466. -
    467. 148 return ret.promise();
    468. -
    469. }));
    470. -
    471. } else {
    472. -
    473. 112 item = this._toModel(item);
    474. -
    475. 112 var items = this.getAssociation(model);
    476. -
    477. 112 if (isUndefinedOrNull(items)) {
    478. -
    479. 39 this.__setValue(model, [item]);
    480. -
    481. } else {
    482. -
    483. 73 items.push(item);
    484. -
    485. }
    486. -
    487. }
    488. -
    489. }
    490. -
    491. 260 return ret.promise();
    492. +
    493. /**
    494. +
    495. * Create a new model initialized with the specified values.
    496. +
    497. *
    498. +
    499. * @param {Object} values the values to initialize the model with.
    500. +
    501. *
    502. +
    503. * @returns {Model} instantiated model initialized with the values passed in.
    504. +
    505. */
    506. +
    507. create:function (values) {
    508. +
    509. //load an object from an object
    510. +
    511. 0 return new this(values, false);
    512. },
    513. -
    514. removeItem:function (item, model, remove, reload) {
    515. -
    516. 56 reload = isBoolean(reload) ? reload : false;
    517. -
    518. 56 remove = isBoolean(remove) ? remove : false;
    519. -
    520. 56 var ret = new Promise().callback(model);
    521. -
    522. 56 if (!isUndefinedOrNull(item)) {
    523. -
    524. 56 if (!model.isNew) {
    525. -
    526. 56 if (isInstanceOf(item, this.model) && !item.isNew) {
    527. -
    528. 56 var loaded = this.associationLoaded(model);
    529. -
    530. 56 remove = remove && !item.isNew;
    531. -
    532. 56 ret = new Promise();
    533. -
    534. 56 model._checkTransaction(hitch(this, function () {
    535. -
    536. 56 return remove ? item.remove() : this.joinTable.where(this.__createJoinTableInsertRemoveQuery(model, item)).remove();
    537. -
    538. })).then(hitch(this, function () {
    539. -
    540. 56 if (loaded && reload) {
    541. -
    542. 18 return this.parent._reloadAssociationsForType(this.type, this.model, model).then(ret);
    543. -
    544. } else {
    545. -
    546. 38 ret.callback(model);
    547. -
    548. }
    549. -
    550. }), ret);
    551. -
    552. 56 return ret.promise();
    553. -
    554. }
    555. +
    556. load:function (vals) {
    557. +
    558. 924 var ret = new Promise();
    559. +
    560. //sync our model
    561. +
    562. 924 this.sync().then(hitch(this, function () {
    563. +
    564. 924 var m = new this(vals, true);
    565. +
    566. //call the hooks!
    567. +
    568. 924 m._hook("post", "load").then(function () {
    569. +
    570. 924 ret.callback(m);
    571. +
    572. }, ret);
    573. +
    574. }), ret);
    575. +
    576. 924 return ret.promise();
    577. +
    578. },
    579. +
    580. +
    581. _checkTransaction:function (opts, cb) {
    582. +
    583. 2892 if (isFunction(opts)) {
    584. +
    585. 639 cb = opts;
    586. +
    587. 639 opts = {};
    588. +
    589. } else {
    590. +
    591. 2253 opts = opts || {};
    592. +
    593. }
    594. +
    595. 2892 var ret = new Promise(), retVal = null, errored = false;
    596. +
    597. 2892 this.sync().then(hitch(this, function () {
    598. +
    599. 2892 if (this.useTransaction(opts)) {
    600. +
    601. 2892 this.db.transaction(opts, hitch(this, function () {
    602. +
    603. 2892 return when(cb()).then(function () {
    604. +
    605. 2848 retVal = argsToArray(arguments);
    606. +
    607. }, function () {
    608. +
    609. 44 retVal = argsToArray(arguments);
    610. +
    611. 44 errored = true;
    612. +
    613. });
    614. +
    615. })).then(hitch(this, function () {
    616. +
    617. 2848 ret[errored ? "errback" : "callback"].apply(ret, retVal);
    618. +
    619. }), hitch(this, function () {
    620. +
    621. 44 if (errored) {
    622. +
    623. 44 ret.errback.apply(ret, retVal);
    624. +
    625. } else {
    626. +
    627. 0 ret.errback.apply(ret, arguments);
    628. +
    629. }
    630. +
    631. }));
    632. } else {
    633. -
    634. 0 item = this._toModel(item);
    635. -
    636. 0 var items = this.getAssociation(model), index;
    637. -
    638. 0 if (!isUndefinedOrNull(items) && (index = items.indexOf(item)) !== -1) {
    639. -
    640. 0 items.splice(index, 1);
    641. -
    642. }
    643. +
    644. 0 when(cb()).then(ret);
    645. }
    646. +
    647. }), ret);
    648. +
    649. 2892 return ret.promise();
    650. +
    651. },
    652. +
    653. +
    654. /**
    655. +
    656. * @private
    657. +
    658. * Returns a boolean indicating whether or not to use a transaction.
    659. +
    660. * @param {Object} [opts] set a transaction property to override the {@link patio.Model#useTransaction}.
    661. +
    662. */
    663. +
    664. useTransaction:function (opts) {
    665. +
    666. 2892 opts = opts || {};
    667. +
    668. 2892 return isBoolean(opts.transaction) ? opts.transaction === true : this.useTransactions === true;
    669. +
    670. },
    671. +
    672. +
    673. _setDataset:function (ds) {
    674. +
    675. 3 this.__dataset = ds;
    676. +
    677. 3 if (ds.db) {
    678. +
    679. 3 this._setDb(ds.db);
    680. }
    681. -
    682. 0 return ret.promise();
    683. },
    684. -
    685. removeAllItems:function (model, remove) {
    686. -
    687. 4 remove = isBoolean(remove) ? remove : false;
    688. -
    689. 4 var ret = new Promise();
    690. -
    691. 4 if (!model.isNew) {
    692. -
    693. 4 var q = {}, removeQ = {};
    694. -
    695. 4 this._setAssociationKeys(model, q);
    696. -
    697. 4 this._setAssociationKeys(model, removeQ, null);
    698. -
    699. 4 var loaded = this.associationLoaded(model);
    700. -
    701. 4 return model._checkTransaction(hitch(this, function () {
    702. -
    703. 4 var ds = this.model.dataset, ret = new Promise();
    704. -
    705. 4 when(remove ? this._filter(model).forEach(function (m) {
    706. -
    707. 6 return m.remove();
    708. -
    709. }) : this.joinTable.filter(q).update(removeQ)).then(function () {
    710. -
    711. 4 if (loaded) {
    712. -
    713. 2 this.parent._reloadAssociationsForType(this.type, this.model, model)
    714. -
    715. .then(hitchIgnore(ret, "callback", model), ret);
    716. -
    717. } else {
    718. -
    719. 2 ret.callback(model);
    720. -
    721. }
    722. -
    723. }.bind(this), ret);
    724. -
    725. 4 return ret.promise();
    726. -
    727. }));
    728. -
    729. } else {
    730. -
    731. //todo we may want to check if any of the items were previously saved items;
    732. -
    733. 0 this._clearAssociations(model);
    734. -
    735. 0 ret.callback(model);
    736. +
    737. _setDb:function (db) {
    738. +
    739. 10 this.__db = db;
    740. +
    741. },
    742. +
    743. +
    744. _setTableName:function (name) {
    745. +
    746. 91 this.__tableName = name;
    747. +
    748. },
    749. +
    750. +
    751. _setColumns:function (cols) {
    752. +
    753. 96 var proto = this.prototype;
    754. +
    755. 96 if (this.__columns) {
    756. +
    757. 6 this.__columns.forEach(function (name) {
    758. +
    759. 16 delete proto[name];
    760. +
    761. });
    762. }
    763. -
    764. 0 return ret.promise();
    765. +
    766. 96 this.__columns = cols;
    767. +
    768. 96 cols.forEach(function (name) {
    769. +
    770. 779 this._defineColumnSetter(name);
    771. +
    772. 779 this._defineColumnGetter(name);
    773. +
    774. }, this);
    775. +
    776. },
    777. +
    778. +
    779. _setPrimaryKey:function (pks) {
    780. +
    781. 99 this.__primaryKey = pks || [];
    782. +
    783. },
    784. +
    785. +
    786. _setSchema:function (schema) {
    787. +
    788. 96 var columns = [];
    789. +
    790. 96 var pks = [];
    791. +
    792. 96 for (var i in schema) {
    793. +
    794. 779 var col = schema[i];
    795. +
    796. 779 var name = applyColumnTransformMethod(i, this.identifierOutputMethod);
    797. +
    798. 779 schema[name] = col;
    799. +
    800. 779 columns.push(name);
    801. +
    802. 779 col.primaryKey && pks.push(name);
    803. +
    804. }
    805. +
    806. 96 this.__schema = schema;
    807. +
    808. 96 this._setPrimaryKey(pks);
    809. +
    810. 96 this._setColumns(columns);
    811. +
    812. },
    813. +
    814. +
    815. _defineColumnSetter:function (name) {
    816. +
    817. /*Adds a setter to an object*/
    818. +
    819. 779 this.prototype.__defineSetter__(name, function (val) {
    820. +
    821. 7556 this._setColumnValue(name, val);
    822. +
    823. });
    824. +
    825. },
    826. +
    827. +
    828. _defineColumnGetter:function (name) {
    829. +
    830. 779 this.prototype.__defineGetter__(name, function () {
    831. +
    832. 5427 return this._getColumnValue(name);
    833. +
    834. });
    835. },
    836. +
    837. /**
    838. +
    839. * @ignore
    840. +
    841. */
    842. getters:{
    843. +
    844. /**@lends patio.Model*/
    845. -
    846. select:function () {
    847. -
    848. 326 return this.__select;
    849. -
    850. },
    851. +
    852. /**
    853. +
    854. * Set to true if this models column names should be use the "underscore" method when sending
    855. +
    856. * keys to the database and to "camelize" method on columns returned from the database. If set to false see
    857. +
    858. * {@link patio.Model#underscore}.
    859. +
    860. * @field
    861. +
    862. * @default false
    863. +
    864. * @type {Boolean}
    865. +
    866. */
    867. +
    868. camelize:function (camelize) {
    869. +
    870. 10686 return this.__camelize;
    871. -
    872. defaultLeftKey:function () {
    873. -
    874. 1740 return this.__opts.leftKey || this.parent.tableName + "Id";
    875. },
    876. -
    877. defaultRightKey:function () {
    878. -
    879. 1740 return this.__opts.rightKey || this.model.tableName + "Id";
    880. -
    881. },
    882. +
    883. /**
    884. +
    885. * Set to true if this models column names should be use the "camelize" method when sending
    886. +
    887. * keys to the database and to "underscore" method on columns returned from the database. If set to false see
    888. +
    889. * {@link patio.Model#underscore}.
    890. +
    891. * @field
    892. +
    893. * @default false
    894. +
    895. * @type {Boolean}
    896. +
    897. */
    898. +
    899. underscore:function (underscore) {
    900. +
    901. 36 return this.__underscore;
    902. -
    903. parentPrimaryKey:function () {
    904. -
    905. 1036 return this.__opts.leftPrimaryKey || this.parent.primaryKey;
    906. },
    907. -
    908. modelPrimaryKey:function () {
    909. -
    910. 502 return this.__opts.rightPrimaryKey || this.model.primaryKey;
    911. -
    912. },
    913. +
    914. /**@lends patio.Model*/
    915. -
    916. joinTableName:function () {
    917. -
    918. 344 if (!this._joinTable) {
    919. -
    920. 18 var options = this.__opts;
    921. -
    922. 18 var joinTable = options.joinTable;
    923. -
    924. 18 if (isUndefined(joinTable)) {
    925. -
    926. 18 var defaultJoinTable = this.defaultJoinTable;
    927. -
    928. 18 if (isUndefined(defaultJoinTable)) {
    929. -
    930. 0 throw new Error("Unable to determine jointable for " + this.name);
    931. -
    932. } else {
    933. -
    934. 18 this._joinTable = defaultJoinTable;
    935. -
    936. }
    937. -
    938. } else {
    939. -
    940. 0 this._joinTable = joinTable;
    941. -
    942. }
    943. -
    944. }
    945. -
    946. 344 return this._joinTable;
    947. +
    948. /**
    949. +
    950. * The name of the table all instances of the this {@link patio.Model} use.
    951. +
    952. * @field
    953. +
    954. * @ignoreCode
    955. +
    956. * @type String
    957. +
    958. */
    959. +
    960. tableName:function () {
    961. +
    962. 6318 return this.__tableName;
    963. },
    964. -
    965. //returns our join table model
    966. -
    967. joinTable:function () {
    968. -
    969. 380 if (!this.__joinTableDataset) {
    970. -
    971. 18 var ds = this.__joinTableDataset = this.model.dataset.db.from(this.joinTableName), model = this.model, options = this.__opts;
    972. -
    973. 18 var identifierInputMethod = isUndefined(options.identifierInputMethod) ? model.identifierInputMethod : options.identifierInputMethod,
    974. -
    975. identifierOutputMethod = isUndefined(options.identifierOutputMethod) ? model.identifierOutputMethod : options.identifierOutputMethod;
    976. -
    977. 18 if (identifierInputMethod) {
    978. -
    979. 14 ds.identifierInputMethod = identifierInputMethod;
    980. -
    981. }
    982. -
    983. 18 if (identifierOutputMethod) {
    984. -
    985. 14 ds.identifierOutputMethod = identifierOutputMethod;
    986. -
    987. }
    988. +
    989. /**
    990. +
    991. * The database all instances of this {@link patio.Model} use.
    992. +
    993. * @field
    994. +
    995. * @ignoreCode
    996. +
    997. * @type patio.Database
    998. +
    999. */
    1000. +
    1001. db:function () {
    1002. +
    1003. 46374 var db = this.__db;
    1004. +
    1005. 46374 if (!db) {
    1006. +
    1007. 86 db = this.__db = patio.defaultDatabase;
    1008. }
    1009. -
    1010. 380 return this.__joinTableDataset;
    1011. +
    1012. 46374 if (!db) {
    1013. +
    1014. 0 throw new ModelError("patio has not been connected to a database");
    1015. +
    1016. }
    1017. +
    1018. 46374 return db;
    1019. },
    1020. -
    1021. defaultJoinTable:function () {
    1022. -
    1023. 18 var ret;
    1024. -
    1025. 18 var recip = this.model._findAssociation(this);
    1026. -
    1027. 18 if (recip && recip.length) {
    1028. -
    1029. 18 var names = [pluralize(this._model), pluralize(recip[1]._model)].sort();
    1030. -
    1031. 18 names[1] = names[1].charAt(0).toUpperCase() + names[1].substr(1);
    1032. -
    1033. 18 ret = names.join("");
    1034. +
    1035. /**
    1036. +
    1037. * A dataset to use to retrieve instances of this {@link patio.Model{ from the database. The dataset
    1038. +
    1039. * has the {@link patio.Dataset#rowCb} set to create instances of this model.
    1040. +
    1041. * @field
    1042. +
    1043. * @ignoreCode
    1044. +
    1045. * @type patio.Dataset
    1046. +
    1047. */
    1048. +
    1049. dataset:function () {
    1050. +
    1051. 4836 var ds = this.__dataset;
    1052. +
    1053. 4836 if (!ds) {
    1054. +
    1055. 84 ds = this.db.from(this.tableName);
    1056. +
    1057. 84 ds.rowCb = hitch(this, function (vals) {
    1058. +
    1059. 873 return this.load(vals);
    1060. +
    1061. });
    1062. +
    1063. 84 this.identifierInputMethod && (ds.identifierInputMethod = this.identifierInputMethod);
    1064. +
    1065. 84 this.identifierOutputMethod && (ds.identifierOutputMethod = this.identifierOutputMethod);
    1066. +
    1067. 84 this.__dataset = ds;
    1068. +
    1069. 4752 } else if (!ds.rowCb) {
    1070. +
    1071. 6 ds.rowCb = hitch(this, function rowCb(vals) {
    1072. +
    1073. 25 return this.load(vals);
    1074. +
    1075. });
    1076. }
    1077. -
    1078. 18 return ret;
    1079. +
    1080. 4836 return ds;
    1081. +
    1082. },
    1083. +
    1084. +
    1085. /**
    1086. +
    1087. * A list of columns this models table contains.
    1088. +
    1089. * @field
    1090. +
    1091. * @ignoreCode
    1092. +
    1093. * @type String[]
    1094. +
    1095. +
    1096. */
    1097. +
    1098. columns:function () {
    1099. +
    1100. 1128 return this.__columns;
    1101. +
    1102. },
    1103. +
    1104. +
    1105. /**
    1106. +
    1107. * The schema of this {@link patio.Model}'s table. See {@link patio.Database#schema} for details
    1108. +
    1109. * on the schema object.
    1110. +
    1111. * @field
    1112. +
    1113. * @ignoreCode
    1114. +
    1115. * @type Object
    1116. +
    1117. */
    1118. +
    1119. schema:function () {
    1120. +
    1121. 75103 if (this.synced) {
    1122. +
    1123. 75103 return this.__schema;
    1124. +
    1125. } else {
    1126. +
    1127. 0 throw new ModelError("Model has not been synced yet");
    1128. +
    1129. }
    1130. +
    1131. },
    1132. +
    1133. +
    1134. /**
    1135. +
    1136. * The primaryKey column/s of this {@link patio.Model}
    1137. +
    1138. * @field
    1139. +
    1140. * @ignoreCode
    1141. +
    1142. */
    1143. +
    1144. primaryKey:function () {
    1145. +
    1146. 8221 if (this.synced) {
    1147. +
    1148. 8221 return this.__primaryKey.slice(0);
    1149. +
    1150. } else {
    1151. +
    1152. 0 throw new ModelError("Model has not been synced yet");
    1153. +
    1154. }
    1155. +
    1156. },
    1157. +
    1158. +
    1159. /**
    1160. +
    1161. * A reference to the global {@link patio}.
    1162. +
    1163. * @field
    1164. +
    1165. * @ignoreCode
    1166. +
    1167. */
    1168. +
    1169. patio:function () {
    1170. +
    1171. 81 return patio || require("./index");
    1172. +
    1173. }
    1174. +
    1175. },
    1176. +
    1177. +
    1178. /**@ignore*/
    1179. +
    1180. setters:{
    1181. +
    1182. /**@lends patio.Model*/
    1183. +
    1184. /**@ignore*/
    1185. +
    1186. camelize:function (camelize) {
    1187. +
    1188. 26 camelize = camelize === true;
    1189. +
    1190. 26 if (camelize) {
    1191. +
    1192. 26 this.identifierOutputMethod = "camelize";
    1193. +
    1194. 26 this.identifierInputMethod = "underscore";
    1195. +
    1196. }
    1197. +
    1198. 26 this.__camelize = camelize;
    1199. +
    1200. 26 this.__underscore = !camelize;
    1201. +
    1202. +
    1203. },
    1204. +
    1205. /**@ignore*/
    1206. +
    1207. underscore:function (underscore) {
    1208. +
    1209. 1 underscore = underscore === true;
    1210. +
    1211. 1 if (underscore) {
    1212. +
    1213. 1 this.identifierOutputMethod = "underscore";
    1214. +
    1215. 1 this.identifierInputMethod = "camelize";
    1216. +
    1217. }
    1218. +
    1219. 1 this.__underscore = underscore;
    1220. +
    1221. 1 this.__camelize = !underscore;
    1222. +
    1223. }
    1224. }
    1225. }
    1226. -
    1227. });
    1228. +
    1229. +
    1230. }).as(exports, "Model");
    1231. +
    1232. +
    1233. +
    1234. 1function checkAndAddDBToTable(db, table) {
    1235. +
    1236. 93 if (!table.contains(db)) {
    1237. +
    1238. 34 table.set(db, new HashTable());
    1239. +
    1240. }
    1241. +
    1242. }
    1243. +
    1244. +
    1245. /**@ignore*/
    1246. +
    1247. 1exports.create = function (name, supers, modelOptions) {
    1248. +
    1249. 93 if (!patio) {
    1250. +
    1251. 1 (patio = require("./index"));
    1252. +
    1253. 1 patio.on("disconnect", function () {
    1254. +
    1255. 40 MODELS.clear();
    1256. +
    1257. });
    1258. +
    1259. }
    1260. +
    1261. 93 var db, ds, tableName;
    1262. +
    1263. 93 var key, modelKey;
    1264. +
    1265. 93 if (isString(name)) {
    1266. +
    1267. 87 tableName = name;
    1268. +
    1269. 87 key = db = patio.defaultDatabase || "default";
    1270. +
    1271. 6 } else if (isInstanceOf(name, patio.Dataset)) {
    1272. +
    1273. 6 ds = name;
    1274. +
    1275. 6 tableName = ds.firstSourceAlias;
    1276. +
    1277. 6 key = db = ds.db;
    1278. +
    1279. }
    1280. +
    1281. 93 var hasSuper = false;
    1282. +
    1283. 93 if (isHash(supers) || isUndefinedOrNull(supers)) {
    1284. +
    1285. 90 modelOptions = supers;
    1286. +
    1287. 90 supers = [Model];
    1288. +
    1289. } else {
    1290. +
    1291. 3 supers = toArray(supers);
    1292. +
    1293. 3 supers = supers.map(function (sup) {
    1294. +
    1295. 3 return exports.getModel(sup, db);
    1296. +
    1297. });
    1298. +
    1299. 3 hasSuper = true;
    1300. +
    1301. }
    1302. +
    1303. +
    1304. 93 var model;
    1305. +
    1306. 93 checkAndAddDBToTable(key, MODELS);
    1307. +
    1308. +
    1309. 93 var DEFAULT_PROTO = {instance:{}, "static":{}};
    1310. +
    1311. 93 modelOptions = merge(DEFAULT_PROTO, modelOptions || {});
    1312. +
    1313. 93 modelOptions.instance._hooks = ["save", "update", "remove", "load"];
    1314. +
    1315. 93 modelOptions.instance.__hooks = {pre:{}, post:{}};
    1316. +
    1317. //Mixin the column setter/getters
    1318. +
    1319. 93 modelOptions["static"].synced = false;
    1320. +
    1321. 93 modelOptions["static"].__tableName = tableName;
    1322. +
    1323. 93 modelOptions["static"].__db = (db === "default" ? null : db);
    1324. +
    1325. 93 modelOptions["static"].__supers = hasSuper ? supers : [];
    1326. +
    1327. 93 modelOptions["static"].__dataset = ds;
    1328. +
    1329. 93 model = define(supers.concat(modelOptions.plugins || []).concat([AssociationPlugin]), modelOptions);
    1330. +
    1331. 93 ["pre", "post"].forEach(function (op) {
    1332. +
    1333. 186 var optionsOp = modelOptions[op];
    1334. +
    1335. 186 if (optionsOp) {
    1336. +
    1337. 0 for (var i in optionsOp) {
    1338. +
    1339. 0 model[op](i, optionsOp[i]);
    1340. +
    1341. }
    1342. +
    1343. }
    1344. +
    1345. });
    1346. +
    1347. 93 if (!(MODELS.get(key).contains(checkAndTransformName(name)))) {
    1348. +
    1349. 61 MODELS.get(key).set(name, model);
    1350. +
    1351. }
    1352. +
    1353. 93 return model;
    1354. +
    1355. };
    1356. +
    1357. +
    1358. 1exports.syncModels = function (cb) {
    1359. +
    1360. 35 var ret = new Promise();
    1361. +
    1362. 35 serial(MODELS.entrySet.map(function (entry) {
    1363. +
    1364. 34 return function () {
    1365. +
    1366. 34 var value = entry.value;
    1367. +
    1368. 34 return serial(value.entrySet.map(function (m) {
    1369. +
    1370. 61 return hitch(m.value, "sync");
    1371. +
    1372. }));
    1373. +
    1374. };
    1375. +
    1376. })).then(hitchIgnore(ret, "callback", true), ret);
    1377. +
    1378. 35 if (isFunction(cb)) {
    1379. +
    1380. 0 ret.classic(cb);
    1381. +
    1382. }
    1383. +
    1384. 35 return ret.promise();
    1385. +
    1386. };
    1387. +
    1388. +
    1389. 1var checkAndGetModel = function (db, name) {
    1390. +
    1391. 21198 var ret;
    1392. +
    1393. 21198 if (MODELS.contains(db)) {
    1394. +
    1395. 10600 ret = MODELS.get(db).get(checkAndTransformName(name));
    1396. +
    1397. }
    1398. +
    1399. 21198 return ret;
    1400. +
    1401. };
    1402. +
    1403. +
    1404. +
    1405. 1exports.getModel = function (name, db) {
    1406. +
    1407. 10603 var ret = null;
    1408. +
    1409. 10603 if (isDefined(name)) {
    1410. +
    1411. 10603 !patio && (patio = require("./index"));
    1412. +
    1413. 10603 if (isFunction(name)) {
    1414. +
    1415. 3 ret = name;
    1416. +
    1417. } else {
    1418. +
    1419. 10600 if (!db && isInstanceOf(name, patio.Dataset)) {
    1420. +
    1421. 2 db = name.db;
    1422. +
    1423. }
    1424. +
    1425. 10600 var defaultDb = patio.defaultDatabase;
    1426. +
    1427. 10600 if (db) {
    1428. +
    1429. 10574 ret = checkAndGetModel(db, name);
    1430. +
    1431. 10574 if (!ret && db === defaultDb) {
    1432. +
    1433. 10572 ret = checkAndGetModel("default", name);
    1434. +
    1435. }
    1436. +
    1437. } else {
    1438. +
    1439. 26 db = patio.defaultDatabase;
    1440. +
    1441. 26 ret = checkAndGetModel(db, name);
    1442. +
    1443. 26 if (!ret) {
    1444. +
    1445. 26 ret = checkAndGetModel("default", name);
    1446. +
    1447. }
    1448. +
    1449. }
    1450. +
    1451. }
    1452. +
    1453. } else {
    1454. +
    1455. 0 ret = name;
    1456. +
    1457. }
    1458. +
    1459. 10603 if (isUndefinedOrNull(ret)) {
    1460. +
    1461. 0 throw new ModelError("Model " + name + " has not been registered with patio");
    1462. +
    1463. }
    1464. +
    1465. 10603 return ret;
    1466. +
    1467. };
    1468. +
    -
    +
    -
    database/schema.js
    +
    associations/manyToMany.js
    - Coverage89.45 - SLOC1230 - LOC275 - Missed29 + Coverage89.05 + SLOC308 + LOC137 + Missed15
    -
    1. 1"use strict";
    2. -
    3. 1var comb = require("comb"),
    4. -
    5. isFunction = comb.isFunction,
    6. -
    7. argsToArray = comb.argsToArray,
    8. -
    9. array = comb.array,
    10. -
    11. isArray = comb.isArray,
    12. -
    13. isString = comb.isString,
    14. +
      1. 1var comb = require("comb-proxy"),
      2. +
      3. define = comb.define,
      4. isUndefined = comb.isUndefined,
      5. -
      6. isNumber = comb.isNumber,
      7. -
      8. toArray = comb.array.toArray,
      9. +
      10. isUndefinedOrNull = comb.isUndefinedOrNull,
      11. +
      12. isFunction = comb.isFunction,
      13. +
      14. isInstanceOf = comb.isInstanceOf,
      15. +
      16. sql = require("../sql").sql,
      17. hitch = comb.hitch,
      18. -
      19. format = comb.string.format,
      20. -
      21. Dataset = require("../dataset"),
      22. -
      23. Promise = comb.Promise,
      24. -
      25. PromiseList = comb.PromiseList,
      26. -
      27. errors = require("../errors"),
      28. -
      29. DatabaseError = errors.DatabaseError,
      30. -
      31. generators = require("./schemaGenerators"),
      32. -
      33. SchemaGenerator = generators.SchemaGenerator,
      34. -
      35. AlterTableGenerator = generators.AlterTableGenerator,
      36. -
      37. sql = require("../sql").sql,
      38. -
      39. Time = sql.Time,
      40. -
      41. TimeStamp = sql.TimeStamp,
      42. -
      43. DateTime = sql.DateTime,
      44. -
      45. Year = sql.Year,
      46. -
      47. Float = sql.Float,
      48. -
      49. Decimal = sql.Decimal,
      50. -
      51. isInstanceOf = comb.isInstanceOf,
      52. -
      53. Identifier = sql.Identifier,
      54. -
      55. QualifiedIdentifier = sql.QualifiedIdentifier,
      56. -
      57. define = comb.define;
      58. -
      59. +
      60. array = comb.array,
      61. +
      62. isBoolean = comb.isBoolean,
      63. +
      64. serial = comb.serial,
      65. +
      66. when = comb.when,
      67. +
      68. zip = array.zip,
      69. +
      70. Promise = comb.Promise,
      71. +
      72. PromiseList = comb.PromiseList,
      73. +
      74. hitchIgnore = comb.hitchIgnore,
      75. +
      76. OneToMany = require("./oneToMany"),
      77. +
      78. pluralize = comb.pluralize,
      79. +
      80. AssociationError = require("../errors").AssociationError;
      81. -
      82. 1define(null, {
      83. +
      84. 1var LOGGER = comb.logger("comb.associations.ManyToMany");
      85. +
      86. /**
      87. +
      88. * @class Class to define a manyToMany association.
      89. +
      90. *
      91. +
      92. * </br>
      93. +
      94. * <b>NOT to be instantiated directly</b>
      95. +
      96. * Its just documented for reference.
      97. +
      98. *
      99. +
      100. * @name ManyToMany
      101. +
      102. * @augments patio.associations.OneToMany
      103. +
      104. * @memberOf patio.associations
      105. +
      106. *
      107. +
      108. * @param {String} options.joinTable the joinTable of the association.
      109. +
      110. *
      111. +
      112. *
      113. +
      114. * @property {String} joinTable the join table used in the relation.
      115. +
      116. * */
      117. +
      118. 1module.exports = define(OneToMany, {
      119. instance:{
      120. -
      121. /**@lends patio.Database.prototype*/
      122. +
      123. /**@lends patio.associations.ManyToMany.prototype*/
      124. +
      125. type:"manyToMany",
      126. -
      127. /**@ignore*/
      128. -
      129. constructor:function () {
      130. -
      131. 123 this._super(arguments);
      132. -
      133. 123 this.schemas = {};
      134. -
      135. },
      136. +
      137. _fetchMethod:"all",
      138. -
      139. /**
      140. -
      141. * Adds a column to the specified table. This method expects a column name,
      142. -
      143. * a datatype and optionally a hash with additional constraints and options:
      144. -
      145. *
      146. -
      147. * <p>
      148. -
      149. * This method is a shortcut to {@link patio.Database#alterTable} with an
      150. -
      151. * addColumn call.
      152. -
      153. * </p>
      154. -
      155. *
      156. -
      157. *
      158. -
      159. * @example
      160. -
      161. * //Outside of a table
      162. -
      163. * //ALTER TABLE test ADD COLUMN name text UNIQUE'
      164. -
      165. * DB.addColumn("test", "name", "text", {unique : true});
      166. -
      167. *
      168. -
      169. * @param {String} table the table to add the column to.
      170. -
      171. * @param {String} column the name of the column to add.
      172. -
      173. * @param type datatype of the column
      174. -
      175. * @param {Object} [opts] additional options that can be used when adding a column.
      176. -
      177. * @param {Boolean} [opts.primaryKey] set to true if this column is a primary key.
      178. -
      179. * @param {Boolean} [opts.allowNull] whether or not this column should allow null.
      180. -
      181. * @param {Boolean} [opts.unique] set to true to add a UNIQUE constraint to a column,
      182. -
      183. *
      184. -
      185. * @return {Promise} a promise that is resolved when the ADD COLUMN action is complete.
      186. -
      187. **/
      188. -
      189. addColumn:function (table, column, type, opts) {
      190. -
      191. 9 var args = argsToArray(arguments).slice(1);
      192. -
      193. 9 return this.alterTable(table, function () {
      194. -
      195. 9 this.addColumn.apply(this, args);
      196. -
      197. });
      198. -
      199. },
      200. +
      201. supportsStringKey:false,
      202. -
      203. /**
      204. -
      205. * Adds an index to a table for the given columns
      206. -
      207. *
      208. -
      209. * <p>
      210. -
      211. * This method is a shortcut to {@link patio.Database#alterTable} with an
      212. -
      213. * addIndex call.
      214. -
      215. * </p>
      216. -
      217. * @example
      218. -
      219. * DB.addIndex("test", "name", {unique : true});
      220. -
      221. * //=> 'CREATE UNIQUE INDEX test_name_index ON test (name)'
      222. -
      223. * DB.addIndex("test", ["one", "two"]);
      224. -
      225. * //=> ''CREATE INDEX test_one_two_index ON test (one, two)''
      226. -
      227. *
      228. -
      229. * @param {String} table the table to add the index to.
      230. -
      231. * @param {String|String[]} columns the name of the column/s to create an index for.
      232. -
      233. * @param {Object} [options] additional options that can be used when adding an index.
      234. -
      235. * @param {Boolean} [options.unique] set to true if this this index should have a UNIQUE constraint.
      236. -
      237. * @param {Boolean} [options.ignoreErrors] set to true to ignore errors.
      238. -
      239. *
      240. -
      241. * @return {Promise} a promise that is resolved when the CREATE INDEX action is complete.
      242. -
      243. * */
      244. -
      245. addIndex:function (table, columns, options) {
      246. -
      247. 4 options = options || {};
      248. -
      249. 4 var ignoreErrors = options.ignoreErrors === true;
      250. -
      251. 4 var ret = new Promise();
      252. -
      253. 4 this.alterTable(table,function () {
      254. -
      255. 4 this.addIndex(columns, options);
      256. -
      257. }).then(ret, function (err) {
      258. -
      259. 0 if (!ignoreErrors) {
      260. -
      261. 0 ret.errback(err);
      262. -
      263. } else {
      264. -
      265. 0 ret.callback();
      266. +
      267. supportsCompositeKey:false,
      268. +
      269. +
      270. +
      271. _filter:function (parent) {
      272. +
      273. 326 var keys = this._getAssociationKey(parent);
      274. +
      275. 326 var options = this.__opts || {};
      276. +
      277. 326 var ds;
      278. +
      279. 326 if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {
      280. +
      281. 0 ds = ds.apply(parent, [parent]);
      282. +
      283. }
      284. +
      285. 326 if (!ds) {
      286. +
      287. 326 ds = this.model.dataset.naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {
      288. +
      289. 326 return sql.stringToIdentifier(k);
      290. +
      291. })).concat(zip(keys[0], this.parentPrimaryKey.map(function (k) {
      292. +
      293. 326 return parent[k];
      294. +
      295. }))));
      296. +
      297. 326 var recip = this.model._findAssociation(this);
      298. +
      299. 326 if (recip) {
      300. +
      301. 326 recip = recip[1];
      302. +
      303. }
      304. +
      305. 326 ds.rowCb = hitch(this, function (item) {
      306. +
      307. 340 var ret = new Promise();
      308. +
      309. 340 var model = this._toModel(item, true);
      310. +
      311. 340 if (recip) {
      312. +
      313. 340 recip.__setValue(model, parent);
      314. }
      315. +
      316. //call hook to finish other model associations
      317. +
      318. 340 model._hook("post", "load").then(hitch(this, function () {
      319. +
      320. 340 ret.callback(model);
      321. +
      322. }), ret);
      323. +
      324. 340 return ret.promise();
      325. });
      326. -
      327. 4 return ret.promise();
      328. -
      329. },
      330. -
      331. -
      332. /**
      333. -
      334. * Removes a column from the specified table.
      335. -
      336. * <p>
      337. -
      338. * This method is a shortcut to {@link patio.Database#alterTable} with an
      339. -
      340. * dropColumn call.
      341. -
      342. * </p>
      343. -
      344. *
      345. -
      346. * @example
      347. -
      348. * DB.dropColumn("items", "category");
      349. -
      350. * //=> 'ALTER TABLE items DROP COLUMN category',
      351. -
      352. *
      353. -
      354. * @param {String|patio.sql.Identifier} table the table to alter.
      355. -
      356. * @param {String|patio.sql.Identifier} column the column to drop.
      357. -
      358. *
      359. -
      360. * @return {Promise} a promise that is resolved once the DROP COLUMN action is complete.
      361. -
      362. * */
      363. -
      364. dropColumn:function (table, column) {
      365. -
      366. 3 column = argsToArray(arguments).slice(1);
      367. -
      368. 3 return this.alterTable(table, function () {
      369. -
      370. 3 this.dropColumn.apply(this, column);
      371. -
      372. });
      373. -
      374. },
      375. -
      376. /**
      377. -
      378. * Removes an index for the given table and column/s.
      379. -
      380. *
      381. -
      382. * <p>
      383. -
      384. * This method is a shortcut to {@link patio.Database#alterTable} with an
      385. -
      386. * dropIndex call.
      387. -
      388. * </p>
      389. -
      390. *
      391. -
      392. * @example
      393. -
      394. * DB.dropIndex("posts", "title");
      395. -
      396. * //=>'DROP INDEX posts_title_index
      397. -
      398. * DB.dropIndex("posts", ["author", "title"]);
      399. -
      400. * //'DROP INDEX posts_author_title_index'
      401. -
      402. *
      403. -
      404. * @param {String|patio.sql.Identifier} table the table to alter.
      405. -
      406. * @param {String|patio.sql.Identifier} column the name of the column/s the index was created from.
      407. -
      408. *
      409. -
      410. * @return {Promise} a promise that is resolved once the DROP INDEX action is complete.
      411. -
      412. * */
      413. -
      414. dropIndex:function (table, columns, options) {
      415. -
      416. 1 var args = argsToArray(arguments).slice(1);
      417. -
      418. 1 return this.alterTable(table, function () {
      419. -
      420. 1 this.dropIndex.apply(this, args);
      421. -
      422. });
      423. -
      424. },
      425. +
      426. }
      427. -
      428. /**
      429. -
      430. * Renames a column in the specified table.
      431. -
      432. *
      433. -
      434. * <p>
      435. -
      436. * This method is a shortcut to {@link patio.Database#alterTable} with an
      437. -
      438. * renameColumn call.
      439. -
      440. * </p>
      441. -
      442. *
      443. -
      444. * @example
      445. -
      446. * DB.renameColumn("items", "cntr", "counter");
      447. -
      448. * //=> ALTER TABLE items RENAME COLUMN cntr TO counter
      449. -
      450. *
      451. -
      452. * @param {String|patio.sql.Identifier} table the table to alter.
      453. -
      454. * @param {String|patio.sql.Identifier} column the name of the column to rename.
      455. -
      456. * @param {String|patio.sql.Identifier} newColumn the new name of the column.
      457. -
      458. *
      459. -
      460. * @return {Promise} a promise that is resolved once the RENAME COLUMN action is complete.
      461. -
      462. * */
      463. -
      464. renameColumn:function (table, column, newColumn) {
      465. -
      466. 4 var args = argsToArray(arguments).slice(1);
      467. -
      468. 4 return this.alterTable(table, function () {
      469. -
      470. 4 this.renameColumn.apply(this, args);
      471. -
      472. });
      473. +
      474. 326 return this._setDatasetOptions(ds);
      475. },
      476. -
      477. -
      478. /**
      479. -
      480. *Sets the default value for the given column in the given table:
      481. -
      482. *
      483. -
      484. * <p>
      485. -
      486. * This method is a shortcut to {@link patio.Database#alterTable} with an
      487. -
      488. * setColumnDefault call.
      489. -
      490. * </p>
      491. -
      492. *
      493. -
      494. * @example
      495. -
      496. * DB.setColumnDefault("items", "category", "misc");
      497. -
      498. * //=> ALTER TABLE items ALTER COLUMN category SET DEFAULT 'misc'
      499. -
      500. *
      501. -
      502. * @param {String|patio.sql.Identifier} table the table to alter.
      503. -
      504. * @param {String|patio.sql.Identifier} column the name of the column to set the DEFAULT on.
      505. -
      506. * @param def the new default value of the column.
      507. -
      508. *
      509. -
      510. * @return {Promise} a promise that is resolved once the SET DEFAULT action is complete.
      511. -
      512. * */
      513. -
      514. setColumnDefault:function (table, column, def) {
      515. -
      516. 1 var args = argsToArray(arguments).slice(1);
      517. -
      518. 1 return this.alterTable(table, function () {
      519. -
      520. 1 this.setColumnDefault.apply(this, args);
      521. -
      522. });
      523. +
      524. _setAssociationKeys:function (parent, model, val) {
      525. +
      526. 534 var keys = this._getAssociationKey(parent),
      527. +
      528. leftKey = keys[0],
      529. +
      530. parentPk = this.parentPrimaryKey;
      531. +
      532. 534 if (!(leftKey && leftKey.length === parentPk.length)) {
      533. +
      534. 0 throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);
      535. +
      536. }
      537. +
      538. 534 for (var i = 0; i < leftKey.length; i++) {
      539. +
      540. 534 model[leftKey[i]] = !isUndefined(val) ? val : parent[parentPk[i]];
      541. +
      542. }
      543. },
      544. -
      545. /**
      546. -
      547. * Set the data type for the given column in the given table:
      548. -
      549. * <p>
      550. -
      551. * This method is a shortcut to {@link patio.Database#alterTable} with an
      552. -
      553. * setColumnType call.
      554. -
      555. * </p>
      556. -
      557. *
      558. -
      559. * @example
      560. -
      561. * DB.setColumnType("items", "category", String);
      562. -
      563. * //=> ALTER TABLE items ALTER COLUMN category TYPE varchar(255)
      564. -
      565. *
      566. -
      567. * @param {String|patio.sql.Identifier} table the table to alter.
      568. -
      569. * @param {String|patio.sql.Identifier} column the name of the column to set the TYPE on.
      570. -
      571. * @param type the datatype of the column.
      572. -
      573. *
      574. -
      575. * @return {Promise} a promise that is resolved once the SET TYPE action is complete.
      576. -
      577. * */
      578. -
      579. setColumnType:function (table, column, type) {
      580. -
      581. 3 var args = argsToArray(arguments).slice(1);
      582. -
      583. 3 return this.alterTable(table, function () {
      584. -
      585. 3 this.setColumnType.apply(this, args);
      586. -
      587. });
      588. -
      589. },
      590. -
      591. -
      592. -
      593. /**
      594. -
      595. * Alters the given table with the specified block.
      596. -
      597. * <p>
      598. -
      599. * <b>NOTE:</b> The block is invoked in the scope of the table that is being altered. The block
      600. -
      601. * is also called with the table as the first argument. Within the block you must use
      602. -
      603. * <b>this</b>(If the block has not been bound to a different scope), or the table object
      604. -
      605. * that is passed in for all alter table operations. See {@link patio.AlterTableGenerator} for
      606. -
      607. * avaiable operations.
      608. -
      609. * </p>
      610. -
      611. *
      612. -
      613. * <p>
      614. -
      615. * <b>Note</b> that addColumn accepts all the options available for column
      616. -
      617. * definitions using createTable, and addIndex accepts all the options
      618. -
      619. * available for index definition.
      620. -
      621. * </p>
      622. -
      623. *
      624. -
      625. * @example
      626. -
      627. * //using the table object
      628. -
      629. * DB.alterTable("items", function(table){
      630. -
      631. * //you must use the passed in table object.
      632. -
      633. * table.addColumn("category", "text", {default : 'javascript'});
      634. -
      635. * table.dropColumn("category");
      636. -
      637. * table.renameColumn("cntr", "counter");
      638. -
      639. * table.setColumnType("value", "float");
      640. -
      641. * table.setColumnDefault("value", "float");
      642. -
      643. * table.addIndex(["group", "category"]);
      644. -
      645. * table.dropIndex [:group, :category]
      646. -
      647. * });
      648. -
      649. *
      650. -
      651. * //using this
      652. -
      653. * DB.alterTable("items", function(){
      654. -
      655. * this.addColumn("category", "text", {default : 'javascript'});
      656. -
      657. * this.dropColumn("category");
      658. -
      659. * this.renameColumn("cntr", "counter");
      660. -
      661. * this.setColumnType("value", "float");
      662. -
      663. * this.setColumnDefault("value", "float");
      664. -
      665. * this.addIndex(["group", "category"]);
      666. -
      667. * this.dropIndex [:group, :category]
      668. -
      669. * });
      670. -
      671. *
      672. -
      673. * //This will not work
      674. -
      675. * DB.alterTable("items", comb.hitch(someObject, function(){
      676. -
      677. * //This is called in the scope of someObject so this
      678. -
      679. * //will not work and will throw an error
      680. -
      681. * this.addColumn("category", "text", {default : 'javascript'});
      682. -
      683. * }));
      684. -
      685. *
      686. -
      687. * //This will work
      688. -
      689. * DB.alterTable("items", comb.hitch(someObject, function(table){
      690. -
      691. * //This is called in the scope of someObject so you must
      692. -
      693. * //use the table argument
      694. -
      695. * table.category("text", {default : 'javascript'});
      696. -
      697. * }));
      698. -
      699. *
      700. -
      701. *
      702. -
      703. * @param {String|patio.sql.Identifier} table to the table to perform the ALTER TABLE operations on.
      704. -
      705. * @param {Function} block the block to invoke for the ALTER TABLE operations
      706. -
      707. *
      708. -
      709. * @return {Promise} a promise that is resolved once all ALTER TABLE operations have completed.
      710. -
      711. * */
      712. -
      713. alterTable:function (name, generator, block) {
      714. -
      715. 84 if (isFunction(generator)) {
      716. -
      717. 84 block = generator;
      718. -
      719. 84 generator = new AlterTableGenerator(this, block);
      720. +
      721. __createJoinTableInsertRemoveQuery:function (model, item) {
      722. +
      723. 176 var q = {};
      724. +
      725. 176 var keys = this._getAssociationKey(model),
      726. +
      727. leftKey = keys[0],
      728. +
      729. rightKey = keys[1],
      730. +
      731. parentPk = this.parentPrimaryKey,
      732. +
      733. modelPk = this.modelPrimaryKey;
      734. +
      735. 176 if (!(leftKey && leftKey.length === parentPk.length)) {
      736. +
      737. 0 throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);
      738. }
      739. -
      740. 84 var ret = new Promise();
      741. -
      742. 84 this.__alterTableSqlList(name, generator.operations).then(hitch(this, function (res) {
      743. -
      744. 84 var sqls = array.flatten(res.map(function (r) {
      745. -
      746. 93 return r[1];
      747. -
      748. }));
      749. -
      750. 84 var l = sqls.length;
      751. -
      752. 84 var drop = hitch(this, function (i) {
      753. -
      754. 179 if (i < l) {
      755. -
      756. 95 var sql = sqls[i++];
      757. -
      758. 95 this.executeDdl(sql).then(hitch(this, drop, i), ret);
      759. -
      760. } else {
      761. -
      762. 84 this.removeCachedSchema(name);
      763. -
      764. 84 ret.callback();
      765. -
      766. }
      767. -
      768. });
      769. -
      770. 84 drop(0);
      771. -
      772. }));
      773. -
      774. 84 return ret.promise();
      775. -
      776. },
      777. -
      778. -
      779. /**
      780. -
      781. * Creates a table with the columns given in the provided block:
      782. -
      783. *
      784. -
      785. * <p>
      786. -
      787. * <b>NOTE:</b> The block is invoked in the scope of the table that is being created. The block
      788. -
      789. * is also called with the table as the first argument. Within the block you must use
      790. -
      791. * <b>this</b>(If the block has not been bound to a different scope), or the table object
      792. -
      793. * that is passed in for all create table operations. See {@link patio.SchemaGenerator} for
      794. -
      795. * available operations.
      796. -
      797. * </p>
      798. -
      799. *
      800. -
      801. *
      802. -
      803. * @example
      804. -
      805. *
      806. -
      807. * //using the table to create the table
      808. -
      809. * DB.createTable("posts", function(table){
      810. -
      811. * table.primaryKey("id");
      812. -
      813. * table.column('title", "text");
      814. -
      815. * //you may also invoke the column name as
      816. -
      817. * //function on the table
      818. -
      819. * table.content(String);
      820. -
      821. * table.index(title);
      822. -
      823. * });
      824. -
      825. *
      826. -
      827. * //using this to create the table
      828. -
      829. * DB.createTable("posts", function(){
      830. -
      831. * this.primaryKey("id");
      832. -
      833. * this.column('title", "text");
      834. -
      835. * //you may also invoke the column name as
      836. -
      837. * //function on the table
      838. -
      839. * this.content(String);
      840. -
      841. * this.index(title);
      842. -
      843. * });
      844. -
      845. *
      846. -
      847. * @param {String|patio.sql.Identifier} name the name of the table to create.
      848. -
      849. * @param {Object} [options] an optional options object
      850. -
      851. * @param {Boolean} [options.temp] set to true if this table is a TEMPORARY table.
      852. -
      853. * @param {Boolean} [options.ignoreIndexErrors] Ignore any errors when creating indexes.
      854. -
      855. * @param {Function} block the block to invoke when creating the table.
      856. -
      857. *
      858. -
      859. * @return {Promise} a promise that is resolved when the CREATE TABLE action is completed.
      860. -
      861. *
      862. -
      863. */
      864. -
      865. createTable:function (name, options, block) {
      866. -
      867. 218 if (isFunction(options)) {
      868. -
      869. 208 block = options;
      870. -
      871. 208 options = {};
      872. +
      873. 176 if (!(rightKey && rightKey.length === modelPk.length)) {
      874. +
      875. 0 throw new AssociationError("Invalid rightKey for " + this.name + " : " + rightKey);
      876. }
      877. -
      878. 218 this.removeCachedSchema(name);
      879. -
      880. 218 if (isInstanceOf(options, SchemaGenerator)) {
      881. -
      882. 0 options = {generator:options};
      883. +
      884. 176 for (var i = 0; i < leftKey.length; i++) {
      885. +
      886. 176 q[leftKey[i]] = model[parentPk[i]];
      887. }
      888. -
      889. 218 var generator = options.generator || new SchemaGenerator(this, block);
      890. -
      891. 218 return this.__createTableFromGenerator(name, generator, options)
      892. -
      893. .chain(hitch(this, "__createTableIndexesFromGenerator", name, generator, options))
      894. -
      895. .promise();
      896. +
      897. 176 for (i = 0; i < rightKey.length; i++) {
      898. +
      899. 176 q[rightKey[i]] = item[modelPk[i]];
      900. +
      901. }
      902. +
      903. 176 return q;
      904. },
      905. -
      906. /**
      907. -
      908. * Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.
      909. -
      910. * <p>
      911. -
      912. * See {@link patio.Database#createTable} for parameter types.
      913. -
      914. * </p>
      915. -
      916. *
      917. -
      918. * @example
      919. -
      920. * // DROP TABLE a
      921. -
      922. * // CREATE TABLE a (a integer)
      923. -
      924. * DB.forceCreateTable("a", function(){
      925. -
      926. * this.a("integer");
      927. -
      928. * });
      929. -
      930. *
      931. -
      932. **/
      933. -
      934. forceCreateTable:function (name, options, block) {
      935. -
      936. 18 return this.dropTable(name)
      937. -
      938. .chainBoth(hitch(this, this.createTable, name, options, block))
      939. -
      940. .promise();
      941. +
      942. _preRemove:function (next, model) {
      943. +
      944. 202 if (this.isOwner && !this.isCascading) {
      945. +
      946. 202 var q = {};
      947. +
      948. 202 this._setAssociationKeys(model, q);
      949. +
      950. 202 this.joinTable.where(q).remove().classic(next);
      951. +
      952. } else {
      953. +
      954. 0 next();
      955. +
      956. }
      957. },
      958. -
      959. /**
      960. -
      961. * Creates the table unless the table already exists.
      962. -
      963. * <p>
      964. -
      965. * See {@link patio.Database#createTable} for parameter types.
      966. -
      967. * </p>
      968. -
      969. */
      970. -
      971. createTableUnlessExists:function (name, options, block) {
      972. -
      973. 0 var ret = new Promise();
      974. -
      975. 0 this.tableExists(name).then(hitch(this, function (exists) {
      976. -
      977. 0 if (!exists) {
      978. -
      979. 0 this.createTable(name, options, block).then(ret);
      980. +
      981. addAssociation:function (item, model, reload) {
      982. +
      983. 260 reload = isBoolean(reload) ? reload : false;
      984. +
      985. 260 var ret = new Promise().callback(model);
      986. +
      987. 260 if (!isUndefinedOrNull(item)) {
      988. +
      989. 260 if (!model.isNew) {
      990. +
      991. 148 item = this._toModel(item);
      992. +
      993. 148 var loaded = this.associationLoaded(model);
      994. +
      995. 148 var recip = this.model._findAssociation(this), save = item.isNew;
      996. +
      997. 148 ret = model._checkTransaction(hitch(this, function () {
      998. +
      999. 148 var joinTable = this.joinTable, ret = new Promise();
      1000. +
      1001. 148 serial([
      1002. +
      1003. function () {
      1004. +
      1005. 148 return save ? item.save() : null;
      1006. +
      1007. },
      1008. +
      1009. function () {
      1010. +
      1011. 148 return joinTable.insert(this.__createJoinTableInsertRemoveQuery(model, item));
      1012. +
      1013. }.bind(this),
      1014. +
      1015. function () {
      1016. +
      1017. 148 if (recip) {
      1018. +
      1019. 148 recip[1].__setValue(item, [model]);
      1020. +
      1021. }
      1022. +
      1023. },
      1024. +
      1025. function () {
      1026. +
      1027. 148 if (loaded && reload) {
      1028. +
      1029. 3 return this.parent._reloadAssociationsForType(this.type, this.model, model);
      1030. +
      1031. } else {
      1032. +
      1033. 145 return model;
      1034. +
      1035. }
      1036. +
      1037. }.bind(this)
      1038. +
      1039. ]).then(hitchIgnore(ret, "callback", model), ret);
      1040. +
      1041. 148 return ret.promise();
      1042. +
      1043. }));
      1044. } else {
      1045. -
      1046. 0 ret.callback();
      1047. -
      1048. }
      1049. -
      1050. }), ret);
      1051. -
      1052. 0 return ret.promise();
      1053. -
      1054. },
      1055. -
      1056. -
      1057. /**
      1058. -
      1059. * Creates a view, replacing it if it already exists:
      1060. -
      1061. * @example
      1062. -
      1063. * DB.createOrReplaceView("cheapItems", "SELECT * FROM items WHERE price < 100");
      1064. -
      1065. * //=> CREATE OR REPLACE VIEW cheapItems AS SELECT * FROM items WHERE price < 100
      1066. -
      1067. * DB.createOrReplaceView("miscItems", DB[:items].filter({category : 'misc'}));
      1068. -
      1069. * //=> CREATE OR REPLACE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'
      1070. -
      1071. *
      1072. -
      1073. * @param {String|patio.sql.Identifier} name the name of the view to create.
      1074. -
      1075. * @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the
      1076. -
      1077. * view.
      1078. -
      1079. *
      1080. -
      1081. * @return {Promise} a promise that is resolved when the CREATE OR REPLACE VIEW action is complete.
      1082. -
      1083. **/
      1084. -
      1085. createOrReplaceView:function (name, source) {
      1086. -
      1087. 4 if (isInstanceOf(source, Dataset)) {
      1088. -
      1089. 2 source = source.sql;
      1090. -
      1091. }
      1092. -
      1093. 4 var ret = new Promise();
      1094. -
      1095. 4 this.executeDdl(format("CREATE OR REPLACE VIEW %s AS %s", this.__quoteSchemaTable(name), source)).then(hitch(this, function () {
      1096. -
      1097. 4 this.removeCachedSchema(name);
      1098. -
      1099. 4 ret.callback();
      1100. -
      1101. }), ret);
      1102. -
      1103. 4 return ret.promise();
      1104. -
      1105. },
      1106. -
      1107. -
      1108. /**
      1109. -
      1110. * Creates a view based on a dataset or an SQL string:
      1111. -
      1112. * @example
      1113. -
      1114. * DB.createView("cheapItems", "SELECT * FROM items WHERE price < 100");
      1115. -
      1116. * //=> CREATE VIEW cheapItems AS SELECT * FROM items WHERE price < 100
      1117. -
      1118. * DB.createView("miscItems", DB[:items].filter({category : 'misc'}));
      1119. -
      1120. * //=> CREATE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'
      1121. -
      1122. *
      1123. -
      1124. * @param {String|patio.sql.Identifier} name the name of the view to create.
      1125. -
      1126. * @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the
      1127. -
      1128. * view.
      1129. -
      1130. **/
      1131. -
      1132. createView:function (name, source) {
      1133. -
      1134. 4 if (isInstanceOf(source, Dataset)) {
      1135. -
      1136. 2 source = source.sql;
      1137. +
      1138. 112 item = this._toModel(item);
      1139. +
      1140. 112 var items = this.getAssociation(model);
      1141. +
      1142. 112 if (isUndefinedOrNull(items)) {
      1143. +
      1144. 39 this.__setValue(model, [item]);
      1145. +
      1146. } else {
      1147. +
      1148. 73 items.push(item);
      1149. +
      1150. }
      1151. +
      1152. }
      1153. }
      1154. -
      1155. 4 return this.executeDdl(format("CREATE VIEW %s AS %s", this.__quoteSchemaTable(name), source));
      1156. +
      1157. 260 return ret.promise();
      1158. },
      1159. -
      1160. /**
      1161. -
      1162. * Drops one or more tables corresponding to the given names.
      1163. -
      1164. *
      1165. -
      1166. * @example
      1167. -
      1168. * DB.dropTable("test");
      1169. -
      1170. * //=>'DROP TABLE test'
      1171. -
      1172. * DB.dropTable("a", "bb", "ccc");
      1173. -
      1174. * //=>'DROP TABLE a',
      1175. -
      1176. * //=>'DROP TABLE bb',
      1177. -
      1178. * //=>'DROP TABLE ccc'
      1179. -
      1180. *
      1181. -
      1182. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the tables
      1183. -
      1184. * to drop.
      1185. -
      1186. *
      1187. -
      1188. * @return {Promise} a promise that is resolved once all tables have been dropped.
      1189. -
      1190. **/
      1191. -
      1192. dropTable:function (names) {
      1193. -
      1194. 81 var ret = new Promise(), l = names.length;
      1195. -
      1196. 81 if (isArray(names)) {
      1197. -
      1198. 48 var drop = hitch(this, function (i) {
      1199. -
      1200. 104 if (i < l) {
      1201. -
      1202. 65 var name = names[i++];
      1203. -
      1204. 65 this.executeDdl(this.__dropTableSql(name)).then(hitch(this, function () {
      1205. -
      1206. 56 this.removeCachedSchema(name);
      1207. -
      1208. 56 drop(i);
      1209. +
      1210. removeItem:function (item, model, remove, reload) {
      1211. +
      1212. 56 reload = isBoolean(reload) ? reload : false;
      1213. +
      1214. 56 remove = isBoolean(remove) ? remove : false;
      1215. +
      1216. 56 var ret = new Promise().callback(model);
      1217. +
      1218. 56 if (!isUndefinedOrNull(item)) {
      1219. +
      1220. 56 if (!model.isNew) {
      1221. +
      1222. 56 if (isInstanceOf(item, this.model) && !item.isNew) {
      1223. +
      1224. 56 var loaded = this.associationLoaded(model);
      1225. +
      1226. 56 remove = remove && !item.isNew;
      1227. +
      1228. 56 ret = new Promise();
      1229. +
      1230. 56 model._checkTransaction(hitch(this, function () {
      1231. +
      1232. 56 return remove ? item.remove() : this.joinTable.where(this.__createJoinTableInsertRemoveQuery(model, item)).remove();
      1233. +
      1234. })).then(hitch(this, function () {
      1235. +
      1236. 56 if (loaded && reload) {
      1237. +
      1238. 18 return this.parent._reloadAssociationsForType(this.type, this.model, model).then(ret);
      1239. +
      1240. } else {
      1241. +
      1242. 38 ret.callback(model);
      1243. +
      1244. }
      1245. }), ret);
      1246. -
      1247. } else {
      1248. -
      1249. 39 ret.callback();
      1250. +
      1251. 56 return ret.promise();
      1252. }
      1253. -
      1254. });
      1255. -
      1256. 48 drop(0);
      1257. -
      1258. 48 return ret.promise();
      1259. -
      1260. } else {
      1261. -
      1262. 33 return this.dropTable(argsToArray(arguments).filter(function (t) {
      1263. -
      1264. 38 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
      1265. -
      1266. }));
      1267. -
      1268. +
      1269. } else {
      1270. +
      1271. 0 item = this._toModel(item);
      1272. +
      1273. 0 var items = this.getAssociation(model), index;
      1274. +
      1275. 0 if (!isUndefinedOrNull(items) && (index = items.indexOf(item)) !== -1) {
      1276. +
      1277. 0 items.splice(index, 1);
      1278. +
      1279. }
      1280. +
      1281. }
      1282. }
      1283. +
      1284. 0 return ret.promise();
      1285. },
      1286. -
      1287. /**
      1288. -
      1289. * Forcible drops one or more tables corresponding to the given names, ignoring errors.
      1290. -
      1291. *
      1292. -
      1293. * @example
      1294. -
      1295. * DB.dropTable("test");
      1296. -
      1297. * //=>'DROP TABLE test'
      1298. -
      1299. * DB.dropTable("a", "bb", "ccc");
      1300. -
      1301. * //=>'DROP TABLE a',
      1302. -
      1303. * //=>'DROP TABLE bb',
      1304. -
      1305. * //=>'DROP TABLE ccc'
      1306. -
      1307. *
      1308. -
      1309. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the tables
      1310. -
      1311. * to drop.
      1312. -
      1313. *
      1314. -
      1315. * @return {Promise} a promise that is resolved once all tables have been dropped.
      1316. -
      1317. **/
      1318. -
      1319. forceDropTable:function (names) {
      1320. -
      1321. 158 var ret = new Promise(), l = names.length;
      1322. -
      1323. 158 if (isArray(names)) {
      1324. -
      1325. 96 var drop = hitch(this, function (i) {
      1326. -
      1327. 241 if (i < l) {
      1328. -
      1329. 145 var name = names[i++];
      1330. -
      1331. 145 this.executeDdl(this.__dropTableSql(name)).both(hitch(this, function () {
      1332. -
      1333. 145 this.removeCachedSchema(name);
      1334. -
      1335. 145 drop(i);
      1336. -
      1337. }));
      1338. -
      1339. } else {
      1340. -
      1341. 96 ret.callback();
      1342. -
      1343. }
      1344. -
      1345. });
      1346. -
      1347. 96 drop(0);
      1348. +
      1349. removeAllItems:function (model, remove) {
      1350. +
      1351. 4 remove = isBoolean(remove) ? remove : false;
      1352. +
      1353. 4 var ret = new Promise();
      1354. +
      1355. 4 if (!model.isNew) {
      1356. +
      1357. 4 var q = {}, removeQ = {};
      1358. +
      1359. 4 this._setAssociationKeys(model, q);
      1360. +
      1361. 4 this._setAssociationKeys(model, removeQ, null);
      1362. +
      1363. 4 var loaded = this.associationLoaded(model);
      1364. +
      1365. 4 return model._checkTransaction(hitch(this, function () {
      1366. +
      1367. 4 var ds = this.model.dataset, ret = new Promise();
      1368. +
      1369. 4 when(remove ? this._filter(model).forEach(function (m) {
      1370. +
      1371. 6 return m.remove();
      1372. +
      1373. }) : this.joinTable.filter(q).update(removeQ)).then(function () {
      1374. +
      1375. 4 if (loaded) {
      1376. +
      1377. 2 this.parent._reloadAssociationsForType(this.type, this.model, model)
      1378. +
      1379. .then(hitchIgnore(ret, "callback", model), ret);
      1380. +
      1381. } else {
      1382. +
      1383. 2 ret.callback(model);
      1384. +
      1385. }
      1386. +
      1387. }.bind(this), ret);
      1388. +
      1389. 4 return ret.promise();
      1390. +
      1391. }));
      1392. } else {
      1393. -
      1394. 62 this.forceDropTable(argsToArray(arguments).filter(function (t) {
      1395. -
      1396. 62 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
      1397. -
      1398. })).both(ret);
      1399. +
      1400. //todo we may want to check if any of the items were previously saved items;
      1401. +
      1402. 0 this._clearAssociations(model);
      1403. +
      1404. 0 ret.callback(model);
      1405. }
      1406. -
      1407. 158 return ret.promise();
      1408. +
      1409. 0 return ret.promise();
      1410. },
      1411. -
      1412. /**
      1413. -
      1414. * Drops one or more views corresponding to the given names.
      1415. -
      1416. *
      1417. -
      1418. * @example
      1419. -
      1420. * DB.dropView("test_view");
      1421. -
      1422. * //=>'DROP VIEW test_view'
      1423. -
      1424. * DB.dropTable("test_view_1", "test_view_2", "test_view_3");
      1425. -
      1426. * //=>'DROP VIEW test_view_1',
      1427. -
      1428. * //=>'DROP VIEW test_view_2',
      1429. -
      1430. * //=>'DROP VIEW test_view_3'
      1431. -
      1432. *
      1433. -
      1434. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the views
      1435. -
      1436. * to drop.
      1437. -
      1438. *
      1439. -
      1440. * @return {Promise} a promise that is resolved once the view/s have been dropped.
      1441. -
      1442. **/
      1443. -
      1444. dropView:function (names) {
      1445. -
      1446. 8 var ret = new Promise(), l = names.length;
      1447. -
      1448. 8 if (isArray(names)) {
      1449. -
      1450. 4 var drop = hitch(this, function (i) {
      1451. -
      1452. 8 if (i < l) {
      1453. -
      1454. 4 var name = names[i++];
      1455. -
      1456. 4 this.executeDdl(format("DROP VIEW %s", this.__quoteSchemaTable(name))).then(hitch(this, function () {
      1457. -
      1458. 4 this.removeCachedSchema(name);
      1459. -
      1460. 4 drop(i);
      1461. -
      1462. }), ret);
      1463. -
      1464. } else {
      1465. -
      1466. 4 ret.callback();
      1467. -
      1468. }
      1469. -
      1470. });
      1471. -
      1472. 4 drop(0);
      1473. -
      1474. 4 return ret.promise();
      1475. -
      1476. } else {
      1477. -
      1478. 4 return this.dropView(argsToArray(arguments).filter(function (t) {
      1479. -
      1480. 4 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
      1481. -
      1482. }));
      1483. -
      1484. }
      1485. -
      1486. },
      1487. +
      1488. getters:{
      1489. -
      1490. /**
      1491. -
      1492. * Renames a table.
      1493. -
      1494. *
      1495. -
      1496. * @example
      1497. -
      1498. * comb.executeInOrder(DB, function(DB){
      1499. -
      1500. * DB.tables(); //=> ["items"]
      1501. -
      1502. * DB.renameTable("items", "old_items");
      1503. -
      1504. * //=>'ALTER TABLE items RENAME TO old_items'
      1505. -
      1506. * DB.tables; //=> ["old_items"]
      1507. -
      1508. *});
      1509. -
      1510. *
      1511. -
      1512. * @param {String|patio.sql.Identifier} name the name of the table to rename
      1513. -
      1514. * @param {String|patio.sql.Identifier} newName the new name of the table
      1515. -
      1516. * @return {Promise} a promise that is resolved once the table is renamed.
      1517. -
      1518. **/
      1519. -
      1520. renameTable:function (name, newName) {
      1521. -
      1522. 2 return this.executeDdl(this.__renameTableSql(name, newName)).chain(hitch(this, function () {
      1523. -
      1524. 2 this.removeCachedSchema(name);
      1525. -
      1526. })).promise();
      1527. +
      1528. select:function () {
      1529. +
      1530. 326 return this.__select;
      1531. +
      1532. },
      1533. -
      1534. },
      1535. +
      1536. defaultLeftKey:function () {
      1537. +
      1538. 1740 return this.__opts.leftKey || this.parent.tableName + "Id";
      1539. +
      1540. },
      1541. -
      1542. /**
      1543. -
      1544. * @private
      1545. -
      1546. * The SQL to execute to modify the DDL for the given table name. op
      1547. -
      1548. * should be one of the operations returned by the AlterTableGenerator.
      1549. -
      1550. * */
      1551. -
      1552. __alterTableSql:function (table, op) {
      1553. -
      1554. 83 var ret = new Promise();
      1555. -
      1556. 83 var quotedName = op.name ? this.__quoteIdentifier(op.name) : null;
      1557. -
      1558. 83 var alterTableOp = null;
      1559. -
      1560. 83 switch (op.op) {
      1561. -
      1562. case "addColumn":
      1563. -
      1564. 13 alterTableOp = format("ADD COLUMN %s", this.__columnDefinitionSql(op));
      1565. -
      1566. 13 break;
      1567. -
      1568. case "dropColumn":
      1569. -
      1570. 4 alterTableOp = format("DROP COLUMN %s", quotedName);
      1571. -
      1572. 4 break;
      1573. -
      1574. case "renameColumn":
      1575. -
      1576. 52 alterTableOp = format("RENAME COLUMN %s TO %s", quotedName, this.__quoteIdentifier(op.newName));
      1577. -
      1578. 52 break;
      1579. -
      1580. case "setColumnType":
      1581. -
      1582. 3 alterTableOp = format("ALTER COLUMN %s TYPE %s", quotedName, this.typeLiteral(op));
      1583. -
      1584. 3 break;
      1585. -
      1586. case "setColumnDefault":
      1587. -
      1588. 2 alterTableOp = format("ALTER COLUMN %s SET DEFAULT %s", quotedName, this.literal(op["default"]));
      1589. -
      1590. 2 break;
      1591. -
      1592. case "setColumnNull":
      1593. -
      1594. 0 alterTableOp = format("ALTER COLUMN %s %s NOT NULL", quotedName, op["null"] ? "DROP" : "SET");
      1595. -
      1596. 0 break;
      1597. -
      1598. case "addIndex":
      1599. -
      1600. 5 return ret.callback(this.__indexDefinitionSql(table, op)).promise();
      1601. -
      1602. case "dropIndex":
      1603. -
      1604. 2 return ret.callback(this.__dropIndexSql(table, op)).promise();
      1605. -
      1606. case "addConstraint":
      1607. -
      1608. 1 alterTableOp = format("ADD %s", this.__constraintDefinitionSql(op));
      1609. -
      1610. 1 break;
      1611. -
      1612. case "dropConstraint":
      1613. -
      1614. 0 alterTableOp = format("DROP CONSTRAINT %s", quotedName);
      1615. -
      1616. 0 break;
      1617. -
      1618. default :
      1619. -
      1620. 1 throw new DatabaseError("Invalid altertable operator");
      1621. +
      1622. defaultRightKey:function () {
      1623. +
      1624. 1740 return this.__opts.rightKey || this.model.tableName + "Id";
      1625. +
      1626. },
      1627. +
      1628. +
      1629. parentPrimaryKey:function () {
      1630. +
      1631. 1036 return this.__opts.leftPrimaryKey || this.parent.primaryKey;
      1632. +
      1633. },
      1634. +
      1635. +
      1636. modelPrimaryKey:function () {
      1637. +
      1638. 502 return this.__opts.rightPrimaryKey || this.model.primaryKey;
      1639. +
      1640. },
      1641. +
      1642. +
      1643. joinTableName:function () {
      1644. +
      1645. 344 if (!this._joinTable) {
      1646. +
      1647. 18 var options = this.__opts;
      1648. +
      1649. 18 var joinTable = options.joinTable;
      1650. +
      1651. 18 if (isUndefined(joinTable)) {
      1652. +
      1653. 18 var defaultJoinTable = this.defaultJoinTable;
      1654. +
      1655. 18 if (isUndefined(defaultJoinTable)) {
      1656. +
      1657. 0 throw new Error("Unable to determine jointable for " + this.name);
      1658. +
      1659. } else {
      1660. +
      1661. 18 this._joinTable = defaultJoinTable;
      1662. +
      1663. }
      1664. +
      1665. } else {
      1666. +
      1667. 0 this._joinTable = joinTable;
      1668. +
      1669. }
      1670. +
      1671. }
      1672. +
      1673. 344 return this._joinTable;
      1674. +
      1675. },
      1676. +
      1677. +
      1678. //returns our join table model
      1679. +
      1680. joinTable:function () {
      1681. +
      1682. 380 if (!this.__joinTableDataset) {
      1683. +
      1684. 18 var ds = this.__joinTableDataset = this.model.dataset.db.from(this.joinTableName), model = this.model, options = this.__opts;
      1685. +
      1686. 18 var identifierInputMethod = isUndefined(options.identifierInputMethod) ? model.identifierInputMethod : options.identifierInputMethod,
      1687. +
      1688. identifierOutputMethod = isUndefined(options.identifierOutputMethod) ? model.identifierOutputMethod : options.identifierOutputMethod;
      1689. +
      1690. 18 if (identifierInputMethod) {
      1691. +
      1692. 14 ds.identifierInputMethod = identifierInputMethod;
      1693. +
      1694. }
      1695. +
      1696. 18 if (identifierOutputMethod) {
      1697. +
      1698. 14 ds.identifierOutputMethod = identifierOutputMethod;
      1699. +
      1700. }
      1701. +
      1702. }
      1703. +
      1704. 380 return this.__joinTableDataset;
      1705. +
      1706. },
      1707. +
      1708. +
      1709. defaultJoinTable:function () {
      1710. +
      1711. 18 var ret;
      1712. +
      1713. 18 var recip = this.model._findAssociation(this);
      1714. +
      1715. 18 if (recip && recip.length) {
      1716. +
      1717. 18 var names = [pluralize(this._model), pluralize(recip[1]._model)].sort();
      1718. +
      1719. 18 names[1] = names[1].charAt(0).toUpperCase() + names[1].substr(1);
      1720. +
      1721. 18 ret = names.join("");
      1722. +
      1723. }
      1724. +
      1725. 18 return ret;
      1726. }
      1727. -
      1728. 75 return ret.callback(format("ALTER TABLE %s %s", this.__quoteSchemaTable(table), alterTableOp)).promise();
      1729. -
      1730. },
      1731. +
      1732. }
      1733. +
      1734. }
      1735. +
      1736. });
      1737. +
      +
    + +
    + + + + + +
    +
    database/schema.js
    +
    +
    + Coverage89.45 + SLOC1230 + LOC275 + Missed29 +
    +
    +
    1. 1"use strict";
    2. +
    3. 1var comb = require("comb"),
    4. +
    5. isFunction = comb.isFunction,
    6. +
    7. argsToArray = comb.argsToArray,
    8. +
    9. array = comb.array,
    10. +
    11. isArray = comb.isArray,
    12. +
    13. isString = comb.isString,
    14. +
    15. isUndefined = comb.isUndefined,
    16. +
    17. isNumber = comb.isNumber,
    18. +
    19. toArray = comb.array.toArray,
    20. +
    21. hitch = comb.hitch,
    22. +
    23. format = comb.string.format,
    24. +
    25. Dataset = require("../dataset"),
    26. +
    27. Promise = comb.Promise,
    28. +
    29. PromiseList = comb.PromiseList,
    30. +
    31. errors = require("../errors"),
    32. +
    33. DatabaseError = errors.DatabaseError,
    34. +
    35. generators = require("./schemaGenerators"),
    36. +
    37. SchemaGenerator = generators.SchemaGenerator,
    38. +
    39. AlterTableGenerator = generators.AlterTableGenerator,
    40. +
    41. sql = require("../sql").sql,
    42. +
    43. Time = sql.Time,
    44. +
    45. TimeStamp = sql.TimeStamp,
    46. +
    47. DateTime = sql.DateTime,
    48. +
    49. Year = sql.Year,
    50. +
    51. Float = sql.Float,
    52. +
    53. Decimal = sql.Decimal,
    54. +
    55. isInstanceOf = comb.isInstanceOf,
    56. +
    57. Identifier = sql.Identifier,
    58. +
    59. QualifiedIdentifier = sql.QualifiedIdentifier,
    60. +
    61. define = comb.define;
    62. -
    63. /**
    64. -
    65. * @private
    66. -
    67. * Array of SQL DDL modification statements for the given table,
    68. -
    69. * corresponding to the DDL changes specified by the operations.
    70. -
    71. * */
    72. -
    73. __alterTableSqlList:function (table, operations) {
    74. -
    75. 84 return new PromiseList(operations.map(hitch(this, "__alterTableSql", table)));
    76. +
    77. +
    78. 1define(null, {
    79. +
    80. instance:{
    81. +
    82. /**@lends patio.Database.prototype*/
    83. +
    84. +
    85. /**@ignore*/
    86. +
    87. constructor:function () {
    88. +
    89. 123 this._super(arguments);
    90. +
    91. 123 this.schemas = {};
    92. },
    93. /**
    94. -
    95. * @private
    96. -
    97. * SQL DDL fragment containing the column creation SQL for the given column.
    98. +
    99. * Adds a column to the specified table. This method expects a column name,
    100. +
    101. * a datatype and optionally a hash with additional constraints and options:
    102. *
    103. -
    104. * @param column
    105. -
    106. */
    107. -
    108. __columnDefinitionSql:function (column) {
    109. -
    110. 644 var sql = [format("%s %s", this.__quoteIdentifier(column.name), this.typeLiteral(column))];
    111. -
    112. 644 column.unique && sql.push(this._static.UNIQUE);
    113. -
    114. 644 (column.allowNull === false || column["null"] === false) && sql.push(this._static.NOT_NULL);
    115. -
    116. 644 (column.allowNull === true || column["null"] === true) && sql.push(this._static.NULL);
    117. -
    118. 644 !isUndefined(column["default"]) && sql.push(format(" DEFAULT %s", this.literal(column["default"])));
    119. -
    120. 644 column.primaryKey && sql.push(this._static.PRIMARY_KEY);
    121. -
    122. 644 column.autoIncrement && sql.push(" " + this.autoIncrementSql);
    123. -
    124. 644 column.table && sql.push(this.__columnReferencesColumnConstraintSql(column));
    125. -
    126. 644 return sql.join("");
    127. +
    128. * <p>
    129. +
    130. * This method is a shortcut to {@link patio.Database#alterTable} with an
    131. +
    132. * addColumn call.
    133. +
    134. * </p>
    135. +
    136. *
    137. +
    138. *
    139. +
    140. * @example
    141. +
    142. * //Outside of a table
    143. +
    144. * //ALTER TABLE test ADD COLUMN name text UNIQUE'
    145. +
    146. * DB.addColumn("test", "name", "text", {unique : true});
    147. +
    148. *
    149. +
    150. * @param {String} table the table to add the column to.
    151. +
    152. * @param {String} column the name of the column to add.
    153. +
    154. * @param type datatype of the column
    155. +
    156. * @param {Object} [opts] additional options that can be used when adding a column.
    157. +
    158. * @param {Boolean} [opts.primaryKey] set to true if this column is a primary key.
    159. +
    160. * @param {Boolean} [opts.allowNull] whether or not this column should allow null.
    161. +
    162. * @param {Boolean} [opts.unique] set to true to add a UNIQUE constraint to a column,
    163. +
    164. *
    165. +
    166. * @return {Promise} a promise that is resolved when the ADD COLUMN action is complete.
    167. +
    168. **/
    169. +
    170. addColumn:function (table, column, type, opts) {
    171. +
    172. 9 var args = argsToArray(arguments).slice(1);
    173. +
    174. 9 return this.alterTable(table, function () {
    175. +
    176. 9 this.addColumn.apply(this, args);
    177. +
    178. });
    179. },
    180. /**
    181. -
    182. * @private
    183. -
    184. * SQL DDL fragment containing the column creation
    185. -
    186. * SQL for all given columns, used inside a CREATE TABLE block.
    187. -
    188. */
    189. -
    190. __columnListSql:function (generator) {
    191. -
    192. 218 return generator.columns.map(hitch(this, "__columnDefinitionSql")).concat(generator.constraints.map(hitch(this, "__constraintDefinitionSql"))).join(this._static.COMMA_SEPARATOR);
    193. +
    194. * Adds an index to a table for the given columns
    195. +
    196. *
    197. +
    198. * <p>
    199. +
    200. * This method is a shortcut to {@link patio.Database#alterTable} with an
    201. +
    202. * addIndex call.
    203. +
    204. * </p>
    205. +
    206. * @example
    207. +
    208. * DB.addIndex("test", "name", {unique : true});
    209. +
    210. * //=> 'CREATE UNIQUE INDEX test_name_index ON test (name)'
    211. +
    212. * DB.addIndex("test", ["one", "two"]);
    213. +
    214. * //=> ''CREATE INDEX test_one_two_index ON test (one, two)''
    215. +
    216. *
    217. +
    218. * @param {String} table the table to add the index to.
    219. +
    220. * @param {String|String[]} columns the name of the column/s to create an index for.
    221. +
    222. * @param {Object} [options] additional options that can be used when adding an index.
    223. +
    224. * @param {Boolean} [options.unique] set to true if this this index should have a UNIQUE constraint.
    225. +
    226. * @param {Boolean} [options.ignoreErrors] set to true to ignore errors.
    227. +
    228. *
    229. +
    230. * @return {Promise} a promise that is resolved when the CREATE INDEX action is complete.
    231. +
    232. * */
    233. +
    234. addIndex:function (table, columns, options) {
    235. +
    236. 4 options = options || {};
    237. +
    238. 4 var ignoreErrors = options.ignoreErrors === true;
    239. +
    240. 4 var ret = new Promise();
    241. +
    242. 4 this.alterTable(table,function () {
    243. +
    244. 4 this.addIndex(columns, options);
    245. +
    246. }).then(ret, function (err) {
    247. +
    248. 0 if (!ignoreErrors) {
    249. +
    250. 0 ret.errback(err);
    251. +
    252. } else {
    253. +
    254. 0 ret.callback();
    255. +
    256. }
    257. +
    258. });
    259. +
    260. 4 return ret.promise();
    261. },
    262. /**
    263. -
    264. * @private
    265. -
    266. *SQL DDL fragment for column foreign key references (column constraints)
    267. -
    268. */
    269. -
    270. __columnReferencesColumnConstraintSql:function (column) {
    271. -
    272. 30 return this.__columnReferencesSql(column);
    273. +
    274. * Removes a column from the specified table.
    275. +
    276. * <p>
    277. +
    278. * This method is a shortcut to {@link patio.Database#alterTable} with an
    279. +
    280. * dropColumn call.
    281. +
    282. * </p>
    283. +
    284. *
    285. +
    286. * @example
    287. +
    288. * DB.dropColumn("items", "category");
    289. +
    290. * //=> 'ALTER TABLE items DROP COLUMN category',
    291. +
    292. *
    293. +
    294. * @param {String|patio.sql.Identifier} table the table to alter.
    295. +
    296. * @param {String|patio.sql.Identifier} column the column to drop.
    297. +
    298. *
    299. +
    300. * @return {Promise} a promise that is resolved once the DROP COLUMN action is complete.
    301. +
    302. * */
    303. +
    304. dropColumn:function (table, column) {
    305. +
    306. 3 column = argsToArray(arguments).slice(1);
    307. +
    308. 3 return this.alterTable(table, function () {
    309. +
    310. 3 this.dropColumn.apply(this, column);
    311. +
    312. });
    313. },
    314. /**
    315. -
    316. * @private
    317. -
    318. * SQL DDL fragment for column foreign key references
    319. -
    320. */
    321. -
    322. __columnReferencesSql:function (column) {
    323. -
    324. 38 var sql = format(" REFERENCES %s", this.__quoteSchemaTable(column.table));
    325. -
    326. 38 column.key && (sql += format("(%s)", array.toArray(column.key).map(this.__quoteIdentifier, this).join(this._static.COMMA_SEPARATOR)));
    327. -
    328. 38 column.onDelete && (sql += format(" ON DELETE %s", this.__onDeleteClause(column.onDelete)));
    329. -
    330. 38 column.onUpdate && (sql += format(" ON UPDATE %s", this.__onUpdateClause(column.onUpdate)));
    331. -
    332. 38 column.deferrable && (sql += " DEFERRABLE INITIALLY DEFERRED");
    333. -
    334. 38 return sql;
    335. +
    336. * Removes an index for the given table and column/s.
    337. +
    338. *
    339. +
    340. * <p>
    341. +
    342. * This method is a shortcut to {@link patio.Database#alterTable} with an
    343. +
    344. * dropIndex call.
    345. +
    346. * </p>
    347. +
    348. *
    349. +
    350. * @example
    351. +
    352. * DB.dropIndex("posts", "title");
    353. +
    354. * //=>'DROP INDEX posts_title_index
    355. +
    356. * DB.dropIndex("posts", ["author", "title"]);
    357. +
    358. * //'DROP INDEX posts_author_title_index'
    359. +
    360. *
    361. +
    362. * @param {String|patio.sql.Identifier} table the table to alter.
    363. +
    364. * @param {String|patio.sql.Identifier} column the name of the column/s the index was created from.
    365. +
    366. *
    367. +
    368. * @return {Promise} a promise that is resolved once the DROP INDEX action is complete.
    369. +
    370. * */
    371. +
    372. dropIndex:function (table, columns, options) {
    373. +
    374. 1 var args = argsToArray(arguments).slice(1);
    375. +
    376. 1 return this.alterTable(table, function () {
    377. +
    378. 1 this.dropIndex.apply(this, args);
    379. +
    380. });
    381. },
    382. /**
    383. -
    384. * @private
    385. -
    386. * SQL DDL fragment for table foreign key references (table constraints)
    387. +
    388. * Renames a column in the specified table.
    389. +
    390. *
    391. +
    392. * <p>
    393. +
    394. * This method is a shortcut to {@link patio.Database#alterTable} with an
    395. +
    396. * renameColumn call.
    397. +
    398. * </p>
    399. +
    400. *
    401. +
    402. * @example
    403. +
    404. * DB.renameColumn("items", "cntr", "counter");
    405. +
    406. * //=> ALTER TABLE items RENAME COLUMN cntr TO counter
    407. +
    408. *
    409. +
    410. * @param {String|patio.sql.Identifier} table the table to alter.
    411. +
    412. * @param {String|patio.sql.Identifier} column the name of the column to rename.
    413. +
    414. * @param {String|patio.sql.Identifier} newColumn the new name of the column.
    415. +
    416. *
    417. +
    418. * @return {Promise} a promise that is resolved once the RENAME COLUMN action is complete.
    419. * */
    420. -
    421. __columnReferencesTableConstraintSql:function (constraint) {
    422. -
    423. 6 return format("FOREIGN KEY %s%s", this.literal(constraint.columns.map(function (c) {
    424. -
    425. 6 return isString(c) ? sql.stringToIdentifier(c) : c;
    426. -
    427. })), this.__columnReferencesSql(constraint));
    428. +
    429. renameColumn:function (table, column, newColumn) {
    430. +
    431. 4 var args = argsToArray(arguments).slice(1);
    432. +
    433. 4 return this.alterTable(table, function () {
    434. +
    435. 4 this.renameColumn.apply(this, args);
    436. +
    437. });
    438. },
    439. -
    440. /**
    441. -
    442. * @private
    443. -
    444. * SQL DDL fragment specifying a constraint on a table.
    445. -
    446. */
    447. -
    448. __constraintDefinitionSql:function (constraint) {
    449. -
    450. 6 var ret = [constraint.name ? format("CONSTRAINT %s ", this.__quoteIdentifier(constraint.name)) : ""];
    451. -
    452. 6 switch (constraint.type) {
    453. -
    454. case "check":
    455. -
    456. 0 var check = constraint.check;
    457. -
    458. 0 ret.push(format("CHECK %s", this.__filterExpr(isArray(check) && check.length === 1 ? check[0] : check)));
    459. -
    460. 0 break;
    461. -
    462. case "primaryKey":
    463. -
    464. 0 ret.push(format("PRIMARY KEY %s", this.literal(constraint.columns.map(function (c) {
    465. -
    466. 0 return isString(c) ? sql.stringToIdentifier(c) : c;
    467. -
    468. }))));
    469. -
    470. 0 break;
    471. -
    472. case "foreignKey":
    473. -
    474. 6 ret.push(this.__columnReferencesTableConstraintSql(constraint));
    475. -
    476. 6 break;
    477. -
    478. case "unique":
    479. -
    480. 0 ret.push(format("UNIQUE %s", this.literal(constraint.columns.map(function (c) {
    481. -
    482. 0 return isString(c) ? sql.stringToIdentifier(c) : c;
    483. -
    484. }))));
    485. -
    486. 0 break;
    487. -
    488. default:
    489. -
    490. 0 throw new DatabaseError(format("Invalid constriant type %s, should be 'check', 'primaryKey', foreignKey', or 'unique'", constraint.type));
    491. -
    492. }
    493. -
    494. 6 return ret.join("");
    495. -
    496. },
    497. /**
    498. -
    499. * @private
    500. -
    501. * Execute the create table statements using the generator.
    502. +
    503. *Sets the default value for the given column in the given table:
    504. +
    505. *
    506. +
    507. * <p>
    508. +
    509. * This method is a shortcut to {@link patio.Database#alterTable} with an
    510. +
    511. * setColumnDefault call.
    512. +
    513. * </p>
    514. +
    515. *
    516. +
    517. * @example
    518. +
    519. * DB.setColumnDefault("items", "category", "misc");
    520. +
    521. * //=> ALTER TABLE items ALTER COLUMN category SET DEFAULT 'misc'
    522. +
    523. *
    524. +
    525. * @param {String|patio.sql.Identifier} table the table to alter.
    526. +
    527. * @param {String|patio.sql.Identifier} column the name of the column to set the DEFAULT on.
    528. +
    529. * @param def the new default value of the column.
    530. +
    531. *
    532. +
    533. * @return {Promise} a promise that is resolved once the SET DEFAULT action is complete.
    534. * */
    535. -
    536. __createTableFromGenerator:function (name, generator, options) {
    537. -
    538. 218 return this.executeDdl(this.__createTableSql(name, generator, options));
    539. +
    540. setColumnDefault:function (table, column, def) {
    541. +
    542. 1 var args = argsToArray(arguments).slice(1);
    543. +
    544. 1 return this.alterTable(table, function () {
    545. +
    546. 1 this.setColumnDefault.apply(this, args);
    547. +
    548. });
    549. },
    550. -
    551. /**
    552. -
    553. * @private
    554. -
    555. * Execute the create index statements using the generator.
    556. -
    557. * */
    558. -
    559. __createTableIndexesFromGenerator:function (name, generator, options) {
    560. -
    561. 218 var e = options.ignoreIndexErrors;
    562. -
    563. 218 var ret = new Promise();
    564. -
    565. 218 var promises = generator.indexes.map(function (index) {
    566. -
    567. 18 var ps = this.__indexSqlList(name, [index]).map(this.executeDdl, this);
    568. -
    569. 18 return new PromiseList(ps);
    570. -
    571. }, this);
    572. -
    573. 218 if (promises.length) {
    574. -
    575. 16 new PromiseList(promises).then(ret, hitch(ret, e ? "callback" : "errback"));
    576. -
    577. } else {
    578. -
    579. 202 ret.callback();
    580. -
    581. }
    582. -
    583. 218 return ret.promise();
    584. -
    585. },
    586. /**
    587. -
    588. * @private
    589. -
    590. * DDL statement for creating a table with the given name, columns, and options
    591. +
    592. * Set the data type for the given column in the given table:
    593. +
    594. * <p>
    595. +
    596. * This method is a shortcut to {@link patio.Database#alterTable} with an
    597. +
    598. * setColumnType call.
    599. +
    600. * </p>
    601. +
    602. *
    603. +
    604. * @example
    605. +
    606. * DB.setColumnType("items", "category", String);
    607. +
    608. * //=> ALTER TABLE items ALTER COLUMN category TYPE varchar(255)
    609. +
    610. *
    611. +
    612. * @param {String|patio.sql.Identifier} table the table to alter.
    613. +
    614. * @param {String|patio.sql.Identifier} column the name of the column to set the TYPE on.
    615. +
    616. * @param type the datatype of the column.
    617. +
    618. *
    619. +
    620. * @return {Promise} a promise that is resolved once the SET TYPE action is complete.
    621. * */
    622. -
    623. __createTableSql:function (name, generator, options) {
    624. -
    625. 218 return format("CREATE %sTABLE %s (%s)", options.temp ? this.temporaryTableSql : "", this.__quoteSchemaTable(name), this.__columnListSql(generator));
    626. +
    627. setColumnType:function (table, column, type) {
    628. +
    629. 3 var args = argsToArray(arguments).slice(1);
    630. +
    631. 3 return this.alterTable(table, function () {
    632. +
    633. 3 this.setColumnType.apply(this, args);
    634. +
    635. });
    636. },
    637. +
    638. /**
    639. -
    640. * @private
    641. -
    642. * Default index name for the table and columns, may be too long
    643. -
    644. * for certain databases.
    645. -
    646. */
    647. -
    648. __defaultIndexName:function (tableName, columns) {
    649. -
    650. 25 var parts = this.__schemaAndTable(tableName);
    651. -
    652. 25 var schema = parts[0], table = parts[1];
    653. -
    654. 25 var index = [];
    655. -
    656. 25 if (schema && schema !== this.defaultSchema) {
    657. -
    658. 0 index.push(schema);
    659. +
    660. * Alters the given table with the specified block.
    661. +
    662. * <p>
    663. +
    664. * <b>NOTE:</b> The block is invoked in the scope of the table that is being altered. The block
    665. +
    666. * is also called with the table as the first argument. Within the block you must use
    667. +
    668. * <b>this</b>(If the block has not been bound to a different scope), or the table object
    669. +
    670. * that is passed in for all alter table operations. See {@link patio.AlterTableGenerator} for
    671. +
    672. * avaiable operations.
    673. +
    674. * </p>
    675. +
    676. *
    677. +
    678. * <p>
    679. +
    680. * <b>Note</b> that addColumn accepts all the options available for column
    681. +
    682. * definitions using createTable, and addIndex accepts all the options
    683. +
    684. * available for index definition.
    685. +
    686. * </p>
    687. +
    688. *
    689. +
    690. * @example
    691. +
    692. * //using the table object
    693. +
    694. * DB.alterTable("items", function(table){
    695. +
    696. * //you must use the passed in table object.
    697. +
    698. * table.addColumn("category", "text", {default : 'javascript'});
    699. +
    700. * table.dropColumn("category");
    701. +
    702. * table.renameColumn("cntr", "counter");
    703. +
    704. * table.setColumnType("value", "float");
    705. +
    706. * table.setColumnDefault("value", "float");
    707. +
    708. * table.addIndex(["group", "category"]);
    709. +
    710. * table.dropIndex [:group, :category]
    711. +
    712. * });
    713. +
    714. *
    715. +
    716. * //using this
    717. +
    718. * DB.alterTable("items", function(){
    719. +
    720. * this.addColumn("category", "text", {default : 'javascript'});
    721. +
    722. * this.dropColumn("category");
    723. +
    724. * this.renameColumn("cntr", "counter");
    725. +
    726. * this.setColumnType("value", "float");
    727. +
    728. * this.setColumnDefault("value", "float");
    729. +
    730. * this.addIndex(["group", "category"]);
    731. +
    732. * this.dropIndex [:group, :category]
    733. +
    734. * });
    735. +
    736. *
    737. +
    738. * //This will not work
    739. +
    740. * DB.alterTable("items", comb.hitch(someObject, function(){
    741. +
    742. * //This is called in the scope of someObject so this
    743. +
    744. * //will not work and will throw an error
    745. +
    746. * this.addColumn("category", "text", {default : 'javascript'});
    747. +
    748. * }));
    749. +
    750. *
    751. +
    752. * //This will work
    753. +
    754. * DB.alterTable("items", comb.hitch(someObject, function(table){
    755. +
    756. * //This is called in the scope of someObject so you must
    757. +
    758. * //use the table argument
    759. +
    760. * table.category("text", {default : 'javascript'});
    761. +
    762. * }));
    763. +
    764. *
    765. +
    766. *
    767. +
    768. * @param {String|patio.sql.Identifier} table to the table to perform the ALTER TABLE operations on.
    769. +
    770. * @param {Function} block the block to invoke for the ALTER TABLE operations
    771. +
    772. *
    773. +
    774. * @return {Promise} a promise that is resolved once all ALTER TABLE operations have completed.
    775. +
    776. * */
    777. +
    778. alterTable:function (name, generator, block) {
    779. +
    780. 84 if (isFunction(generator)) {
    781. +
    782. 84 block = generator;
    783. +
    784. 84 generator = new AlterTableGenerator(this, block);
    785. }
    786. -
    787. 25 index.push(table);
    788. -
    789. 25 index = index.concat(columns.map(function (c) {
    790. -
    791. 28 return isString(c) ? c : this.literal(c).replace(/\W/g, "");
    792. -
    793. }, this));
    794. -
    795. 25 index.push("index");
    796. -
    797. 25 return index.join(this._static.UNDERSCORE);
    798. -
    799. +
    800. 84 var ret = new Promise();
    801. +
    802. 84 this.__alterTableSqlList(name, generator.operations).then(hitch(this, function (res) {
    803. +
    804. 84 var sqls = array.flatten(res.map(function (r) {
    805. +
    806. 93 return r[1];
    807. +
    808. }));
    809. +
    810. 84 var l = sqls.length;
    811. +
    812. 84 var drop = hitch(this, function (i) {
    813. +
    814. 179 if (i < l) {
    815. +
    816. 95 var sql = sqls[i++];
    817. +
    818. 95 this.executeDdl(sql).then(hitch(this, drop, i), ret);
    819. +
    820. } else {
    821. +
    822. 84 this.removeCachedSchema(name);
    823. +
    824. 84 ret.callback();
    825. +
    826. }
    827. +
    828. });
    829. +
    830. 84 drop(0);
    831. +
    832. }));
    833. +
    834. 84 return ret.promise();
    835. },
    836. /**
    837. -
    838. * @private
    839. -
    840. * The SQL to drop an index for the table.
    841. -
    842. * */
    843. -
    844. __dropIndexSql:function (table, op) {
    845. -
    846. 2 return format("DROP INDEX %s", this.__quoteIdentifier(op.name || this.__defaultIndexName(table, op.columns)));
    847. -
    848. },
    849. -
    850. -
    851. /**
    852. -
    853. * @private
    854. +
    855. * Creates a table with the columns given in the provided block:
    856. +
    857. *
    858. +
    859. * <p>
    860. +
    861. * <b>NOTE:</b> The block is invoked in the scope of the table that is being created. The block
    862. +
    863. * is also called with the table as the first argument. Within the block you must use
    864. +
    865. * <b>this</b>(If the block has not been bound to a different scope), or the table object
    866. +
    867. * that is passed in for all create table operations. See {@link patio.SchemaGenerator} for
    868. +
    869. * available operations.
    870. +
    871. * </p>
    872. +
    873. *
    874. +
    875. *
    876. +
    877. * @example
    878. +
    879. *
    880. +
    881. * //using the table to create the table
    882. +
    883. * DB.createTable("posts", function(table){
    884. +
    885. * table.primaryKey("id");
    886. +
    887. * table.column('title", "text");
    888. +
    889. * //you may also invoke the column name as
    890. +
    891. * //function on the table
    892. +
    893. * table.content(String);
    894. +
    895. * table.index(title);
    896. +
    897. * });
    898. +
    899. *
    900. +
    901. * //using this to create the table
    902. +
    903. * DB.createTable("posts", function(){
    904. +
    905. * this.primaryKey("id");
    906. +
    907. * this.column('title", "text");
    908. +
    909. * //you may also invoke the column name as
    910. +
    911. * //function on the table
    912. +
    913. * this.content(String);
    914. +
    915. * this.index(title);
    916. +
    917. * });
    918. +
    919. *
    920. +
    921. * @param {String|patio.sql.Identifier} name the name of the table to create.
    922. +
    923. * @param {Object} [options] an optional options object
    924. +
    925. * @param {Boolean} [options.temp] set to true if this table is a TEMPORARY table.
    926. +
    927. * @param {Boolean} [options.ignoreIndexErrors] Ignore any errors when creating indexes.
    928. +
    929. * @param {Function} block the block to invoke when creating the table.
    930. +
    931. *
    932. +
    933. * @return {Promise} a promise that is resolved when the CREATE TABLE action is completed.
    934. *
    935. -
    936. * SQL DDL statement to drop the table with the given name.
    937. -
    938. **/
    939. -
    940. __dropTableSql:function (name) {
    941. -
    942. 210 return format("DROP TABLE %s", this.__quoteSchemaTable(name));
    943. -
    944. },
    945. -
    946. -
    947. /**
    948. -
    949. * @private
    950. -
    951. * Proxy the filterExpr call to the dataset, used for creating constraints.
    952. -
    953. * */
    954. -
    955. __filterExpr:function (args, block) {
    956. -
    957. 2 var ds = this.__schemaUtiltyDataset;
    958. -
    959. 2 return ds.literal(ds._filterExpr.apply(ds, arguments));
    960. -
    961. },
    962. -
    963. -
    964. -
    965. /**
    966. -
    967. * @private
    968. -
    969. * SQL DDL statement for creating an index for the table with the given name
    970. -
    971. * and index specifications.
    972. */
    973. -
    974. __indexDefinitionSql:function (tableName, index) {
    975. -
    976. 7 var indexName = index.name || this.__defaultIndexName(tableName, index.columns);
    977. -
    978. 7 if (index.type) {
    979. -
    980. 0 throw new DatabaseError("Index types are not supported for this database");
    981. -
    982. 7 } else if (index.where) {
    983. -
    984. 0 throw new DatabaseError("Partial indexes are not supported for this database");
    985. -
    986. } else {
    987. -
    988. 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) {
    989. -
    990. 8 return isString(c) ? new Identifier(c) : c;
    991. -
    992. })));
    993. +
    994. createTable:function (name, options, block) {
    995. +
    996. 218 if (isFunction(options)) {
    997. +
    998. 208 block = options;
    999. +
    1000. 208 options = {};
    1001. +
    1002. }
    1003. +
    1004. 218 this.removeCachedSchema(name);
    1005. +
    1006. 218 if (isInstanceOf(options, SchemaGenerator)) {
    1007. +
    1008. 0 options = {generator:options};
    1009. }
    1010. +
    1011. 218 var generator = options.generator || new SchemaGenerator(this, block);
    1012. +
    1013. 218 return this.__createTableFromGenerator(name, generator, options)
    1014. +
    1015. .chain(hitch(this, "__createTableIndexesFromGenerator", name, generator, options))
    1016. +
    1017. .promise();
    1018. },
    1019. /**
    1020. -
    1021. * Array of SQL DDL statements, one for each index specification,
    1022. -
    1023. * for the given table.
    1024. -
    1025. */
    1026. -
    1027. __indexSqlList:function (tableName, indexes) {
    1028. -
    1029. 18 return indexes.map(hitch(this, this.__indexDefinitionSql, tableName));
    1030. +
    1031. * Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.
    1032. +
    1033. * <p>
    1034. +
    1035. * See {@link patio.Database#createTable} for parameter types.
    1036. +
    1037. * </p>
    1038. +
    1039. *
    1040. +
    1041. * @example
    1042. +
    1043. * // DROP TABLE a
    1044. +
    1045. * // CREATE TABLE a (a integer)
    1046. +
    1047. * DB.forceCreateTable("a", function(){
    1048. +
    1049. * this.a("integer");
    1050. +
    1051. * });
    1052. +
    1053. *
    1054. +
    1055. **/
    1056. +
    1057. forceCreateTable:function (name, options, block) {
    1058. +
    1059. 18 return this.dropTable(name)
    1060. +
    1061. .chainBoth(hitch(this, this.createTable, name, options, block))
    1062. +
    1063. .promise();
    1064. },
    1065. -
    1066. /**
    1067. -
    1068. * @private
    1069. -
    1070. * SQL DDL ON DELETE fragment to use, based on the given action.
    1071. -
    1072. *The following actions are recognized:
    1073. -
    1074. * <ul>
    1075. -
    1076. * <li>cascade - Delete rows referencing this row.</li>
    1077. -
    1078. * <li>noAction (default) - Raise an error if other rows reference this
    1079. -
    1080. * row, allow deferring of the integrity check.
    1081. -
    1082. * </li>
    1083. -
    1084. * <li>restrict - Raise an error if other rows reference this row,
    1085. -
    1086. * but do not allow deferring the integrity check.</li>
    1087. -
    1088. * <li> setDefault - Set columns referencing this row to their default value.</li>
    1089. -
    1090. * <li>setNull - Set columns referencing this row to NULL.</li>
    1091. -
    1092. * </ul>
    1093. -
    1094. */
    1095. -
    1096. __onDeleteClause:function (action) {
    1097. -
    1098. 23 return this._static[action.toUpperCase()] || this._static.NO_ACTION;
    1099. -
    1100. },
    1101. /**
    1102. -
    1103. * @private
    1104. -
    1105. * SQL DDL ON UPDATE fragment to use, based on the given action.
    1106. -
    1107. *The following actions are recognized:
    1108. -
    1109. * <ul>
    1110. -
    1111. * <li>cascade - Delete rows referencing this row.</li>
    1112. -
    1113. * <li>noAction (default) - Raise an error if other rows reference this
    1114. -
    1115. * row, allow deferring of the integrity check.
    1116. -
    1117. * </li>
    1118. -
    1119. * <li>restrict - Raise an error if other rows reference this row,
    1120. -
    1121. * but do not allow deferring the integrity check.</li>
    1122. -
    1123. * <li> setDefault - Set columns referencing this row to their default value.</li>
    1124. -
    1125. * <li>setNull - Set columns referencing this row to NULL.</li>
    1126. -
    1127. * </ul>
    1128. +
    1129. * Creates the table unless the table already exists.
    1130. +
    1131. * <p>
    1132. +
    1133. * See {@link patio.Database#createTable} for parameter types.
    1134. +
    1135. * </p>
    1136. */
    1137. -
    1138. __onUpdateClause:function (action) {
    1139. -
    1140. 1 return this._static[action.toUpperCase()] || this._static.NO_ACTION;
    1141. +
    1142. createTableUnlessExists:function (name, options, block) {
    1143. +
    1144. 0 var ret = new Promise();
    1145. +
    1146. 0 this.tableExists(name).then(hitch(this, function (exists) {
    1147. +
    1148. 0 if (!exists) {
    1149. +
    1150. 0 this.createTable(name, options, block).then(ret);
    1151. +
    1152. } else {
    1153. +
    1154. 0 ret.callback();
    1155. +
    1156. }
    1157. +
    1158. }), ret);
    1159. +
    1160. 0 return ret.promise();
    1161. },
    1162. /**
    1163. -
    1164. * @private
    1165. -
    1166. * Proxy the quoteSchemaTable method to the dataset
    1167. -
    1168. * */
    1169. -
    1170. __quoteSchemaTable:function (table) {
    1171. -
    1172. 2212 return this.__schemaUtiltyDataset.quoteSchemaTable(table);
    1173. +
    1174. * Creates a view, replacing it if it already exists:
    1175. +
    1176. * @example
    1177. +
    1178. * DB.createOrReplaceView("cheapItems", "SELECT * FROM items WHERE price < 100");
    1179. +
    1180. * //=> CREATE OR REPLACE VIEW cheapItems AS SELECT * FROM items WHERE price < 100
    1181. +
    1182. * DB.createOrReplaceView("miscItems", DB[:items].filter({category : 'misc'}));
    1183. +
    1184. * //=> CREATE OR REPLACE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'
    1185. +
    1186. *
    1187. +
    1188. * @param {String|patio.sql.Identifier} name the name of the view to create.
    1189. +
    1190. * @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the
    1191. +
    1192. * view.
    1193. +
    1194. *
    1195. +
    1196. * @return {Promise} a promise that is resolved when the CREATE OR REPLACE VIEW action is complete.
    1197. +
    1198. **/
    1199. +
    1200. createOrReplaceView:function (name, source) {
    1201. +
    1202. 4 if (isInstanceOf(source, Dataset)) {
    1203. +
    1204. 2 source = source.sql;
    1205. +
    1206. }
    1207. +
    1208. 4 var ret = new Promise();
    1209. +
    1210. 4 this.executeDdl(format("CREATE OR REPLACE VIEW %s AS %s", this.__quoteSchemaTable(name), source)).then(hitch(this, function () {
    1211. +
    1212. 4 this.removeCachedSchema(name);
    1213. +
    1214. 4 ret.callback();
    1215. +
    1216. }), ret);
    1217. +
    1218. 4 return ret.promise();
    1219. },
    1220. /**
    1221. -
    1222. * @private
    1223. -
    1224. * Proxy the quoteIdentifier method to the dataset, used for quoting tables and columns.
    1225. -
    1226. * */
    1227. -
    1228. __quoteIdentifier:function (v) {
    1229. -
    1230. 846 return this.__schemaUtiltyDataset.quoteIdentifier(v);
    1231. +
    1232. * Creates a view based on a dataset or an SQL string:
    1233. +
    1234. * @example
    1235. +
    1236. * DB.createView("cheapItems", "SELECT * FROM items WHERE price < 100");
    1237. +
    1238. * //=> CREATE VIEW cheapItems AS SELECT * FROM items WHERE price < 100
    1239. +
    1240. * DB.createView("miscItems", DB[:items].filter({category : 'misc'}));
    1241. +
    1242. * //=> CREATE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'
    1243. +
    1244. *
    1245. +
    1246. * @param {String|patio.sql.Identifier} name the name of the view to create.
    1247. +
    1248. * @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the
    1249. +
    1250. * view.
    1251. +
    1252. **/
    1253. +
    1254. createView:function (name, source) {
    1255. +
    1256. 4 if (isInstanceOf(source, Dataset)) {
    1257. +
    1258. 2 source = source.sql;
    1259. +
    1260. }
    1261. +
    1262. 4 return this.executeDdl(format("CREATE VIEW %s AS %s", this.__quoteSchemaTable(name), source));
    1263. },
    1264. /**
    1265. -
    1266. * @private
    1267. -
    1268. * SQL DDL statement for renaming a table.
    1269. -
    1270. * */
    1271. -
    1272. __renameTableSql:function (name, newName) {
    1273. -
    1274. 1 return format("ALTER TABLE %s RENAME TO %s", this.__quoteSchemaTable(name), this.__quoteSchemaTable(newName));
    1275. -
    1276. },
    1277. +
    1278. * Drops one or more tables corresponding to the given names.
    1279. +
    1280. *
    1281. +
    1282. * @example
    1283. +
    1284. * DB.dropTable("test");
    1285. +
    1286. * //=>'DROP TABLE test'
    1287. +
    1288. * DB.dropTable("a", "bb", "ccc");
    1289. +
    1290. * //=>'DROP TABLE a',
    1291. +
    1292. * //=>'DROP TABLE bb',
    1293. +
    1294. * //=>'DROP TABLE ccc'
    1295. +
    1296. *
    1297. +
    1298. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the tables
    1299. +
    1300. * to drop.
    1301. +
    1302. *
    1303. +
    1304. * @return {Promise} a promise that is resolved once all tables have been dropped.
    1305. +
    1306. **/
    1307. +
    1308. dropTable:function (names) {
    1309. +
    1310. 81 var ret = new Promise(), l = names.length;
    1311. +
    1312. 81 if (isArray(names)) {
    1313. +
    1314. 48 var drop = hitch(this, function (i) {
    1315. +
    1316. 104 if (i < l) {
    1317. +
    1318. 65 var name = names[i++];
    1319. +
    1320. 65 this.executeDdl(this.__dropTableSql(name)).then(hitch(this, function () {
    1321. +
    1322. 56 this.removeCachedSchema(name);
    1323. +
    1324. 56 drop(i);
    1325. +
    1326. }), ret);
    1327. +
    1328. } else {
    1329. +
    1330. 39 ret.callback();
    1331. +
    1332. }
    1333. +
    1334. });
    1335. +
    1336. 48 drop(0);
    1337. +
    1338. 48 return ret.promise();
    1339. +
    1340. } else {
    1341. +
    1342. 33 return this.dropTable(argsToArray(arguments).filter(function (t) {
    1343. +
    1344. 38 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
    1345. +
    1346. }));
    1347. -
    1348. /**
    1349. -
    1350. * @private
    1351. -
    1352. * Remove the cached schemaUtilityDataset, because the identifier
    1353. -
    1354. * quoting has changed.
    1355. -
    1356. */
    1357. -
    1358. __resetSchemaUtilityDataset:function () {
    1359. -
    1360. 0 this.__schemaUtiltyDs = null;
    1361. +
    1362. }
    1363. },
    1364. /**
    1365. -
    1366. * @private
    1367. -
    1368. * Split the schema information from the table
    1369. -
    1370. * */
    1371. -
    1372. __schemaAndTable:function (tableName) {
    1373. -
    1374. 244 return this.__schemaUtiltyDataset.schemaAndTable(tableName);
    1375. +
    1376. * Forcible drops one or more tables corresponding to the given names, ignoring errors.
    1377. +
    1378. *
    1379. +
    1380. * @example
    1381. +
    1382. * DB.dropTable("test");
    1383. +
    1384. * //=>'DROP TABLE test'
    1385. +
    1386. * DB.dropTable("a", "bb", "ccc");
    1387. +
    1388. * //=>'DROP TABLE a',
    1389. +
    1390. * //=>'DROP TABLE bb',
    1391. +
    1392. * //=>'DROP TABLE ccc'
    1393. +
    1394. *
    1395. +
    1396. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the tables
    1397. +
    1398. * to drop.
    1399. +
    1400. *
    1401. +
    1402. * @return {Promise} a promise that is resolved once all tables have been dropped.
    1403. +
    1404. **/
    1405. +
    1406. forceDropTable:function (names) {
    1407. +
    1408. 158 var ret = new Promise(), l = names.length;
    1409. +
    1410. 158 if (isArray(names)) {
    1411. +
    1412. 96 var drop = hitch(this, function (i) {
    1413. +
    1414. 241 if (i < l) {
    1415. +
    1416. 145 var name = names[i++];
    1417. +
    1418. 145 this.executeDdl(this.__dropTableSql(name)).both(hitch(this, function () {
    1419. +
    1420. 145 this.removeCachedSchema(name);
    1421. +
    1422. 145 drop(i);
    1423. +
    1424. }));
    1425. +
    1426. } else {
    1427. +
    1428. 96 ret.callback();
    1429. +
    1430. }
    1431. +
    1432. });
    1433. +
    1434. 96 drop(0);
    1435. +
    1436. } else {
    1437. +
    1438. 62 this.forceDropTable(argsToArray(arguments).filter(function (t) {
    1439. +
    1440. 62 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
    1441. +
    1442. })).both(ret);
    1443. +
    1444. }
    1445. +
    1446. 158 return ret.promise();
    1447. },
    1448. /**
    1449. -
    1450. * @private
    1451. -
    1452. * Return true if the given column schema represents an autoincrementing primary key.
    1453. +
    1454. * Drops one or more views corresponding to the given names.
    1455. *
    1456. -
    1457. */
    1458. -
    1459. _schemaAutoincrementingPrimaryKey:function (schema) {
    1460. -
    1461. 0 return !!schema.primaryKey;
    1462. +
    1463. * @example
    1464. +
    1465. * DB.dropView("test_view");
    1466. +
    1467. * //=>'DROP VIEW test_view'
    1468. +
    1469. * DB.dropTable("test_view_1", "test_view_2", "test_view_3");
    1470. +
    1471. * //=>'DROP VIEW test_view_1',
    1472. +
    1473. * //=>'DROP VIEW test_view_2',
    1474. +
    1475. * //=>'DROP VIEW test_view_3'
    1476. +
    1477. *
    1478. +
    1479. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the views
    1480. +
    1481. * to drop.
    1482. +
    1483. *
    1484. +
    1485. * @return {Promise} a promise that is resolved once the view/s have been dropped.
    1486. +
    1487. **/
    1488. +
    1489. dropView:function (names) {
    1490. +
    1491. 8 var ret = new Promise(), l = names.length;
    1492. +
    1493. 8 if (isArray(names)) {
    1494. +
    1495. 4 var drop = hitch(this, function (i) {
    1496. +
    1497. 8 if (i < l) {
    1498. +
    1499. 4 var name = names[i++];
    1500. +
    1501. 4 this.executeDdl(format("DROP VIEW %s", this.__quoteSchemaTable(name))).then(hitch(this, function () {
    1502. +
    1503. 4 this.removeCachedSchema(name);
    1504. +
    1505. 4 drop(i);
    1506. +
    1507. }), ret);
    1508. +
    1509. } else {
    1510. +
    1511. 4 ret.callback();
    1512. +
    1513. }
    1514. +
    1515. });
    1516. +
    1517. 4 drop(0);
    1518. +
    1519. 4 return ret.promise();
    1520. +
    1521. } else {
    1522. +
    1523. 4 return this.dropView(argsToArray(arguments).filter(function (t) {
    1524. +
    1525. 4 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
    1526. +
    1527. }));
    1528. +
    1529. +
    1530. }
    1531. },
    1532. /**
    1533. -
    1534. * @private
    1535. -
    1536. * SQL fragment specifying the type of a given column.
    1537. -
    1538. * */
    1539. -
    1540. typeLiteral:function (column) {
    1541. -
    1542. 690 return this.__typeLiteralGeneric(column);
    1543. +
    1544. * Renames a table.
    1545. +
    1546. *
    1547. +
    1548. * @example
    1549. +
    1550. * comb.executeInOrder(DB, function(DB){
    1551. +
    1552. * DB.tables(); //=> ["items"]
    1553. +
    1554. * DB.renameTable("items", "old_items");
    1555. +
    1556. * //=>'ALTER TABLE items RENAME TO old_items'
    1557. +
    1558. * DB.tables; //=> ["old_items"]
    1559. +
    1560. *});
    1561. +
    1562. *
    1563. +
    1564. * @param {String|patio.sql.Identifier} name the name of the table to rename
    1565. +
    1566. * @param {String|patio.sql.Identifier} newName the new name of the table
    1567. +
    1568. * @return {Promise} a promise that is resolved once the table is renamed.
    1569. +
    1570. **/
    1571. +
    1572. renameTable:function (name, newName) {
    1573. +
    1574. 2 return this.executeDdl(this.__renameTableSql(name, newName)).chain(hitch(this, function () {
    1575. +
    1576. 2 this.removeCachedSchema(name);
    1577. +
    1578. })).promise();
    1579. +
    1580. },
    1581. /**
    1582. * @private
    1583. -
    1584. * SQL fragment specifying the full type of a column,
    1585. -
    1586. * consider the type with possible modifiers.
    1587. -
    1588. */
    1589. -
    1590. __typeLiteralGeneric:function (column) {
    1591. -
    1592. 690 var type = column.type;
    1593. -
    1594. 690 var meth = "__typeLiteralGeneric";
    1595. -
    1596. 690 var isStr = isString(type);
    1597. -
    1598. 690 var proper = isStr ? type.charAt(0).toUpperCase() + type.substr(1) : null;
    1599. -
    1600. 690 if (type === String || (isStr && type.match(/string/i))) {
    1601. -
    1602. 198 meth += "String";
    1603. -
    1604. 492 } else if ((isStr && type.match(/number/i)) || type === Number) {
    1605. -
    1606. 12 meth += "Numeric";
    1607. -
    1608. 480 } else if ((isStr && type.match(/datetime/i)) || type === DateTime) {
    1609. -
    1610. 8 meth += "DateTime";
    1611. -
    1612. 472 } else if ((isStr && type.match(/date/i)) || type === Date) {
    1613. -
    1614. 11 meth += "Date";
    1615. -
    1616. 461 } else if ((isStr && type.match(/year/i)) || type === Year) {
    1617. -
    1618. 2 meth += "Year";
    1619. -
    1620. 459 } else if ((isStr && type.match(/timestamp/i))|| type === TimeStamp) {
    1621. -
    1622. 4 meth += "Timestamp";
    1623. -
    1624. 455 } else if ((isStr && type.match(/time/i)) || type === Time) {
    1625. -
    1626. 2 meth += "Time";
    1627. -
    1628. 453 } else if ((isStr && type.match(/decimal/i)) || type === Decimal) {
    1629. -
    1630. 2 meth += "Decimal";
    1631. -
    1632. 451 } else if ((isStr && type.match(/float/i)) || type === Float) {
    1633. -
    1634. 15 meth += "Float";
    1635. -
    1636. 436 } else if ((isStr && type.match(/boolean/i)) || type === Boolean) {
    1637. -
    1638. 5 meth += "Boolean";
    1639. -
    1640. 431 } else if ((isStr && type.match(/buffer/i)) || type === Buffer) {
    1641. -
    1642. 29 meth += "Blob";
    1643. -
    1644. 402 } else if (isStr && isFunction(this[meth + proper])) {
    1645. -
    1646. 138 meth += proper;
    1647. -
    1648. } else {
    1649. -
    1650. 264 return this.__typeLiteralSpecific(column);
    1651. +
    1652. * The SQL to execute to modify the DDL for the given table name. op
    1653. +
    1654. * should be one of the operations returned by the AlterTableGenerator.
    1655. +
    1656. * */
    1657. +
    1658. __alterTableSql:function (table, op) {
    1659. +
    1660. 83 var ret = new Promise();
    1661. +
    1662. 83 var quotedName = op.name ? this.__quoteIdentifier(op.name) : null;
    1663. +
    1664. 83 var alterTableOp = null;
    1665. +
    1666. 83 switch (op.op) {
    1667. +
    1668. case "addColumn":
    1669. +
    1670. 13 alterTableOp = format("ADD COLUMN %s", this.__columnDefinitionSql(op));
    1671. +
    1672. 13 break;
    1673. +
    1674. case "dropColumn":
    1675. +
    1676. 4 alterTableOp = format("DROP COLUMN %s", quotedName);
    1677. +
    1678. 4 break;
    1679. +
    1680. case "renameColumn":
    1681. +
    1682. 52 alterTableOp = format("RENAME COLUMN %s TO %s", quotedName, this.__quoteIdentifier(op.newName));
    1683. +
    1684. 52 break;
    1685. +
    1686. case "setColumnType":
    1687. +
    1688. 3 alterTableOp = format("ALTER COLUMN %s TYPE %s", quotedName, this.typeLiteral(op));
    1689. +
    1690. 3 break;
    1691. +
    1692. case "setColumnDefault":
    1693. +
    1694. 2 alterTableOp = format("ALTER COLUMN %s SET DEFAULT %s", quotedName, this.literal(op["default"]));
    1695. +
    1696. 2 break;
    1697. +
    1698. case "setColumnNull":
    1699. +
    1700. 0 alterTableOp = format("ALTER COLUMN %s %s NOT NULL", quotedName, op["null"] ? "DROP" : "SET");
    1701. +
    1702. 0 break;
    1703. +
    1704. case "addIndex":
    1705. +
    1706. 5 return ret.callback(this.__indexDefinitionSql(table, op)).promise();
    1707. +
    1708. case "dropIndex":
    1709. +
    1710. 2 return ret.callback(this.__dropIndexSql(table, op)).promise();
    1711. +
    1712. case "addConstraint":
    1713. +
    1714. 1 alterTableOp = format("ADD %s", this.__constraintDefinitionSql(op));
    1715. +
    1716. 1 break;
    1717. +
    1718. case "dropConstraint":
    1719. +
    1720. 0 alterTableOp = format("DROP CONSTRAINT %s", quotedName);
    1721. +
    1722. 0 break;
    1723. +
    1724. default :
    1725. +
    1726. 1 throw new DatabaseError("Invalid altertable operator");
    1727. }
    1728. -
    1729. 426 return this[meth](column);
    1730. +
    1731. 75 return ret.callback(format("ALTER TABLE %s %s", this.__quoteSchemaTable(table), alterTableOp)).promise();
    1732. },
    1733. /**
    1734. * @private
    1735. -
    1736. * patio uses the date type by default for Dates.
    1737. -
    1738. * <ul>
    1739. -
    1740. * <li>if onlyTime is present then time is used</li>
    1741. -
    1742. * <li>if timeStamp is present then timestamp is used,</li>
    1743. -
    1744. * <li>if dateTime is present then datetime is used</li>
    1745. -
    1746. * <li>if yearOnly is present then year is used</li>
    1747. -
    1748. * <li>else date is used</li>
    1749. -
    1750. * </ul>
    1751. -
    1752. */
    1753. -
    1754. __typeLiteralGenericDate:function (column) {
    1755. -
    1756. 11 var type = column.type, ret = "date";
    1757. -
    1758. 11 if (column.onlyTime) {
    1759. -
    1760. 2 ret = "time";
    1761. -
    1762. 9 } else if (column.timeStamp) {
    1763. -
    1764. 2 ret = "timestamp";
    1765. -
    1766. 7 } else if (column.dateTime) {
    1767. -
    1768. 2 ret = "datetime";
    1769. -
    1770. 5 } else if (column.yearOnly) {
    1771. -
    1772. 2 ret = "year";
    1773. -
    1774. }
    1775. -
    1776. 11 return ret;
    1777. +
    1778. * Array of SQL DDL modification statements for the given table,
    1779. +
    1780. * corresponding to the DDL changes specified by the operations.
    1781. +
    1782. * */
    1783. +
    1784. __alterTableSqlList:function (table, operations) {
    1785. +
    1786. 84 return new PromiseList(operations.map(hitch(this, "__alterTableSql", table)));
    1787. },
    1788. /**
    1789. * @private
    1790. -
    1791. * * patio uses the blob type by default for Buffers.
    1792. +
    1793. * SQL DDL fragment containing the column creation SQL for the given column.
    1794. +
    1795. *
    1796. +
    1797. * @param column
    1798. */
    1799. -
    1800. __typeLiteralGenericBlob:function (column) {
    1801. -
    1802. 25 return "blob";
    1803. +
    1804. __columnDefinitionSql:function (column) {
    1805. +
    1806. 644 var sql = [format("%s %s", this.__quoteIdentifier(column.name), this.typeLiteral(column))];
    1807. +
    1808. 644 column.unique && sql.push(this._static.UNIQUE);
    1809. +
    1810. 644 (column.allowNull === false || column["null"] === false) && sql.push(this._static.NOT_NULL);
    1811. +
    1812. 644 (column.allowNull === true || column["null"] === true) && sql.push(this._static.NULL);
    1813. +
    1814. 644 !isUndefined(column["default"]) && sql.push(format(" DEFAULT %s", this.literal(column["default"])));
    1815. +
    1816. 644 column.primaryKey && sql.push(this._static.PRIMARY_KEY);
    1817. +
    1818. 644 column.autoIncrement && sql.push(" " + this.autoIncrementSql);
    1819. +
    1820. 644 column.table && sql.push(this.__columnReferencesColumnConstraintSql(column));
    1821. +
    1822. 644 return sql.join("");
    1823. },
    1824. /**
    1825. * @private
    1826. -
    1827. * * patio uses the year type by default for {@link patio.sql.DateTime}.
    1828. +
    1829. * SQL DDL fragment containing the column creation
    1830. +
    1831. * SQL for all given columns, used inside a CREATE TABLE block.
    1832. */
    1833. -
    1834. __typeLiteralGenericDateTime:function (column) {
    1835. -
    1836. 2 return "datetime";
    1837. +
    1838. __columnListSql:function (generator) {
    1839. +
    1840. 218 return generator.columns.map(hitch(this, "__columnDefinitionSql")).concat(generator.constraints.map(hitch(this, "__constraintDefinitionSql"))).join(this._static.COMMA_SEPARATOR);
    1841. },
    1842. /**
    1843. * @private
    1844. -
    1845. * patio uses the timestamp type by default for {@link patio.sql.TimeStamp}.
    1846. +
    1847. *SQL DDL fragment for column foreign key references (column constraints)
    1848. */
    1849. -
    1850. __typeLiteralGenericTimestamp:function (column) {
    1851. -
    1852. 4 return "timestamp";
    1853. +
    1854. __columnReferencesColumnConstraintSql:function (column) {
    1855. +
    1856. 30 return this.__columnReferencesSql(column);
    1857. },
    1858. /**
    1859. * @private
    1860. -
    1861. * patio uses the time type by default for {@link patio.sql.Time}.
    1862. +
    1863. * SQL DDL fragment for column foreign key references
    1864. */
    1865. -
    1866. __typeLiteralGenericTime:function (column) {
    1867. -
    1868. 2 return "time";
    1869. +
    1870. __columnReferencesSql:function (column) {
    1871. +
    1872. 38 var sql = format(" REFERENCES %s", this.__quoteSchemaTable(column.table));
    1873. +
    1874. 38 column.key && (sql += format("(%s)", array.toArray(column.key).map(this.__quoteIdentifier, this).join(this._static.COMMA_SEPARATOR)));
    1875. +
    1876. 38 column.onDelete && (sql += format(" ON DELETE %s", this.__onDeleteClause(column.onDelete)));
    1877. +
    1878. 38 column.onUpdate && (sql += format(" ON UPDATE %s", this.__onUpdateClause(column.onUpdate)));
    1879. +
    1880. 38 column.deferrable && (sql += " DEFERRABLE INITIALLY DEFERRED");
    1881. +
    1882. 38 return sql;
    1883. },
    1884. /**
    1885. * @private
    1886. -
    1887. * patio uses the year type by default for {@link patio.sql.Year}.
    1888. -
    1889. */
    1890. -
    1891. __typeLiteralGenericYear:function (column) {
    1892. -
    1893. 2 return "year";
    1894. +
    1895. * SQL DDL fragment for table foreign key references (table constraints)
    1896. +
    1897. * */
    1898. +
    1899. __columnReferencesTableConstraintSql:function (constraint) {
    1900. +
    1901. 6 return format("FOREIGN KEY %s%s", this.literal(constraint.columns.map(function (c) {
    1902. +
    1903. 6 return isString(c) ? sql.stringToIdentifier(c) : c;
    1904. +
    1905. })), this.__columnReferencesSql(constraint));
    1906. },
    1907. /**
    1908. * @private
    1909. -
    1910. * patio uses the boolean type by default for Boolean class
    1911. -
    1912. * */
    1913. -
    1914. __typeLiteralGenericBoolean:function (column) {
    1915. -
    1916. 3 return "boolean";
    1917. +
    1918. * SQL DDL fragment specifying a constraint on a table.
    1919. +
    1920. */
    1921. +
    1922. __constraintDefinitionSql:function (constraint) {
    1923. +
    1924. 6 var ret = [constraint.name ? format("CONSTRAINT %s ", this.__quoteIdentifier(constraint.name)) : ""];
    1925. +
    1926. 6 switch (constraint.type) {
    1927. +
    1928. case "check":
    1929. +
    1930. 0 var check = constraint.check;
    1931. +
    1932. 0 ret.push(format("CHECK %s", this.__filterExpr(isArray(check) && check.length === 1 ? check[0] : check)));
    1933. +
    1934. 0 break;
    1935. +
    1936. case "primaryKey":
    1937. +
    1938. 0 ret.push(format("PRIMARY KEY %s", this.literal(constraint.columns.map(function (c) {
    1939. +
    1940. 0 return isString(c) ? sql.stringToIdentifier(c) : c;
    1941. +
    1942. }))));
    1943. +
    1944. 0 break;
    1945. +
    1946. case "foreignKey":
    1947. +
    1948. 6 ret.push(this.__columnReferencesTableConstraintSql(constraint));
    1949. +
    1950. 6 break;
    1951. +
    1952. case "unique":
    1953. +
    1954. 0 ret.push(format("UNIQUE %s", this.literal(constraint.columns.map(function (c) {
    1955. +
    1956. 0 return isString(c) ? sql.stringToIdentifier(c) : c;
    1957. +
    1958. }))));
    1959. +
    1960. 0 break;
    1961. +
    1962. default:
    1963. +
    1964. 0 throw new DatabaseError(format("Invalid constriant type %s, should be 'check', 'primaryKey', foreignKey', or 'unique'", constraint.type));
    1965. +
    1966. }
    1967. +
    1968. 6 return ret.join("");
    1969. },
    1970. /**
    1971. * @private
    1972. -
    1973. * patio uses the numeric type by default for NumericTypes
    1974. -
    1975. * If a size is given, it is used, otherwise, it will default to whatever
    1976. -
    1977. * the database default is for an unsized value.
    1978. -
    1979. * <ul>
    1980. -
    1981. * <li> if isInt is present the int is used</li>
    1982. -
    1983. * <li> if isDouble is present then double precision is used</li>
    1984. -
    1985. * </ul>
    1986. -
    1987. */
    1988. -
    1989. __typeLiteralGenericNumeric:function (column) {
    1990. -
    1991. 10 return column.size ? format("numeric(%s)", array.toArray(column.size).join(', ')) : column.isInt ? "integer" : column.isDouble ? "double precision" : "numeric";
    1992. +
    1993. * Execute the create table statements using the generator.
    1994. +
    1995. * */
    1996. +
    1997. __createTableFromGenerator:function (name, generator, options) {
    1998. +
    1999. 218 return this.executeDdl(this.__createTableSql(name, generator, options));
    2000. },
    2001. -
    2002. /**
    2003. * @private
    2004. -
    2005. */
    2006. -
    2007. __typeLiteralGenericFloat:function (column) {
    2008. -
    2009. 15 return "double precision";
    2010. +
    2011. * Execute the create index statements using the generator.
    2012. +
    2013. * */
    2014. +
    2015. __createTableIndexesFromGenerator:function (name, generator, options) {
    2016. +
    2017. 218 var e = options.ignoreIndexErrors;
    2018. +
    2019. 218 var ret = new Promise();
    2020. +
    2021. 218 var promises = generator.indexes.map(function (index) {
    2022. +
    2023. 18 var ps = this.__indexSqlList(name, [index]).map(this.executeDdl, this);
    2024. +
    2025. 18 return new PromiseList(ps);
    2026. +
    2027. }, this);
    2028. +
    2029. 218 if (promises.length) {
    2030. +
    2031. 16 new PromiseList(promises).then(ret, hitch(ret, e ? "callback" : "errback"));
    2032. +
    2033. } else {
    2034. +
    2035. 202 ret.callback();
    2036. +
    2037. }
    2038. +
    2039. 218 return ret.promise();
    2040. },
    2041. /**
    2042. * @private
    2043. -
    2044. */
    2045. -
    2046. __typeLiteralGenericDecimal:function (column) {
    2047. -
    2048. 2 return "double precision";
    2049. +
    2050. * DDL statement for creating a table with the given name, columns, and options
    2051. +
    2052. * */
    2053. +
    2054. __createTableSql:function (name, generator, options) {
    2055. +
    2056. 218 return format("CREATE %sTABLE %s (%s)", options.temp ? this.temporaryTableSql : "", this.__quoteSchemaTable(name), this.__columnListSql(generator));
    2057. },
    2058. /**
    2059. * @private
    2060. -
    2061. * patio uses the varchar type by default for Strings. If a
    2062. -
    2063. * size isn't present, patio assumes a size of 255. If the
    2064. -
    2065. * fixed option is used, patio uses the char type. If the
    2066. -
    2067. * text option is used, patio uses the :text type.
    2068. +
    2069. * Default index name for the table and columns, may be too long
    2070. +
    2071. * for certain databases.
    2072. */
    2073. -
    2074. __typeLiteralGenericString:function (column) {
    2075. -
    2076. 41 return column.text ? "text" : format("%s(%s)", column.fixed ? "char" : "varchar", column.size || 255);
    2077. +
    2078. __defaultIndexName:function (tableName, columns) {
    2079. +
    2080. 25 var parts = this.__schemaAndTable(tableName);
    2081. +
    2082. 25 var schema = parts[0], table = parts[1];
    2083. +
    2084. 25 var index = [];
    2085. +
    2086. 25 if (schema && schema !== this.defaultSchema) {
    2087. +
    2088. 0 index.push(schema);
    2089. +
    2090. }
    2091. +
    2092. 25 index.push(table);
    2093. +
    2094. 25 index = index.concat(columns.map(function (c) {
    2095. +
    2096. 28 return isString(c) ? c : this.literal(c).replace(/\W/g, "");
    2097. +
    2098. }, this));
    2099. +
    2100. 25 index.push("index");
    2101. +
    2102. 25 return index.join(this._static.UNDERSCORE);
    2103. +
    2104. },
    2105. /**
    2106. * @private
    2107. -
    2108. * SQL fragment for the given type of a column if the column is not one of the
    2109. -
    2110. * generic types specified with a native javascript type class.
    2111. -
    2112. */
    2113. -
    2114. __typeLiteralSpecific:function (column) {
    2115. -
    2116. 336 var type = column.type;
    2117. -
    2118. 336 type = type === "double" ? "double precision" : type;
    2119. -
    2120. 336 if (type === "varchar") {
    2121. -
    2122. 7 column.size = isNumber(column.size) ? column.size : 255;
    2123. -
    2124. }
    2125. -
    2126. 336 var elements = column.size || column.elements;
    2127. -
    2128. 336 return format("%s%s%s", type, elements ? this.literal(toArray(elements)) : "", column.unsigned ? " UNSIGNED" : "");
    2129. +
    2130. * The SQL to drop an index for the table.
    2131. +
    2132. * */
    2133. +
    2134. __dropIndexSql:function (table, op) {
    2135. +
    2136. 2 return format("DROP INDEX %s", this.__quoteIdentifier(op.name || this.__defaultIndexName(table, op.columns)));
    2137. },
    2138. -
    2139. /**@ignore*/
    2140. -
    2141. getters:{
    2142. -
    2143. /**@lends patio.Database.prototype*/
    2144. -
    2145. /**
    2146. -
    2147. * @private
    2148. -
    2149. * The SQL string specify the autoincrement property, generally used by
    2150. -
    2151. * primary keys.
    2152. -
    2153. *
    2154. -
    2155. * @field
    2156. -
    2157. * */
    2158. -
    2159. autoIncrementSql:function () {
    2160. -
    2161. 10 return this._static.AUTOINCREMENT;
    2162. -
    2163. },
    2164. -
    2165. -
    2166. /**
    2167. -
    2168. * @private
    2169. -
    2170. * @field
    2171. -
    2172. * */
    2173. -
    2174. temporaryTableSql:function () {
    2175. -
    2176. 3 return this._static.TEMPORARY;
    2177. -
    2178. },
    2179. -
    2180. -
    2181. /**
    2182. -
    2183. * @private
    2184. -
    2185. * @field
    2186. -
    2187. * */
    2188. -
    2189. __schemaUtiltyDataset:function () {
    2190. -
    2191. 3304 this.__schemaUtiltyDs = this.__schemaUtiltyDs || this.dataset;
    2192. -
    2193. 3304 return this.__schemaUtiltyDs;
    2194. -
    2195. }
    2196. -
    2197. }
    2198. +
    2199. /**
    2200. +
    2201. * @private
    2202. +
    2203. *
    2204. +
    2205. * SQL DDL statement to drop the table with the given name.
    2206. +
    2207. **/
    2208. +
    2209. __dropTableSql:function (name) {
    2210. +
    2211. 210 return format("DROP TABLE %s", this.__quoteSchemaTable(name));
    2212. +
    2213. },
    2214. -
    2215. },
    2216. +
    2217. /**
    2218. +
    2219. * @private
    2220. +
    2221. * Proxy the filterExpr call to the dataset, used for creating constraints.
    2222. +
    2223. * */
    2224. +
    2225. __filterExpr:function (args, block) {
    2226. +
    2227. 2 var ds = this.__schemaUtiltyDataset;
    2228. +
    2229. 2 return ds.literal(ds._filterExpr.apply(ds, arguments));
    2230. +
    2231. },
    2232. -
    2233. "static":{
    2234. -
    2235. /**@lends patio.Database*/
    2236. /**
    2237. -
    2238. *Default AUTO INCREMENT SQL
    2239. +
    2240. * @private
    2241. +
    2242. * SQL DDL statement for creating an index for the table with the given name
    2243. +
    2244. * and index specifications.
    2245. */
    2246. -
    2247. AUTOINCREMENT:'AUTOINCREMENT',
    2248. +
    2249. __indexDefinitionSql:function (tableName, index) {
    2250. +
    2251. 7 var indexName = index.name || this.__defaultIndexName(tableName, index.columns);
    2252. +
    2253. 7 if (index.type) {
    2254. +
    2255. 0 throw new DatabaseError("Index types are not supported for this database");
    2256. +
    2257. 7 } else if (index.where) {
    2258. +
    2259. 0 throw new DatabaseError("Partial indexes are not supported for this database");
    2260. +
    2261. } else {
    2262. +
    2263. 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) {
    2264. +
    2265. 8 return isString(c) ? new Identifier(c) : c;
    2266. +
    2267. })));
    2268. +
    2269. }
    2270. +
    2271. },
    2272. /**
    2273. -
    2274. *Default CASCACDE SQL
    2275. +
    2276. * Array of SQL DDL statements, one for each index specification,
    2277. +
    2278. * for the given table.
    2279. */
    2280. -
    2281. CASCADE:'CASCADE',
    2282. +
    2283. __indexSqlList:function (tableName, indexes) {
    2284. +
    2285. 18 return indexes.map(hitch(this, this.__indexDefinitionSql, tableName));
    2286. +
    2287. },
    2288. /**
    2289. -
    2290. *Default comma
    2291. +
    2292. * @private
    2293. +
    2294. * SQL DDL ON DELETE fragment to use, based on the given action.
    2295. +
    2296. *The following actions are recognized:
    2297. +
    2298. * <ul>
    2299. +
    2300. * <li>cascade - Delete rows referencing this row.</li>
    2301. +
    2302. * <li>noAction (default) - Raise an error if other rows reference this
    2303. +
    2304. * row, allow deferring of the integrity check.
    2305. +
    2306. * </li>
    2307. +
    2308. * <li>restrict - Raise an error if other rows reference this row,
    2309. +
    2310. * but do not allow deferring the integrity check.</li>
    2311. +
    2312. * <li> setDefault - Set columns referencing this row to their default value.</li>
    2313. +
    2314. * <li>setNull - Set columns referencing this row to NULL.</li>
    2315. +
    2316. * </ul>
    2317. */
    2318. -
    2319. COMMA_SEPARATOR:', ',
    2320. +
    2321. __onDeleteClause:function (action) {
    2322. +
    2323. 23 return this._static[action.toUpperCase()] || this._static.NO_ACTION;
    2324. +
    2325. },
    2326. /**
    2327. -
    2328. *Default NO ACTION SQL
    2329. +
    2330. * @private
    2331. +
    2332. * SQL DDL ON UPDATE fragment to use, based on the given action.
    2333. +
    2334. *The following actions are recognized:
    2335. +
    2336. * <ul>
    2337. +
    2338. * <li>cascade - Delete rows referencing this row.</li>
    2339. +
    2340. * <li>noAction (default) - Raise an error if other rows reference this
    2341. +
    2342. * row, allow deferring of the integrity check.
    2343. +
    2344. * </li>
    2345. +
    2346. * <li>restrict - Raise an error if other rows reference this row,
    2347. +
    2348. * but do not allow deferring the integrity check.</li>
    2349. +
    2350. * <li> setDefault - Set columns referencing this row to their default value.</li>
    2351. +
    2352. * <li>setNull - Set columns referencing this row to NULL.</li>
    2353. +
    2354. * </ul>
    2355. */
    2356. -
    2357. NO_ACTION:'NO ACTION',
    2358. +
    2359. __onUpdateClause:function (action) {
    2360. +
    2361. 1 return this._static[action.toUpperCase()] || this._static.NO_ACTION;
    2362. +
    2363. },
    2364. /**
    2365. -
    2366. *Default NOT NULL SQL
    2367. -
    2368. */
    2369. -
    2370. NOT_NULL:' NOT NULL',
    2371. +
    2372. * @private
    2373. +
    2374. * Proxy the quoteSchemaTable method to the dataset
    2375. +
    2376. * */
    2377. +
    2378. __quoteSchemaTable:function (table) {
    2379. +
    2380. 2212 return this.__schemaUtiltyDataset.quoteSchemaTable(table);
    2381. +
    2382. },
    2383. /**
    2384. -
    2385. * Default NULL SQL
    2386. -
    2387. */
    2388. -
    2389. NULL:' NULL',
    2390. +
    2391. * @private
    2392. +
    2393. * Proxy the quoteIdentifier method to the dataset, used for quoting tables and columns.
    2394. +
    2395. * */
    2396. +
    2397. __quoteIdentifier:function (v) {
    2398. +
    2399. 846 return this.__schemaUtiltyDataset.quoteIdentifier(v);
    2400. +
    2401. },
    2402. /**
    2403. -
    2404. *Default PRIMARY KEY SQL
    2405. -
    2406. */
    2407. -
    2408. PRIMARY_KEY:' PRIMARY KEY',
    2409. +
    2410. * @private
    2411. +
    2412. * SQL DDL statement for renaming a table.
    2413. +
    2414. * */
    2415. +
    2416. __renameTableSql:function (name, newName) {
    2417. +
    2418. 1 return format("ALTER TABLE %s RENAME TO %s", this.__quoteSchemaTable(name), this.__quoteSchemaTable(newName));
    2419. +
    2420. },
    2421. /**
    2422. -
    2423. *Default RESTRICT SQL
    2424. +
    2425. * @private
    2426. +
    2427. * Remove the cached schemaUtilityDataset, because the identifier
    2428. +
    2429. * quoting has changed.
    2430. */
    2431. -
    2432. RESTRICT:'RESTRICT',
    2433. +
    2434. __resetSchemaUtilityDataset:function () {
    2435. +
    2436. 0 this.__schemaUtiltyDs = null;
    2437. +
    2438. },
    2439. /**
    2440. -
    2441. *Default SET DEFAULT SQL
    2442. -
    2443. */
    2444. -
    2445. SET_DEFAULT:'SET DEFAULT',
    2446. -
    2447. +
    2448. * @private
    2449. +
    2450. * Split the schema information from the table
    2451. +
    2452. * */
    2453. +
    2454. __schemaAndTable:function (tableName) {
    2455. +
    2456. 244 return this.__schemaUtiltyDataset.schemaAndTable(tableName);
    2457. +
    2458. },
    2459. +
    2460. /**
    2461. -
    2462. *Default SET NULL SQL
    2463. +
    2464. * @private
    2465. +
    2466. * Return true if the given column schema represents an autoincrementing primary key.
    2467. +
    2468. *
    2469. */
    2470. -
    2471. SET_NULL:'SET NULL',
    2472. +
    2473. _schemaAutoincrementingPrimaryKey:function (schema) {
    2474. +
    2475. 0 return !!schema.primaryKey;
    2476. +
    2477. },
    2478. /**
    2479. -
    2480. *Default TEMPORARY SQL
    2481. -
    2482. */
    2483. -
    2484. TEMPORARY:'TEMPORARY ',
    2485. +
    2486. * @private
    2487. +
    2488. * SQL fragment specifying the type of a given column.
    2489. +
    2490. * */
    2491. +
    2492. typeLiteral:function (column) {
    2493. +
    2494. 690 return this.__typeLiteralGeneric(column);
    2495. +
    2496. },
    2497. /**
    2498. -
    2499. *Default UNDERSCORE SQL, used in index creation.
    2500. +
    2501. * @private
    2502. +
    2503. * SQL fragment specifying the full type of a column,
    2504. +
    2505. * consider the type with possible modifiers.
    2506. */
    2507. -
    2508. UNDERSCORE:'_',
    2509. +
    2510. __typeLiteralGeneric:function (column) {
    2511. +
    2512. 690 var type = column.type;
    2513. +
    2514. 690 var meth = "__typeLiteralGeneric";
    2515. +
    2516. 690 var isStr = isString(type);
    2517. +
    2518. 690 var proper = isStr ? type.charAt(0).toUpperCase() + type.substr(1) : null;
    2519. +
    2520. 690 if (type === String || (isStr && type.match(/string/i))) {
    2521. +
    2522. 198 meth += "String";
    2523. +
    2524. 492 } else if ((isStr && type.match(/number/i)) || type === Number) {
    2525. +
    2526. 12 meth += "Numeric";
    2527. +
    2528. 480 } else if ((isStr && type.match(/datetime/i)) || type === DateTime) {
    2529. +
    2530. 8 meth += "DateTime";
    2531. +
    2532. 472 } else if ((isStr && type.match(/date/i)) || type === Date) {
    2533. +
    2534. 11 meth += "Date";
    2535. +
    2536. 461 } else if ((isStr && type.match(/year/i)) || type === Year) {
    2537. +
    2538. 2 meth += "Year";
    2539. +
    2540. 459 } else if ((isStr && type.match(/timestamp/i))|| type === TimeStamp) {
    2541. +
    2542. 4 meth += "Timestamp";
    2543. +
    2544. 455 } else if ((isStr && type.match(/time/i)) || type === Time) {
    2545. +
    2546. 2 meth += "Time";
    2547. +
    2548. 453 } else if ((isStr && type.match(/decimal/i)) || type === Decimal) {
    2549. +
    2550. 2 meth += "Decimal";
    2551. +
    2552. 451 } else if ((isStr && type.match(/float/i)) || type === Float) {
    2553. +
    2554. 15 meth += "Float";
    2555. +
    2556. 436 } else if ((isStr && type.match(/boolean/i)) || type === Boolean) {
    2557. +
    2558. 5 meth += "Boolean";
    2559. +
    2560. 431 } else if ((isStr && type.match(/buffer/i)) || type === Buffer) {
    2561. +
    2562. 29 meth += "Blob";
    2563. +
    2564. 402 } else if (isStr && isFunction(this[meth + proper])) {
    2565. +
    2566. 138 meth += proper;
    2567. +
    2568. } else {
    2569. +
    2570. 264 return this.__typeLiteralSpecific(column);
    2571. +
    2572. }
    2573. +
    2574. 426 return this[meth](column);
    2575. +
    2576. },
    2577. /**
    2578. -
    2579. *Default UNIQUE SQL
    2580. +
    2581. * @private
    2582. +
    2583. * patio uses the date type by default for Dates.
    2584. +
    2585. * <ul>
    2586. +
    2587. * <li>if onlyTime is present then time is used</li>
    2588. +
    2589. * <li>if timeStamp is present then timestamp is used,</li>
    2590. +
    2591. * <li>if dateTime is present then datetime is used</li>
    2592. +
    2593. * <li>if yearOnly is present then year is used</li>
    2594. +
    2595. * <li>else date is used</li>
    2596. +
    2597. * </ul>
    2598. */
    2599. -
    2600. UNIQUE:' UNIQUE',
    2601. +
    2602. __typeLiteralGenericDate:function (column) {
    2603. +
    2604. 11 var type = column.type, ret = "date";
    2605. +
    2606. 11 if (column.onlyTime) {
    2607. +
    2608. 2 ret = "time";
    2609. +
    2610. 9 } else if (column.timeStamp) {
    2611. +
    2612. 2 ret = "timestamp";
    2613. +
    2614. 7 } else if (column.dateTime) {
    2615. +
    2616. 2 ret = "datetime";
    2617. +
    2618. 5 } else if (column.yearOnly) {
    2619. +
    2620. 2 ret = "year";
    2621. +
    2622. }
    2623. +
    2624. 11 return ret;
    2625. +
    2626. },
    2627. /**
    2628. -
    2629. * Default UNSIGNED SQL
    2630. +
    2631. * @private
    2632. +
    2633. * * patio uses the blob type by default for Buffers.
    2634. */
    2635. -
    2636. UNSIGNED:' UNSIGNED'
    2637. -
    2638. -
    2639. }
    2640. -
    2641. }).as(module);
    2642. -
    -
    - -
    - - - - - -
    -
    errors.js
    -
    -
    - Coverage89.47 - SLOC81 - LOC19 - Missed2 -
    -
    -
    1. 1var patio = exports;
    2. +
    3. __typeLiteralGenericBlob:function (column) {
    4. +
    5. 25 return "blob";
    6. +
    7. },
    8. +
    9. /**
    10. +
    11. * @private
    12. +
    13. * * patio uses the year type by default for {@link patio.sql.DateTime}.
    14. +
    15. */
    16. +
    17. __typeLiteralGenericDateTime:function (column) {
    18. +
    19. 2 return "datetime";
    20. +
    21. },
    22. -
    23. /**
    24. -
    25. * @class Thrown if a function is not impltemened
    26. -
    27. *
    28. -
    29. * @param {String} message the message to show.
    30. -
    31. */
    32. -
    33. 1patio.NotImplemented = function(message) {
    34. -
    35. 4 return new Error("Not Implemented : " + message);
    36. -
    37. };
    38. +
    39. /**
    40. +
    41. * @private
    42. +
    43. * patio uses the timestamp type by default for {@link patio.sql.TimeStamp}.
    44. +
    45. */
    46. +
    47. __typeLiteralGenericTimestamp:function (column) {
    48. +
    49. 4 return "timestamp";
    50. +
    51. },
    52. -
    53. /**
    54. -
    55. * @class Thrown if there is an Expression Error.
    56. -
    57. *
    58. -
    59. * @param {String} message the message to show.
    60. -
    61. */
    62. -
    63. 1patio.ExpressionError = function(message) {
    64. -
    65. 0 return new Error("Expression Error :" + message);
    66. -
    67. };
    68. +
    69. /**
    70. +
    71. * @private
    72. +
    73. * patio uses the time type by default for {@link patio.sql.Time}.
    74. +
    75. */
    76. +
    77. __typeLiteralGenericTime:function (column) {
    78. +
    79. 2 return "time";
    80. +
    81. },
    82. -
    83. /**
    84. -
    85. * @class Thrown if there is a Query Error.
    86. -
    87. *
    88. -
    89. * @param {String} message the message to show.
    90. -
    91. */
    92. -
    93. 1patio.QueryError = function(message) {
    94. -
    95. 121 return new Error("QueryError : " + message);
    96. -
    97. };
    98. +
    99. /**
    100. +
    101. * @private
    102. +
    103. * patio uses the year type by default for {@link patio.sql.Year}.
    104. +
    105. */
    106. +
    107. __typeLiteralGenericYear:function (column) {
    108. +
    109. 2 return "year";
    110. +
    111. },
    112. -
    113. /**
    114. -
    115. * @class Thrown if there is a Dataset Error.
    116. -
    117. *
    118. -
    119. * @param {String} message the message to show.
    120. -
    121. */
    122. -
    123. 1patio.DatasetError = function(message) {
    124. -
    125. 7 return new Error("DatasetError : " + message);
    126. -
    127. };
    128. +
    129. /**
    130. +
    131. * @private
    132. +
    133. * patio uses the boolean type by default for Boolean class
    134. +
    135. * */
    136. +
    137. __typeLiteralGenericBoolean:function (column) {
    138. +
    139. 3 return "boolean";
    140. +
    141. },
    142. -
    143. /**
    144. -
    145. * @class Thrown if there is a Database Error.
    146. -
    147. *
    148. -
    149. * @param {String} message the message to show.
    150. -
    151. */
    152. -
    153. 1patio.DatabaseError = function(message) {
    154. -
    155. 5 return new Error("Database error : " + message);
    156. -
    157. };
    158. +
    159. /**
    160. +
    161. * @private
    162. +
    163. * patio uses the numeric type by default for NumericTypes
    164. +
    165. * If a size is given, it is used, otherwise, it will default to whatever
    166. +
    167. * the database default is for an unsized value.
    168. +
    169. * <ul>
    170. +
    171. * <li> if isInt is present the int is used</li>
    172. +
    173. * <li> if isDouble is present then double precision is used</li>
    174. +
    175. * </ul>
    176. +
    177. */
    178. +
    179. __typeLiteralGenericNumeric:function (column) {
    180. +
    181. 10 return column.size ? format("numeric(%s)", array.toArray(column.size).join(', ')) : column.isInt ? "integer" : column.isDouble ? "double precision" : "numeric";
    182. +
    183. },
    184. -
    185. /**
    186. -
    187. * @class Thrown if there is a unexpected Error.
    188. -
    189. *
    190. -
    191. * @param {String} message the message to show.
    192. -
    193. */
    194. -
    195. 1patio.PatioError = function(message) {
    196. -
    197. 31 return new Error("Patio error : " + message);
    198. -
    199. };
    200. -
    201. /**
    202. -
    203. * @class Thrown if there is a error thrown within a model.
    204. -
    205. * @param {String} message the message to show.
    206. -
    207. */
    208. -
    209. 1patio.ModelError = function(message) {
    210. -
    211. 2 return new Error("Model error : " + message);
    212. -
    213. };
    214. +
    215. /**
    216. +
    217. * @private
    218. +
    219. */
    220. +
    221. __typeLiteralGenericFloat:function (column) {
    222. +
    223. 15 return "double precision";
    224. +
    225. },
    226. -
    227. /**
    228. -
    229. * @class Thrown if there is an error when loading/creating/deleteing an association.
    230. -
    231. * @param {String} message the message to show.
    232. -
    233. */
    234. -
    235. 1patio.AssociationError = function(message) {
    236. -
    237. 0 return new Error("Association error : " + message);
    238. -
    239. };
    240. +
    241. /**
    242. +
    243. * @private
    244. +
    245. */
    246. +
    247. __typeLiteralGenericDecimal:function (column) {
    248. +
    249. 2 return "double precision";
    250. +
    251. },
    252. +
    253. /**
    254. +
    255. * @private
    256. +
    257. * patio uses the varchar type by default for Strings. If a
    258. +
    259. * size isn't present, patio assumes a size of 255. If the
    260. +
    261. * fixed option is used, patio uses the char type. If the
    262. +
    263. * text option is used, patio uses the :text type.
    264. +
    265. */
    266. +
    267. __typeLiteralGenericString:function (column) {
    268. +
    269. 41 return column.text ? "text" : format("%s(%s)", column.fixed ? "char" : "varchar", column.size || 255);
    270. +
    271. },
    272. -
    273. /**
    274. -
    275. * Thrown if there is an error when performing a migration.
    276. -
    277. * @param {String} message the message to show.
    278. -
    279. */
    280. -
    281. 1patio.MigrationError = function(message) {
    282. -
    283. 2 return new Error("Migration error : " + message);
    284. -
    285. };
    +
  • /**
  • +
  • * @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) {
  • +
  • 336 var type = column.type;
  • +
  • 336 type = type === "double" ? "double precision" : type;
  • +
  • 336 if (type === "varchar") {
  • +
  • 7 column.size = isNumber(column.size) ? column.size : 255;
  • +
  • }
  • +
  • 336 var elements = column.size || column.elements;
  • +
  • 336 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 () {
  • +
  • 3304 this.__schemaUtiltyDs = this.__schemaUtiltyDs || this.dataset;
  • +
  • 3304 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);
  • +
  • +
    + +
    + + + + + +
    +
    errors.js
    +
    +
    + Coverage89.47 + SLOC81 + LOC19 + Missed2 +
    +
    +
    1. 1var patio = exports;
    2. +
    3. +
    4. +
    5. /**
    6. +
    7. * @class Thrown if a function is not impltemened
    8. +
    9. *
    10. +
    11. * @param {String} message the message to show.
    12. +
    13. */
    14. +
    15. 1patio.NotImplemented = function(message) {
    16. +
    17. 4 return new Error("Not Implemented : " + message);
    18. +
    19. };
    20. +
    21. +
    22. /**
    23. +
    24. * @class Thrown if there is an Expression Error.
    25. +
    26. *
    27. +
    28. * @param {String} message the message to show.
    29. +
    30. */
    31. +
    32. 1patio.ExpressionError = function(message) {
    33. +
    34. 0 return new Error("Expression Error :" + message);
    35. +
    36. };
    37. +
    38. +
    39. /**
    40. +
    41. * @class Thrown if there is a Query Error.
    42. +
    43. *
    44. +
    45. * @param {String} message the message to show.
    46. +
    47. */
    48. +
    49. 1patio.QueryError = function(message) {
    50. +
    51. 121 return new Error("QueryError : " + message);
    52. +
    53. };
    54. +
    55. +
    56. /**
    57. +
    58. * @class Thrown if there is a Dataset Error.
    59. +
    60. *
    61. +
    62. * @param {String} message the message to show.
    63. +
    64. */
    65. +
    66. 1patio.DatasetError = function(message) {
    67. +
    68. 7 return new Error("DatasetError : " + message);
    69. +
    70. };
    71. +
    72. +
    73. /**
    74. +
    75. * @class Thrown if there is a Database Error.
    76. +
    77. *
    78. +
    79. * @param {String} message the message to show.
    80. +
    81. */
    82. +
    83. 1patio.DatabaseError = function(message) {
    84. +
    85. 5 return new Error("Database error : " + message);
    86. +
    87. };
    88. +
    89. +
    90. /**
    91. +
    92. * @class Thrown if there is a unexpected Error.
    93. +
    94. *
    95. +
    96. * @param {String} message the message to show.
    97. +
    98. */
    99. +
    100. 1patio.PatioError = function(message) {
    101. +
    102. 31 return new Error("Patio error : " + message);
    103. +
    104. };
    105. +
    106. +
    107. /**
    108. +
    109. * @class Thrown if there is a error thrown within a model.
    110. +
    111. * @param {String} message the message to show.
    112. +
    113. */
    114. +
    115. 1patio.ModelError = function(message) {
    116. +
    117. 2 return new Error("Model error : " + message);
    118. +
    119. };
    120. +
    121. +
    122. /**
    123. +
    124. * @class Thrown if there is an error when loading/creating/deleteing an association.
    125. +
    126. * @param {String} message the message to show.
    127. +
    128. */
    129. +
    130. 1patio.AssociationError = function(message) {
    131. +
    132. 0 return new Error("Association error : " + message);
    133. +
    134. };
    135. +
    136. +
    137. +
    138. /**
    139. +
    140. * Thrown if there is an error when performing a migration.
    141. +
    142. * @param {String} message the message to show.
    143. +
    144. */
    145. +
    146. 1patio.MigrationError = function(message) {
    147. +
    148. 2 return new Error("Migration error : " + message);
    149. +
    150. };
    @@ -9069,21 +9542,21 @@
  • 1var virtualRow = function (name) {
  • -
  • 874 var WILDCARD = new LiteralString('*');
  • -
  • 874 var QUESTION_MARK = new LiteralString('?');
  • -
  • 874 var COMMA_SEPARATOR = new LiteralString(', ');
  • -
  • 874 var DOUBLE_UNDERSCORE = '__';
  • -
  • -
  • 874 var parts = name.split(DOUBLE_UNDERSCORE);
  • -
  • 874 var table = parts[0], column = parts[1];
  • -
  • 874 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • -
  • 874 var prox = methodMissing(ident, function (m) {
  • +
  • 875 var WILDCARD = new LiteralString('*');
  • +
  • 875 var QUESTION_MARK = new LiteralString('?');
  • +
  • 875 var COMMA_SEPARATOR = new LiteralString(', ');
  • +
  • 875 var DOUBLE_UNDERSCORE = '__';
  • +
  • +
  • 875 var parts = name.split(DOUBLE_UNDERSCORE);
  • +
  • 875 var table = parts[0], column = parts[1];
  • +
  • 875 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • +
  • 875 var prox = methodMissing(ident, function (m) {
  • 4 return function () {
  • 3 var args = argsToArray(arguments);
  • 3 return SQLFunction.fromArgs([m, name].concat(args));
  • }
  • }, column ? QualifiedIdentifier : Identifier);
  • -
  • 874 var ret = createFunctionWrapper(prox, function (m) {
  • +
  • 875 var ret = createFunctionWrapper(prox, function (m) {
  • 548 var args = argsToArray(arguments);
  • 548 if (args.length) {
  • 542 return SQLFunction.fromArgs([name].concat(args));
  • @@ -9093,8 +9566,8 @@
  • }, function () {
  • 0 return SQLFunction.fromArgs(arguments);
  • });
  • -
  • 874 ret.__proto__ = ident;
  • -
  • 874 return ret;
  • +
  • 875 ret.__proto__ = ident;
  • +
  • 875 return ret;
  • };
  • 1var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
  • @@ -9546,7 +10019,7 @@
  • });
  • 1exports.sql = methodMissing(sql, function (name) {
  • -
  • 874 return virtualRow(name);
  • +
  • 875 return virtualRow(name);
  • });
  • 1var OPERTATOR_INVERSIONS = {
  • @@ -10198,7 +10671,7 @@
  • * @return {patio.sql.ColumnAll}
  • */
  • all:function () {
  • -
  • 329 return new ColumnAll(this);
  • +
  • 3 return new ColumnAll(this);
  • }
  • @@ -10356,13 +10829,13 @@
  • * @return {patio.sql.Expression} an expression.
  • */
  • fromArgs:function (args) {
  • -
  • 2215 var ret;
  • -
  • 2215 try {
  • -
  • 2215 ret = new this();
  • +
  • 2216 var ret;
  • +
  • 2216 try {
  • +
  • 2216 ret = new this();
  • } catch (ignore) {
  • }
  • -
  • 2215 this.apply(ret, args);
  • -
  • 2215 return ret;
  • +
  • 2216 this.apply(ret, args);
  • +
  • 2216 return ret;
  • },
  • /**
  • @@ -10554,7 +11027,7 @@
  • * @property table the table this all column expression represents.
  • */
  • constructor:function (table) {
  • -
  • 346 this.table = table;
  • +
  • 20 this.table = table;
  • },
  • /**
  • @@ -10566,9 +11039,9 @@
  • * @return String the SQL columnAll expression fragment.
  • */
  • toString:function (ds) {
  • -
  • 345 !Dataset && (Dataset = require("./dataset"));
  • -
  • 345 ds = ds || new Dataset();
  • -
  • 345 return ds.columnAllSql(this);
  • +
  • 19 !Dataset && (Dataset = require("./dataset"));
  • +
  • 19 ds = ds || new Dataset();
  • +
  • 19 return ds.columnAllSql(this);
  • }
  • }
  • }).as(sql, "ColumnAll");
  • @@ -11165,7 +11638,7 @@
  • * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
  • */
  • constructor:function (value) {
  • -
  • 16412 this.__value = value;
  • +
  • 16414 this.__value = value;
  • },
  • /**
  • @@ -11185,7 +11658,7 @@
  • /**@ignore*/
  • getters:{
  • value:function () {
  • -
  • 25480 return this.__value;
  • +
  • 25154 return this.__value;
  • }
  • }
  • }
  • @@ -11742,7 +12215,7 @@
  • 1var addStringMethod = function (op) {
  • 24 return function () {
  • -
  • 3968 return this.__str[op].apply(this.__str, arguments);
  • +
  • 4294 return this.__str[op].apply(this.__str, arguments);
  • }
  • };
  • @@ -11765,7 +12238,7 @@
  • * @param {String} str the literal string.
  • */
  • constructor:function (str) {
  • -
  • 3092 this.__str = str;
  • +
  • 3095 this.__str = str;
  • }
  • }
  • }).as(sql, "LiteralString");
  • @@ -12275,601 +12748,144 @@
  • } else {
  • 0 ret.callback(this.__appliedMigrations);
  • }
  • -
  • 18 return ret.promise();
  • -
  • },
  • -
  • -
  • __getMirationFiles:function () {
  • -
  • 18 var ret = new Promise();
  • -
  • 18 var upMigrations = [], downMigrations = [], target = this.target;
  • -
  • 18 if (!this.__migrationFiles) {
  • -
  • 18 when(
  • -
  • this.getFileNames(),
  • -
  • this.__getAppliedMigrations()
  • -
  • ).then(hitch(this, function (res) {
  • -
  • 18 var files = res[0], appliedMigrations = res[1];
  • -
  • 18 var l = files.length, inserts = [];
  • -
  • 18 if (l > 0) {
  • -
  • 18 try {
  • -
  • 18 for (var i = 0; i < l; i++) {
  • -
  • 92 var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
  • -
  • 92 if (!isUndefined(target)) {
  • -
  • 48 var version = this.getMigrationVersionFromFile(f);
  • -
  • 48 if (version > target || (version === 0 && target === version)) {
  • -
  • 35 if (appliedMigrations.indexOf(fLowerCase) != -1) {
  • -
  • 26 downMigrations.push([f, require(file), "down"]);
  • -
  • }
  • -
  • 13 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
  • -
  • 9 upMigrations.push([f, require(file), "up"]);
  • -
  • }
  • -
  • 44 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
  • -
  • 35 upMigrations.push([f, require(file), "up"]);
  • -
  • }
  • -
  • }
  • -
  • } catch (e) {
  • -
  • 0 return ret.errback(e)
  • -
  • }
  • -
  • 18 this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
  • -
  • 18 ret.callback(this.__migrationFiles);
  • -
  • } else {
  • -
  • 0 return ret.callback();
  • -
  • }
  • -
  • }), ret);
  • -
  • } else {
  • -
  • 0 ret.callback(this.__migrationFiles);
  • -
  • }
  • -
  • 18 return ret.promise();
  • -
  • },
  • -
  • -
  • -
  • // Returns the dataset for the schema_migrations table. If no such table
  • -
  • // exists, it is automatically created.
  • -
  • _getSchemaDataset:function () {
  • -
  • 36 var ret = new Promise();
  • -
  • 36 if (!this.__schemaDataset) {
  • -
  • 18 var ds = this.db.from(this.table);
  • -
  • 18 this.__createTable().then(hitch(this, function () {
  • -
  • 18 this.__schemaDataset = ds;
  • -
  • 18 ret.callback(ds);
  • -
  • }), ret);
  • -
  • } else {
  • -
  • 18 ret.callback(this.__schemaDataset);
  • -
  • }
  • -
  • 36 return ret.promise();
  • -
  • },
  • -
  • -
  • __convertSchemaInfo:function () {
  • -
  • 1 var ret = new Promise(), c = this.column;
  • -
  • 1 var ds = this.db.from(this.table);
  • -
  • 1 this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
  • -
  • 1 this.getFileNames().then(hitch(this, function (files) {
  • -
  • 1 var l = files.length, inserts = [];
  • -
  • 1 if (l > 0) {
  • -
  • 1 for (var i = 0; i < l; i++) {
  • -
  • 7 var f = path.basename(files[i]);
  • -
  • 7 if (this.getMigrationVersionFromFile(f) <= version) {
  • -
  • 5 var insert = {};
  • -
  • 5 insert[c] = f;
  • -
  • 5 inserts.push(ds.insert(insert));
  • -
  • }
  • -
  • }
  • -
  • }
  • -
  • 1 if (inserts.length) {
  • -
  • 1 when.apply(comb, inserts).then(ret);
  • -
  • } else {
  • -
  • 0 ret.callback();
  • -
  • }
  • -
  • }), ret);
  • -
  • }), ret);
  • -
  • 1 return ret.promise();
  • -
  • -
  • },
  • -
  • -
  • __createTable:function () {
  • -
  • 18 var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
  • -
  • 18 var ds = this.db.from(table);
  • -
  • 18 var ret = new Promise();
  • -
  • 18 when(
  • -
  • db.tableExists(table),
  • -
  • db.tableExists(intMigrationTable)
  • -
  • ).then(hitch(this, function (res) {
  • -
  • 18 var exists = res[0], intMigratorExists = res[1];
  • -
  • 18 if (!exists) {
  • -
  • 10 db.createTable(table,
  • -
  • function () {
  • -
  • 10 this.column(c, String, {primaryKey:true});
  • -
  • }).addErrback(ret);
  • -
  • 10 if (intMigratorExists) {
  • -
  • 1 db.from(intMigrationTable).all().then(hitch(this, function (versions) {
  • -
  • 1 var version;
  • -
  • 1 if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
  • -
  • 1 this.__convertSchemaInfo().then(ret);
  • -
  • } else {
  • -
  • 0 ret.callback();
  • -
  • }
  • -
  • }));
  • -
  • } else {
  • -
  • 9 ret.callback();
  • -
  • }
  • -
  • } else {
  • -
  • 8 ds.columns.then(hitch(this, function (columns) {
  • -
  • 8 if (columns.indexOf(c) === -1) {
  • -
  • 0 ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
  • -
  • } else {
  • -
  • 8 ret.callback();
  • -
  • }
  • -
  • }), ret);
  • -
  • }
  • -
  • }), ret);
  • -
  • 18 return ret.promise();
  • -
  • }
  • -
  • },
  • -
  • -
  • static:{
  • -
  • DEFAULT_SCHEMA_COLUMN:"filename",
  • -
  • DEFAULT_SCHEMA_TABLE:"schema_migrations"
  • -
  • }
  • -
  • }).as(exports, "TimestampMigrator");
  • -
  • -
  • 1exports.run = function () {
  • -
  • 31 return Migrator.run.apply(Migrator, arguments);
  • -
  • };
  • -
    - -
    - - - - - -
    -
    dataset/index.js
    -
    -
    - Coverage92.54 - SLOC439 - LOC67 - Missed5 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. hitch = comb.hitch,
    4. -
    5. logging = comb.logging,
    6. -
    7. Logger = logging.Logger,
    8. -
    9. errors = require("../errors"),
    10. -
    11. QueryError = errors.QueryError,
    12. -
    13. DatasetError = errors.DatasetError,
    14. -
    15. Promise = comb.Promise,
    16. -
    17. PromiseList = comb.PromiseList,
    18. -
    19. isUndefined = comb.isUndefined,
    20. -
    21. isUndefinedOrNull = comb.isUndefinedOrNull,
    22. -
    23. isString = comb.isString,
    24. -
    25. isInstanceOf = comb.isInstanceOf,
    26. -
    27. isString = comb.isString,
    28. -
    29. isFunction = comb.isFunction,
    30. -
    31. isNull = comb.isNull,
    32. -
    33. merge = comb.merge,
    34. -
    35. define = comb.define,
    36. -
    37. graph = require("./graph"),
    38. -
    39. actions = require("./actions"),
    40. -
    41. features = require("./features"),
    42. -
    43. query = require("./query"),
    44. -
    45. sql = require("./sql"),
    46. -
    47. SQL = require("../sql").sql,
    48. -
    49. AliasedExpression = SQL.AliasedExpression,
    50. -
    51. Identifier = SQL.Identifier,
    52. -
    53. QualifiedIdentifier = SQL.QualifiedIdentifier;
    54. -
    55. -
    56. -
    57. 1var LOGGER = comb.logger("patio.Dataset");
    58. -
    59. 1define([actions, graph, features, query, sql], {
    60. -
    61. instance:{
    62. -
    63. -
    64. /**@lends patio.Dataset.prototype*/
    65. -
    66. -
    67. /**
    68. -
    69. * Class that is used for querying/retirving datasets from a database.
    70. -
    71. *
    72. -
    73. * <p> Dynamically genertated methods include
    74. -
    75. * <ul>
    76. -
    77. * <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
    78. -
    79. * {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
    80. -
    81. * to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
    82. -
    83. * {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
    84. -
    85. * <ul>
    86. -
    87. * <li>Conditioned join types that accept conditions.
    88. -
    89. * <ul>
    90. -
    91. * <li>inner - INNER JOIN</li>
    92. -
    93. * <li>fullOuter - FULL OUTER</li>
    94. -
    95. * <li>rightOuter - RIGHT OUTER JOIN</li>
    96. -
    97. * <li>leftOuter - LEFT OUTER JOIN</li>
    98. -
    99. * <li>full - FULL JOIN</li>
    100. -
    101. * <li>right - RIGHT JOIN</li>
    102. -
    103. * <li>left - LEFT JOIN</li>
    104. -
    105. * </ul>
    106. -
    107. * </li>
    108. -
    109. * <li>Unconditioned join types that do not accept join conditions
    110. -
    111. * <ul>
    112. -
    113. * <li>natural - NATURAL JOIN</li>
    114. -
    115. * <li>naturalLeft - NATURAL LEFT JOIN</li>
    116. -
    117. * <li>naturalRight - NATURAL RIGHT JOIN</li>
    118. -
    119. * <li>naturalFull - NATURA FULLL JOIN</li>
    120. -
    121. * <li>cross - CROSS JOIN</li>
    122. -
    123. * </ul>
    124. -
    125. * </li>
    126. -
    127. * </ul>
    128. -
    129. * </li>
    130. -
    131. * </li>
    132. -
    133. * </ul>
    134. -
    135. *
    136. -
    137. * <p>
    138. -
    139. * <h4>Features:</h4>
    140. -
    141. * <p>
    142. -
    143. * Features that a particular {@link patio.Dataset} supports are shown in the example below.
    144. -
    145. * If you wish to implement an adapter please override these values depending on the database that
    146. -
    147. * you are developing the adapter for.
    148. -
    149. * </p>
    150. -
    151. * <pre class="code">
    152. -
    153. * var ds = DB.from("test");
    154. -
    155. *
    156. -
    157. * //The default values returned
    158. -
    159. *
    160. -
    161. * //Whether this dataset quotes identifiers.
    162. -
    163. * //Whether this dataset quotes identifiers.
    164. -
    165. * ds.quoteIdentifiers //=>true
    166. -
    167. *
    168. -
    169. * //Whether this dataset will provide accurate number of rows matched for
    170. -
    171. * //delete and update statements. Accurate in this case is the number of
    172. -
    173. * //rows matched by the dataset's filter.
    174. -
    175. * ds.providesAccurateRowsMatched; //=>true
    176. -
    177. *
    178. -
    179. * //Times Whether the dataset requires SQL standard datetimes (false by default,
    180. -
    181. * // as most allow strings with ISO 8601 format).
    182. -
    183. * ds.requiresSqlStandardDate; //=>false
    184. -
    185. *
    186. -
    187. * //Whether the dataset supports common table expressions (the WITH clause).
    188. -
    189. * ds.supportsCte; //=>true
    190. -
    191. *
    192. -
    193. * //Whether the dataset supports the DISTINCT ON clause, false by default.
    194. -
    195. * ds.supportsDistinctOn; //=>false
    196. -
    197. *
    198. -
    199. * //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
    200. -
    201. * ds.supportsIntersectExcept; //=>true
    202. -
    203. *
    204. -
    205. * //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
    206. -
    207. * ds.supportsIntersectExceptAll; //=>true
    208. -
    209. *
    210. -
    211. * //Whether the dataset supports the IS TRUE syntax.
    212. -
    213. * ds.supportsIsTrue; //=>true
    214. -
    215. *
    216. -
    217. * //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
    218. -
    219. * ds.supportsJoinUsing; //=>true
    220. -
    221. *
    222. -
    223. * //Whether modifying joined datasets is supported.
    224. -
    225. * ds.supportsModifyingJoin; //=>false
    226. -
    227. *
    228. -
    229. * //Whether the IN/NOT IN operators support multiple columns when an
    230. -
    231. * ds.supportsMultipleColumnIn; //=>true
    232. -
    233. *
    234. -
    235. * //Whether the dataset supports timezones in literal timestamps
    236. -
    237. * ds.supportsTimestampTimezone; //=>false
    238. -
    239. *
    240. -
    241. * //Whether the dataset supports fractional seconds in literal timestamps
    242. -
    243. * ds.supportsTimestampUsecs; //=>true
    244. -
    245. *
    246. -
    247. * //Whether the dataset supports window functions.
    248. -
    249. * ds.supportsWindowFunctions; //=>false
    250. -
    251. * </pre>
    252. -
    253. * <p>
    254. -
    255. * <p>
    256. -
    257. * <h4>Actions</h4>
    258. -
    259. * <p>
    260. -
    261. * Each dataset does not actually send any query to the database until an action method has
    262. -
    263. * been called upon it(with the exception of {@link patio.Dataset#graph} because columns
    264. -
    265. * from the other table might need retrived in order to set up the graph). Each action
    266. -
    267. * returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
    268. -
    269. * that you account for errors otherwise it can be difficult to track down issues.
    270. -
    271. * The list of action methods is:
    272. -
    273. * <ul>
    274. -
    275. * <li>{@link patio.Dataset#all}</li>
    276. -
    277. * <li>{@link patio.Dataset#one}</li>
    278. -
    279. * <li>{@link patio.Dataset#avg}</li>
    280. -
    281. * <li>{@link patio.Dataset#count}</li>
    282. -
    283. * <li>{@link patio.Dataset#columns}</li>
    284. -
    285. * <li>{@link patio.Dataset#remove}</li>
    286. -
    287. * <li>{@link patio.Dataset#forEach}</li>
    288. -
    289. * <li>{@link patio.Dataset#empty}</li>
    290. -
    291. * <li>{@link patio.Dataset#first}</li>
    292. -
    293. * <li>{@link patio.Dataset#get}</li>
    294. -
    295. * <li>{@link patio.Dataset#import}</li>
    296. -
    297. * <li>{@link patio.Dataset#insert}</li>
    298. -
    299. * <li>{@link patio.Dataset#save}</li>
    300. -
    301. * <li>{@link patio.Dataset#insertMultiple}</li>
    302. -
    303. * <li>{@link patio.Dataset#saveMultiple}</li>
    304. -
    305. * <li>{@link patio.Dataset#interval}</li>
    306. -
    307. * <li>{@link patio.Dataset#last}</li>
    308. -
    309. * <li>{@link patio.Dataset#map}</li>
    310. -
    311. * <li>{@link patio.Dataset#max}</li>
    312. -
    313. * <li>{@link patio.Dataset#min}</li>
    314. -
    315. * <li>{@link patio.Dataset#multiInsert}</li>
    316. -
    317. * <li>{@link patio.Dataset#range}</li>
    318. -
    319. * <li>{@link patio.Dataset#selectHash}</li>
    320. -
    321. * <li>{@link patio.Dataset#selectMap}</li>
    322. -
    323. * <li>{@link patio.Dataset#selectOrderMap}</li>
    324. -
    325. * <li>{@link patio.Dataset#set}</li>
    326. -
    327. * <li>{@link patio.Dataset#singleRecord}</li>
    328. -
    329. * <li>{@link patio.Dataset#singleValue}</li>
    330. -
    331. * <li>{@link patio.Dataset#sum}</li>
    332. -
    333. * <li>{@link patio.Dataset#toCsv}</li>
    334. -
    335. * <li>{@link patio.Dataset#toHash}</li>
    336. -
    337. * <li>{@link patio.Dataset#truncate}</li>
    338. -
    339. * <li>{@link patio.Dataset#update}</li>
    340. -
    341. * </ul>
    342. -
    343. *
    344. -
    345. * </p>
    346. -
    347. * </p>
    348. -
    349. *
    350. -
    351. * @constructs
    352. -
    353. *
    354. -
    355. *
    356. -
    357. * @param {patio.Database} db the database this dataset should use when querying for data.
    358. -
    359. * @param {Object} opts options to set on this dataset instance
    360. -
    361. *
    362. -
    363. * @property {Function} rowCb callback to be invoked for each row returned from the database.
    364. -
    365. * the return value will be used as the result of query. The rowCb can also return a promise,
    366. -
    367. * The resolved value of the promise will be used as result.
    368. -
    369. *
    370. -
    371. * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
    372. -
    373. * This value will be defaulted to whatever the identifierInputMethod
    374. -
    375. * is on the database used in initialization.
    376. -
    377. *
    378. -
    379. * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
    380. -
    381. * This value will be defaulted to whatever the identifierOutputMethod
    382. -
    383. * is on the database used in initialization.
    384. -
    385. * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
    386. -
    387. * throws a {patio.DatasetError} tf the dataset doesn't have a table.
    388. -
    389. * <pre class="code">
    390. -
    391. * DB.from("table").firstSourceAlias;
    392. -
    393. * //=> "table"
    394. -
    395. *
    396. -
    397. * DB.from("table___t").firstSourceAlias;
    398. -
    399. * //=> "t"
    400. -
    401. * </pre>
    402. -
    403. *
    404. -
    405. * @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
    406. -
    407. * have a table, raises a {@link patio.erros.DatasetError}.
    408. -
    409. *<pre class="code">
    410. -
    411. *
    412. -
    413. * DB.from("table").firstSourceTable;
    414. -
    415. * //=> "table"
    416. -
    417. *
    418. -
    419. * DB.from("table___t").firstSourceTable;
    420. -
    421. * //=> "t"
    422. -
    423. * </pre>
    424. -
    425. * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
    426. -
    427. * <pre class="code">
    428. -
    429. * DB.from("items").isSimpleSelectAll; //=> true
    430. -
    431. * DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
    432. -
    433. * </pre>
    434. -
    435. * @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
    436. -
    437. * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
    438. -
    439. * delete and update statements. Accurate in this case is the number of
    440. -
    441. * rows matched by the dataset's filter.
    442. -
    443. * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
    444. -
    445. * as most allow strings with ISO 8601 format).
    446. -
    447. * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
    448. -
    449. * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
    450. -
    451. * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
    452. -
    453. * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
    454. -
    455. * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
    456. -
    457. * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
    458. -
    459. * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
    460. -
    461. * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
    462. -
    463. * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
    464. -
    465. * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
    466. -
    467. * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
    468. -
    469. */
    470. -
    471. constructor:function (db, opts) {
    472. -
    473. 29542 this._super(arguments);
    474. -
    475. 29542 this.db = db;
    476. -
    477. 29542 this.__opts = {};
    478. -
    479. 29542 this.__rowCb = null;
    480. -
    481. 29542 if (db) {
    482. -
    483. 15365 this.__quoteIdentifiers = db.quoteIdentifiers;
    484. -
    485. 15365 this.__identifierInputMethod = db.identifierInputMethod;
    486. -
    487. 15365 this.__identifierOutputMethod = db.identifierOutputMethod;
    488. -
    489. }
    490. +
    491. 18 return ret.promise();
    492. },
    493. -
    494. -
    495. /**
    496. -
    497. * Returns a new clone of the dataset with with the given options merged into the current datasets options.
    498. -
    499. * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
    500. -
    501. * columns are deleted. This method should generally not be called
    502. -
    503. * directly by user code.
    504. -
    505. *
    506. -
    507. * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
    508. -
    509. * @return [patio.Dataset] a cloned dataset with the merged options
    510. -
    511. **/
    512. -
    513. mergeOptions:function (opts) {
    514. -
    515. 15274 opts = isUndefined(opts) ? {} : opts;
    516. -
    517. 15274 var ds = new this._static(this.db, {});
    518. -
    519. 15274 ds.rowCb = this.rowCb;
    520. -
    521. 15274 this._static.FEATURES.forEach(function (f) {
    522. -
    523. 213836 ds[f] = this[f];
    524. -
    525. }, this);
    526. -
    527. 15274 ds.__opts = merge({}, this.__opts, opts);
    528. -
    529. 15274 ds.identifierInputMethod = this.identifierInputMethod;
    530. -
    531. 15274 ds.identifierOutputMethod = this.identifierOutputMethod;
    532. -
    533. 15274 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
    534. -
    535. 15274 if (Object.keys(opts).some(function (o) {
    536. -
    537. 13862 return columnChangeOpts.indexOf(o) != -1;
    538. -
    539. })) {
    540. -
    541. 2782 ds.__opts.columns = null;
    542. +
    543. __getMirationFiles:function () {
    544. +
    545. 18 var ret = new Promise();
    546. +
    547. 18 var upMigrations = [], downMigrations = [], target = this.target;
    548. +
    549. 18 if (!this.__migrationFiles) {
    550. +
    551. 18 when(
    552. +
    553. this.getFileNames(),
    554. +
    555. this.__getAppliedMigrations()
    556. +
    557. ).then(hitch(this, function (res) {
    558. +
    559. 18 var files = res[0], appliedMigrations = res[1];
    560. +
    561. 18 var l = files.length, inserts = [];
    562. +
    563. 18 if (l > 0) {
    564. +
    565. 18 try {
    566. +
    567. 18 for (var i = 0; i < l; i++) {
    568. +
    569. 92 var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
    570. +
    571. 92 if (!isUndefined(target)) {
    572. +
    573. 48 var version = this.getMigrationVersionFromFile(f);
    574. +
    575. 48 if (version > target || (version === 0 && target === version)) {
    576. +
    577. 35 if (appliedMigrations.indexOf(fLowerCase) != -1) {
    578. +
    579. 26 downMigrations.push([f, require(file), "down"]);
    580. +
    581. }
    582. +
    583. 13 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
    584. +
    585. 9 upMigrations.push([f, require(file), "up"]);
    586. +
    587. }
    588. +
    589. 44 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
    590. +
    591. 35 upMigrations.push([f, require(file), "up"]);
    592. +
    593. }
    594. +
    595. }
    596. +
    597. } catch (e) {
    598. +
    599. 0 return ret.errback(e)
    600. +
    601. }
    602. +
    603. 18 this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
    604. +
    605. 18 ret.callback(this.__migrationFiles);
    606. +
    607. } else {
    608. +
    609. 0 return ret.callback();
    610. +
    611. }
    612. +
    613. }), ret);
    614. +
    615. } else {
    616. +
    617. 0 ret.callback(this.__migrationFiles);
    618. }
    619. -
    620. 15274 return ds;
    621. +
    622. 18 return ret.promise();
    623. },
    624. -
    625. /**
    626. -
    627. * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
    628. -
    629. * or {@link patio.sql.AliasedExpression}, depending on the format:
    630. -
    631. *
    632. -
    633. * <ul>
    634. -
    635. * <li>For columns : table__column___alias.</li>
    636. -
    637. * <li>For tables : schema__table___alias.</li>
    638. -
    639. * </ul>
    640. -
    641. * each portion of the identifier is optional. See example below
    642. -
    643. *
    644. -
    645. * @example
    646. -
    647. *
    648. -
    649. * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
    650. -
    651. * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
    652. -
    653. * ds.stringToIdentifier("table__column___alias");
    654. -
    655. * //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
    656. -
    657. *
    658. -
    659. * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
    660. -
    661. * or {@link patio.sql.AliasedExpression}.
    662. -
    663. *
    664. -
    665. * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
    666. -
    667. */
    668. -
    669. stringToIdentifier:function (name) {
    670. -
    671. 15093 if (isString(name)) {
    672. -
    673. 10138 var parts = this._splitString(name);
    674. -
    675. 10138 var schema = parts[0], table = parts[1], alias = parts[2];
    676. -
    677. 10138 return (schema && table && alias
    678. -
    679. ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
    680. -
    681. : (schema && table
    682. -
    683. ? new QualifiedIdentifier(schema, table)
    684. -
    685. : (table && alias
    686. -
    687. ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
    688. +
    689. // Returns the dataset for the schema_migrations table. If no such table
    690. +
    691. // exists, it is automatically created.
    692. +
    693. _getSchemaDataset:function () {
    694. +
    695. 36 var ret = new Promise();
    696. +
    697. 36 if (!this.__schemaDataset) {
    698. +
    699. 18 var ds = this.db.from(this.table);
    700. +
    701. 18 this.__createTable().then(hitch(this, function () {
    702. +
    703. 18 this.__schemaDataset = ds;
    704. +
    705. 18 ret.callback(ds);
    706. +
    707. }), ret);
    708. } else {
    709. -
    710. 4955 return name;
    711. -
    712. }
    713. -
    714. },
    715. -
    716. -
    717. /**
    718. -
    719. * Can either be a string or null.
    720. -
    721. *
    722. -
    723. *
    724. -
    725. * @example
    726. -
    727. * //columns
    728. -
    729. * table__column___alias //=> table.column as alias
    730. -
    731. * table__column //=> table.column
    732. -
    733. * //tables
    734. -
    735. * schema__table___alias //=> schema.table as alias
    736. -
    737. * schema__table //=> schema.table
    738. -
    739. *
    740. -
    741. * //name and alias
    742. -
    743. * columnOrTable___alias //=> columnOrTable as alias
    744. -
    745. *
    746. -
    747. *
    748. -
    749. *
    750. -
    751. * @return {String[]} an array with the elements being:
    752. -
    753. * <ul>
    754. -
    755. * <li>For columns :[table, column, alias].</li>
    756. -
    757. * <li>For tables : [schema, table, alias].</li>
    758. -
    759. * </ul>
    760. -
    761. */
    762. -
    763. _splitString:function (s) {
    764. -
    765. 19662 var ret, m;
    766. -
    767. 19662 if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
    768. -
    769. 189 ret = m.slice(1);
    770. -
    771. }
    772. -
    773. 19473 else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
    774. -
    775. 28 ret = [null, m[1], m[2]];
    776. -
    777. }
    778. -
    779. 19445 else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
    780. -
    781. 2079 ret = [m[1], m[2], null];
    782. -
    783. }
    784. -
    785. else {
    786. -
    787. 17366 ret = [null, s, null];
    788. +
    789. 18 ret.callback(this.__schemaDataset);
    790. }
    791. -
    792. 19662 return ret;
    793. +
    794. 36 return ret.promise();
    795. },
    796. -
    797. /**
    798. -
    799. * @ignore
    800. -
    801. **/
    802. -
    803. getters:{
    804. -
    805. -
    806. rowCb:function () {
    807. -
    808. 23378 return this.__rowCb;
    809. -
    810. },
    811. -
    812. -
    813. identifierInputMethod:function () {
    814. -
    815. 15274 return this.__identifierInputMethod;
    816. -
    817. },
    818. -
    819. -
    820. identifierOutputMethod:function () {
    821. -
    822. 15274 return this.__identifierOutputMethod;
    823. -
    824. },
    825. -
    826. -
    827. firstSourceAlias:function () {
    828. -
    829. 905 var source = this.__opts.from;
    830. -
    831. 905 if (isUndefinedOrNull(source) || !source.length) {
    832. -
    833. 2 throw new DatasetError("No source specified for the query");
    834. -
    835. }
    836. -
    837. 903 source = source[0];
    838. -
    839. 903 if (isInstanceOf(source, AliasedExpression)) {
    840. -
    841. 20 return source.alias;
    842. -
    843. 883 } else if (isString(source)) {
    844. -
    845. 0 var parts = this._splitString(source);
    846. -
    847. 0 var alias = parts[2];
    848. -
    849. 0 return alias ? alias : source;
    850. -
    851. } else {
    852. -
    853. 883 return source;
    854. -
    855. }
    856. -
    857. },
    858. +
    859. __convertSchemaInfo:function () {
    860. +
    861. 1 var ret = new Promise(), c = this.column;
    862. +
    863. 1 var ds = this.db.from(this.table);
    864. +
    865. 1 this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
    866. +
    867. 1 this.getFileNames().then(hitch(this, function (files) {
    868. +
    869. 1 var l = files.length, inserts = [];
    870. +
    871. 1 if (l > 0) {
    872. +
    873. 1 for (var i = 0; i < l; i++) {
    874. +
    875. 7 var f = path.basename(files[i]);
    876. +
    877. 7 if (this.getMigrationVersionFromFile(f) <= version) {
    878. +
    879. 5 var insert = {};
    880. +
    881. 5 insert[c] = f;
    882. +
    883. 5 inserts.push(ds.insert(insert));
    884. +
    885. }
    886. +
    887. }
    888. +
    889. }
    890. +
    891. 1 if (inserts.length) {
    892. +
    893. 1 when.apply(comb, inserts).then(ret);
    894. +
    895. } else {
    896. +
    897. 0 ret.callback();
    898. +
    899. }
    900. +
    901. }), ret);
    902. +
    903. }), ret);
    904. +
    905. 1 return ret.promise();
    906. -
    907. firstSourceTable:function () {
    908. -
    909. 15 var source = this.__opts.from;
    910. -
    911. 15 if (isUndefinedOrNull(source) || !source.length) {
    912. -
    913. 1 throw new QueryError("No source specified for the query");
    914. -
    915. }
    916. -
    917. 14 var source = source[0];
    918. -
    919. 14 if (isInstanceOf(source, AliasedExpression)) {
    920. -
    921. 3 return source.expression;
    922. -
    923. 11 } else if (isString(source)) {
    924. -
    925. 0 var parts = this._splitString(source);
    926. -
    927. 0 return source;
    928. -
    929. } else {
    930. -
    931. 11 return source;
    932. -
    933. }
    934. -
    935. }
    936. },
    937. -
    938. /**
    939. -
    940. * @ignore
    941. -
    942. **/
    943. -
    944. setters:{
    945. -
    946. /**@lends patio.Dataset.prototype*/
    947. -
    948. -
    949. identifierInputMethod:function (meth) {
    950. -
    951. 15364 this.__identifierInputMethod = meth;
    952. -
    953. },
    954. -
    955. -
    956. identifierOutputMethod:function (meth) {
    957. -
    958. 15364 this.__identifierOutputMethod = meth;
    959. -
    960. },
    961. -
    962. -
    963. rowCb:function (cb) {
    964. -
    965. 18890 if (isFunction(cb) || isNull(cb)) {
    966. -
    967. 18885 this.__rowCb = cb;
    968. +
    969. __createTable:function () {
    970. +
    971. 18 var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
    972. +
    973. 18 var ds = this.db.from(table);
    974. +
    975. 18 var ret = new Promise();
    976. +
    977. 18 when(
    978. +
    979. db.tableExists(table),
    980. +
    981. db.tableExists(intMigrationTable)
    982. +
    983. ).then(hitch(this, function (res) {
    984. +
    985. 18 var exists = res[0], intMigratorExists = res[1];
    986. +
    987. 18 if (!exists) {
    988. +
    989. 10 db.createTable(table,
    990. +
    991. function () {
    992. +
    993. 10 this.column(c, String, {primaryKey:true});
    994. +
    995. }).addErrback(ret);
    996. +
    997. 10 if (intMigratorExists) {
    998. +
    999. 1 db.from(intMigrationTable).all().then(hitch(this, function (versions) {
    1000. +
    1001. 1 var version;
    1002. +
    1003. 1 if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
    1004. +
    1005. 1 this.__convertSchemaInfo().then(ret);
    1006. +
    1007. } else {
    1008. +
    1009. 0 ret.callback();
    1010. +
    1011. }
    1012. +
    1013. }));
    1014. +
    1015. } else {
    1016. +
    1017. 9 ret.callback();
    1018. +
    1019. }
    1020. } else {
    1021. -
    1022. 5 throw new DatasetError("rowCb mus be a function");
    1023. +
    1024. 8 ds.columns.then(hitch(this, function (columns) {
    1025. +
    1026. 8 if (columns.indexOf(c) === -1) {
    1027. +
    1028. 0 ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
    1029. +
    1030. } else {
    1031. +
    1032. 8 ret.callback();
    1033. +
    1034. }
    1035. +
    1036. }), ret);
    1037. }
    1038. -
    1039. }
    1040. +
    1041. }), ret);
    1042. +
    1043. 18 return ret.promise();
    1044. }
    1045. },
    1046. static:{
    1047. -
    1048. COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
    1049. -
    1050. COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
    1051. -
    1052. COLUMN_REF_RE3:/^(\w+)__(\w+)$/
    1053. +
    1054. DEFAULT_SCHEMA_COLUMN:"filename",
    1055. +
    1056. DEFAULT_SCHEMA_TABLE:"schema_migrations"
    1057. }
    1058. -
    1059. }).as(module);
    1060. +
    1061. }).as(exports, "TimestampMigrator");
    1062. -
    +
  • 1exports.run = function () {
  • +
  • 31 return Migrator.run.apply(Migrator, arguments);
  • +
  • };
  • @@ -13876,7 +13892,7 @@
    Coverage92.86 - SLOC1683 + SLOC1684 LOC504 Missed36
    @@ -13884,44 +13900,45 @@
    1. 1var comb = require("comb"),
    2. -
    3. define = comb.define,
    4. -
    5. array = comb.array,
    6. -
    7. intersect = array.intersect,
    8. -
    9. compact = array.compact,
    10. -
    11. string = comb.string,
    12. -
    13. format = string.format,
    14. -
    15. argsToArray = comb.argsToArray,
    16. -
    17. isInstanceOf = comb.isInstanceOf,
    18. -
    19. isArray = comb.isArray,
    20. -
    21. isNumber = comb.isNumber,
    22. -
    23. isDate = comb.isDate,
    24. -
    25. isNull = comb.isNull,
    26. -
    27. isBoolean = comb.isBoolean,
    28. -
    29. isFunction = comb.isFunction,
    30. -
    31. isUndefined = comb.isUndefined,
    32. -
    33. isObject = comb.isObject,
    34. -
    35. isHash = comb.isHash,
    36. -
    37. merge = comb.merge,
    38. -
    39. isUndefinedOrNull = comb.isUndefinedOrNull,
    40. -
    41. isString = comb.isString,
    42. -
    43. sql = require("../sql").sql,
    44. -
    45. Expression = sql.Expression,
    46. -
    47. ComplexExpression = sql.ComplexExpression,
    48. -
    49. AliasedExpression = sql.AliasedExpression,
    50. -
    51. Identifier = sql.Identifier,
    52. -
    53. QualifiedIdentifier = sql.QualifiedIdentifier,
    54. -
    55. OrderedExpression = sql.OrderedExpression,
    56. -
    57. CaseExpression = sql.CaseExpression,
    58. -
    59. SubScript = sql.SubScript,
    60. -
    61. NumericExpression = sql.NumericExpression,
    62. -
    63. ColumnAll = sql.ColumnAll,
    64. -
    65. Cast = sql.Cast,
    66. -
    67. StringExpression = sql.StringExpression,
    68. -
    69. BooleanExpression = sql.BooleanExpression,
    70. -
    71. SQLFunction = sql.SQLFunction,
    72. -
    73. LiteralString = sql.LiteralString,
    74. -
    75. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    76. -
    77. QueryError = require("../errors").QueryError, patio;
    78. +
    79. define = comb.define,
    80. +
    81. array = comb.array,
    82. +
    83. intersect = array.intersect,
    84. +
    85. compact = array.compact,
    86. +
    87. string = comb.string,
    88. +
    89. format = string.format,
    90. +
    91. argsToArray = comb.argsToArray,
    92. +
    93. isInstanceOf = comb.isInstanceOf,
    94. +
    95. isArray = comb.isArray,
    96. +
    97. isNumber = comb.isNumber,
    98. +
    99. isDate = comb.isDate,
    100. +
    101. isNull = comb.isNull,
    102. +
    103. isBoolean = comb.isBoolean,
    104. +
    105. isFunction = comb.isFunction,
    106. +
    107. isUndefined = comb.isUndefined,
    108. +
    109. isObject = comb.isObject,
    110. +
    111. isHash = comb.isHash,
    112. +
    113. isEmpty = comb.isEmpty,
    114. +
    115. merge = comb.merge,
    116. +
    117. isUndefinedOrNull = comb.isUndefinedOrNull,
    118. +
    119. isString = comb.isString,
    120. +
    121. sql = require("../sql").sql,
    122. +
    123. Expression = sql.Expression,
    124. +
    125. ComplexExpression = sql.ComplexExpression,
    126. +
    127. AliasedExpression = sql.AliasedExpression,
    128. +
    129. Identifier = sql.Identifier,
    130. +
    131. QualifiedIdentifier = sql.QualifiedIdentifier,
    132. +
    133. OrderedExpression = sql.OrderedExpression,
    134. +
    135. CaseExpression = sql.CaseExpression,
    136. +
    137. SubScript = sql.SubScript,
    138. +
    139. NumericExpression = sql.NumericExpression,
    140. +
    141. ColumnAll = sql.ColumnAll,
    142. +
    143. Cast = sql.Cast,
    144. +
    145. StringExpression = sql.StringExpression,
    146. +
    147. BooleanExpression = sql.BooleanExpression,
    148. +
    149. SQLFunction = sql.SQLFunction,
    150. +
    151. LiteralString = sql.LiteralString,
    152. +
    153. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    154. +
    155. QueryError = require("../errors").QueryError, patio;
    156. 1var Dataset;
    157. @@ -13985,9 +14002,9 @@
    158. constructor:function () {
    159. //We initialize these here because otherwise
    160. //the will be blank because of recursive dependencies.
    161. -
    162. 29542 !patio && (patio = require("../index"));
    163. -
    164. 29542 !Dataset && (Dataset = patio.Dataset);
    165. -
    166. 29542 this._super(arguments);
    167. +
    168. 29216 !patio && (patio = require("../index"));
    169. +
    170. 29216 !Dataset && (Dataset = patio.Dataset);
    171. +
    172. 29216 this._super(arguments);
    173. },
    174. /**
    175. @@ -14272,15 +14289,15 @@
    176. * @return {String} a literal representation of the value.
    177. */
    178. literal:function (v) {
    179. -
    180. 44748 if (isInstanceOf(v, LiteralString)) {
    181. +
    182. 44422 if (isInstanceOf(v, LiteralString)) {
    183. 204 return "" + v;
    184. -
    185. 44544 } else if (isString(v)) {
    186. +
    187. 44218 } else if (isString(v)) {
    188. 6110 return this._literalString(v);
    189. -
    190. 38434 } else if (isNumber(v)) {
    191. +
    192. 38108 } else if (isNumber(v)) {
    193. 6997 return this._literalNumber(v);
    194. }
    195. -
    196. 31437 else if (isInstanceOf(v, Expression)) {
    197. -
    198. 28903 return this._literalExpression(v);
    199. +
    200. 31111 else if (isInstanceOf(v, Expression)) {
    201. +
    202. 28577 return this._literalExpression(v);
    203. }
    204. 2534 else if (isInstanceOf(v, Dataset)) {
    205. 104 return this._literalDataset(v);
    206. @@ -14332,7 +14349,7 @@
    207. 59 return new QualifiedIdentifier(table, e);
    208. 217 } else if (isInstanceOf(e, OrderedExpression)) {
    209. 2 return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending,
    210. -
    211. {nulls:e.nulls});
    212. +
    213. {nulls:e.nulls});
    214. 215 } else if (isInstanceOf(e, AliasedExpression)) {
    215. 72 return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias);
    216. 143 } else if (isInstanceOf(e, CaseExpression)) {
    217. @@ -14500,9 +14517,9 @@
    218. 1440 var columns = this.__opts.columns, ret = "";
    219. 1440 if (columns && columns.length) {
    220. 1387 ret = " (" + columns.map(
    221. -
    222. function (c) {
    223. -
    224. 6691 return c.toString(this);
    225. -
    226. }, this).join(this._static.COMMA_SEPARATOR) + ")";
    227. +
    228. function (c) {
    229. +
    230. 6691 return c.toString(this);
    231. +
    232. }, this).join(this._static.COMMA_SEPARATOR) + ")";
    233. }
    234. 1440 return ret;
    235. },
    236. @@ -14592,7 +14609,7 @@
    237. * SQL fragment for specifying all columns in a given table
    238. **/
    239. columnAllSql:function (ca) {
    240. -
    241. 345 return string.format("%s.*", this.quoteSchemaTable(ca.table));
    242. +
    243. 19 return string.format("%s.*", this.quoteSchemaTable(ca.table));
    244. },
    245. /**
    246. @@ -14614,8 +14631,8 @@
    247. 0 return this.complexExpressionSql("EQ", args);
    248. } else {
    249. 0 return this.complexExpressionSql("OR",
    250. -
    251. [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
    252. -
    253. null)]);
    254. +
    255. [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
    256. +
    257. null)]);
    258. }
    259. 6454 } else if (["IN", "NOTIN"].indexOf(op) != -1) {
    260. @@ -14646,8 +14663,8 @@
    261. //literal so that if values is an array of two element arrays, it
    262. //will be treated as a value list instead of a condition specifier.
    263. 4 return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
    264. -
    265. ComplexExpression.IN_OPERATORS[op],
    266. -
    267. valArray ? this._arraySql(vals) : this.literal(vals));
    268. +
    269. ComplexExpression.IN_OPERATORS[op],
    270. +
    271. valArray ? this._arraySql(vals) : this.literal(vals));
    272. }
    273. }
    274. else {
    275. @@ -14661,13 +14678,13 @@
    276. }
    277. } else {
    278. 10 return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
    279. -
    280. ComplexExpression.IN_OPERATORS[op], this.literal(vals));
    281. +
    282. ComplexExpression.IN_OPERATORS[op], this.literal(vals));
    283. }
    284. }
    285. 6437 } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
    286. 5427 var l = args[0];
    287. 5427 return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
    288. -
    289. this.literal(args[1]));
    290. +
    291. this.literal(args[1]));
    292. 1010 } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
    293. 976 return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
    294. 34 } else if (op == "NOT") {
    295. @@ -14710,7 +14727,7 @@
    296. }
    297. 925 var tref = this.__tableRef(table);
    298. 925 return string.format(" %s %s", this._joinTypeSql(jc.joinType),
    299. -
    300. tableAlias ? this.__asSql(tref, tableAlias) : tref);
    301. +
    302. tableAlias ? this.__asSql(tref, tableAlias) : tref);
    303. },
    304. /**
    305. @@ -14783,11 +14800,11 @@
    306. */
    307. qualifiedIdentifierSql:function (qcr) {
    308. 4532 return [qcr.table, qcr.column].map(
    309. -
    310. function (x) {
    311. -
    312. 9064 return [QualifiedIdentifier, Identifier, String].some(function (c) {
    313. -
    314. 25671 return x instanceof c
    315. -
    316. }) ? this.literal(x) : this.quoteIdentifier(x)
    317. -
    318. }, this).join('.');
    319. +
    320. function (x) {
    321. +
    322. 9064 return [QualifiedIdentifier, Identifier, String].some(function (c) {
    323. +
    324. 25671 return x instanceof c
    325. +
    326. }) ? this.literal(x) : this.quoteIdentifier(x)
    327. +
    328. }, this).join('.');
    329. },
    330. /**
    331. @@ -14798,18 +14815,18 @@
    332. * quote the name with {@link patio.dataset._Sql#_quotedIdentifier}.
    333. */
    334. quoteIdentifier:function (name) {
    335. -
    336. 34093 if (isInstanceOf(name, LiteralString)) {
    337. +
    338. 33767 if (isInstanceOf(name, LiteralString)) {
    339. 260 return name;
    340. } else {
    341. -
    342. 33833 if (isInstanceOf(name, Identifier)) {
    343. +
    344. 33507 if (isInstanceOf(name, Identifier)) {
    345. 22237 name = name.value;
    346. }
    347. -
    348. 33833 name = this.inputIdentifier(name);
    349. -
    350. 33833 if (this.quoteIdentifiers) {
    351. -
    352. 26623 name = this._quotedIdentifier(name)
    353. +
    354. 33507 name = this.inputIdentifier(name);
    355. +
    356. 33507 if (this.quoteIdentifiers) {
    357. +
    358. 26311 name = this._quotedIdentifier(name)
    359. }
    360. }
    361. -
    362. 33833 return name;
    363. +
    364. 33507 return name;
    365. },
    366. /**
    367. @@ -14819,15 +14836,15 @@
    368. * identifierOutputMethod.
    369. */
    370. inputIdentifier:function (v) {
    371. -
    372. 34019 var i = this.__identifierInputMethod;
    373. -
    374. 34019 v = v.toString(this);
    375. -
    376. 34019 return !isUndefinedOrNull(i) ?
    377. -
    378. isFunction(v[i]) ?
    379. -
    380. v[i]() :
    381. -
    382. isFunction(comb[i]) ?
    383. -
    384. comb[i](v)
    385. -
    386. : v
    387. -
    388. : v;
    389. +
    390. 33693 var i = this.__identifierInputMethod;
    391. +
    392. 33693 v = v.toString(this);
    393. +
    394. 33693 return !isUndefinedOrNull(i) ?
    395. +
    396. isFunction(v[i]) ?
    397. +
    398. v[i]() :
    399. +
    400. isFunction(comb[i]) ?
    401. +
    402. comb[i](v)
    403. +
    404. : v
    405. +
    406. : v;
    407. },
    408. /**
    409. @@ -14837,15 +14854,15 @@
    410. * identifierOutputMethod.
    411. */
    412. outputIdentifier:function (v) {
    413. -
    414. 19523 (v == '' && (v = 'untitled'));
    415. -
    416. 19523 var i = this.__identifierOutputMethod;
    417. -
    418. 19523 return !isUndefinedOrNull(i) ?
    419. -
    420. isFunction(v[i]) ?
    421. -
    422. v[i]() :
    423. -
    424. isFunction(comb[i]) ?
    425. -
    426. comb[i](v)
    427. -
    428. : v
    429. -
    430. : v;
    431. +
    432. 19885 (v == '' && (v = 'untitled'));
    433. +
    434. 19885 var i = this.__identifierOutputMethod;
    435. +
    436. 19885 return !isUndefinedOrNull(i) ?
    437. +
    438. isFunction(v[i]) ?
    439. +
    440. v[i]() :
    441. +
    442. isFunction(comb[i]) ?
    443. +
    444. comb[i](v)
    445. +
    446. : v
    447. +
    448. : v;
    449. },
    450. /**
    451. @@ -14855,10 +14872,10 @@
    452. * quoted (if quoting identifiers)
    453. */
    454. quoteSchemaTable:function (table) {
    455. -
    456. 2557 var parts = this.schemaAndTable(table);
    457. -
    458. 2557 var schema = parts[0];
    459. -
    460. 2557 table = parts[1];
    461. -
    462. 2557 return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));
    463. +
    464. 2231 var parts = this.schemaAndTable(table);
    465. +
    466. 2231 var schema = parts[0];
    467. +
    468. 2231 table = parts[1];
    469. +
    470. 2231 return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));
    471. },
    472. @@ -14867,15 +14884,15 @@
    473. * Split the schema information from the table
    474. */
    475. schemaAndTable:function (tableName) {
    476. -
    477. 2801 var sch = this.db ? this.db.defaultSchema || null : null;
    478. -
    479. 2801 if (isString(tableName)) {
    480. +
    481. 2475 var sch = this.db ? this.db.defaultSchema || null : null;
    482. +
    483. 2475 if (isString(tableName)) {
    484. 1008 var parts = this._splitString(tableName);
    485. 1008 var s = parts[0], table = parts[1];
    486. 1008 return [s || sch, table];
    487. -
    488. 1793 } else if (isInstanceOf(tableName, QualifiedIdentifier)) {
    489. +
    490. 1467 } else if (isInstanceOf(tableName, QualifiedIdentifier)) {
    491. 3 return [tableName.table, tableName.column]
    492. -
    493. 1790 } else if (isInstanceOf(tableName, Identifier)) {
    494. -
    495. 1790 return [null, tableName.value];
    496. +
    497. 1464 } else if (isInstanceOf(tableName, Identifier)) {
    498. +
    499. 1464 return [null, tableName.value];
    500. } else {
    501. 0 throw new QueryError("table should be a QualifiedIdentifier, Identifier, or String");
    502. }
    503. @@ -14924,7 +14941,7 @@
    504. * expressions.
    505. */
    506. __expressionList:function (columns) {
    507. -
    508. 4610 return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);
    509. +
    510. 4284 return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);
    511. },
    512. //Format the timestamp based on the default_timestamp_format, with a couple
    513. @@ -14941,9 +14958,9 @@
    514. */
    515. _joinTypeSql:function (joinType) {
    516. 922 return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g,
    517. -
    518. function (m) {
    519. -
    520. 1127 return m.toUpperCase() + " ";
    521. -
    522. }
    523. +
    524. function (m) {
    525. +
    526. 1127 return m.toUpperCase() + " ";
    527. +
    528. }
    529. ).trimRight() + " JOIN";
    530. },
    531. @@ -15046,7 +15063,7 @@
    532. * @return SQL fragment for SQL::Expression, result depends on the specific type of expression.
    533. * */
    534. _literalExpression:function (v) {
    535. -
    536. 28913 return v.toString(this);
    537. +
    538. 28587 return v.toString(this);
    539. },
    540. /**
    541. @@ -15069,10 +15086,10 @@
    542. 6059 var table = parts[0], column = parts[1], alias = parts[2];
    543. 6059 if (!alias) {
    544. 6059 return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'"
    545. -
    546. + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
    547. +
    548. + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
    549. } else {
    550. 0 return this.literal(new AliasedExpression(column
    551. -
    552. && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
    553. +
    554. && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
    555. }
    556. },
    557. @@ -15365,9 +15382,9 @@
    558. 0 throw new QueryError("No source specified for the query");
    559. }
    560. 6657 return " " + source.map(
    561. -
    562. function (s) {
    563. -
    564. 6971 return this.__tableRef(s);
    565. -
    566. }, this).join(this._static.COMMA_SEPARATOR);
    567. +
    568. function (s) {
    569. +
    570. 6971 return this.__tableRef(s);
    571. +
    572. }, this).join(this._static.COMMA_SEPARATOR);
    573. },
    574. /**
    575. @@ -15712,7 +15729,7 @@
    576. * @type String
    577. */
    578. identifierInputMethod:function () {
    579. -
    580. 15364 return this.__identifierInputMethod;
    581. +
    582. 15038 return this.__identifierInputMethod;
    583. },
    584. /**
    585. @@ -15724,7 +15741,7 @@
    586. * @type String
    587. */
    588. identifierOutputMethod:function () {
    589. -
    590. 15364 return this.__identifierOutputMethod;
    591. +
    592. 15038 return this.__identifierOutputMethod;
    593. },
    594. /**
    595. @@ -15737,7 +15754,7 @@
    596. * @default true
    597. */
    598. quoteIdentifiers:function () {
    599. -
    600. 15330 return this.__quoteIdentifiers;
    601. +
    602. 15004 return this.__quoteIdentifiers;
    603. }
    604. },
    605. @@ -15880,10 +15897,10 @@
    606. /**@ignore*/
    607. constructor:function () {
    608. -
    609. 29542 if (!Dataset) {
    610. +
    611. 29216 if (!Dataset) {
    612. 1 Dataset = require("../index").Dataset;
    613. }
    614. -
    615. 29542 this._super(arguments);
    616. +
    617. 29216 this._super(arguments);
    618. },
    619. @@ -15913,7 +15930,7 @@
    620. 2734 var a = [];
    621. 2734 var ret = new Promise().classic(cb);
    622. 2734 this.forEach(hitch(this, function (r) {
    623. -
    624. 2934 a.push(r);
    625. +
    626. 2947 a.push(r);
    627. })).then(hitch(this, function () {
    628. 2731 this.postLoad(a);
    629. 2731 if (block) {
    630. @@ -17159,10 +17176,10 @@
    631. *
    632. */
    633. constructor:function () {
    634. -
    635. 3111 if (comb.isUndefinedOrNull(this.__associations)) {
    636. -
    637. 3002 this.__associations = {};
    638. +
    639. 3107 if (comb.isUndefinedOrNull(this.__associations)) {
    640. +
    641. 3000 this.__associations = {};
    642. }
    643. -
    644. 3111 this._super(arguments);
    645. +
    646. 3107 this._super(arguments);
    647. },
    648. reload:function () {
    649. @@ -18089,240 +18106,29 @@
    650. * });
    651. * }
    652. *
    653. -
    654. *
    655. -
    656. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    657. -
    658. *
    659. -
    660. * @throws {patio.ModelError} if name is not a function or string.
    661. -
    662. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    663. -
    664. */
    665. -
    666. validate:function (name) {
    667. -
    668. 41 this.__initValidation();
    669. -
    670. 41 var ret;
    671. -
    672. 41 if (isFunction(name)) {
    673. -
    674. 1 name.apply(this, [this.__getValidator.bind(this)]);
    675. -
    676. 1 ret = this;
    677. -
    678. 40 } else if (isString(name)) {
    679. -
    680. 40 ret = this.__getValidator(name);
    681. -
    682. } else {
    683. -
    684. 0 throw new ModelError("name is must be a string or function when validating");
    685. -
    686. }
    687. -
    688. 41 return ret;
    689. -
    690. }
    691. -
    692. }
    693. -
    694. -
    695. }).as(module);
    696. -
    -
    - -
    - - - - - -
    -
    database/logging.js
    -
    -
    - Coverage97.96 - SLOC193 - LOC49 - Missed1 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. define = comb.define,
    4. -
    5. Promise = comb.Promise,
    6. -
    7. isFunction = comb.isFunction,
    8. -
    9. logging = comb.logging,
    10. -
    11. Logger = logging.Logger,
    12. -
    13. hitch = comb.hitch,
    14. -
    15. format = comb.string.format,
    16. -
    17. QueryError = require("../errors").QueryError;
    18. -
    19. -
    20. -
    21. 1var LOGGER = Logger.getLogger("patio.Database");
    22. -
    23. -
    24. 1define(null, {
    25. -
    26. instance:{
    27. -
    28. /**@lends patio.Database.prototype*/
    29. -
    30. -
    31. /**
    32. -
    33. * Logs an INFO level message to the "patio.Database" logger.
    34. -
    35. */
    36. -
    37. logInfo:function () {
    38. -
    39. 8337 if (LOGGER.isInfo) {
    40. -
    41. 8337 LOGGER.info.apply(LOGGER, arguments);
    42. -
    43. }
    44. -
    45. },
    46. -
    47. -
    48. /**
    49. -
    50. * Logs a DEBUG level message to the "patio.Database" logger.
    51. -
    52. */
    53. -
    54. logDebug:function () {
    55. -
    56. 8027 if (LOGGER.isDebug) {
    57. -
    58. 8027 LOGGER.debug.apply(LOGGER, arguments);
    59. -
    60. }
    61. -
    62. },
    63. -
    64. -
    65. /**
    66. -
    67. * Logs an ERROR level message to the "patio.Database" logger.
    68. -
    69. */
    70. -
    71. logError:function (error) {
    72. -
    73. 75 if (LOGGER.isError) {
    74. -
    75. 75 LOGGER.error.apply(LOGGER, arguments);
    76. -
    77. }
    78. -
    79. },
    80. -
    81. -
    82. /**
    83. -
    84. * Logs a WARN level message to the "patio.Database" logger.
    85. -
    86. */
    87. -
    88. logWarn:function () {
    89. -
    90. 1 if (LOGGER.isWarn) {
    91. -
    92. 1 LOGGER.warn.apply(LOGGER, arguments);
    93. -
    94. }
    95. -
    96. },
    97. -
    98. -
    99. /**
    100. -
    101. * Logs a TRACE level message to the "patio.Database" logger.
    102. -
    103. */
    104. -
    105. logTrace:function () {
    106. -
    107. 1 if (LOGGER.isTrace) {
    108. -
    109. 1 LOGGER.trace.apply(LOGGER, arguments);
    110. -
    111. }
    112. -
    113. },
    114. -
    115. -
    116. /**
    117. -
    118. * Logs a FATAL level message to the "patio.Database" logger.
    119. -
    120. */
    121. -
    122. logFatal:function () {
    123. -
    124. 1 if (LOGGER.isFatal) {
    125. -
    126. 1 LOGGER.fatal.apply(LOGGER, arguments);
    127. -
    128. }
    129. -
    130. },
    131. -
    132. -
    133. /* Yield to the block, logging any errors at error level to all loggers,
    134. -
    135. * and all other queries with the duration at warn or info level.
    136. -
    137. * */
    138. -
    139. __logAndExecute:function (sql, args, cb) {
    140. -
    141. 8101 if (isFunction(args)) {
    142. -
    143. 8100 cb = args;
    144. -
    145. 8100 args = null;
    146. -
    147. }
    148. -
    149. -
    150. 8101 if (args) {
    151. -
    152. 0 sql = format("%s; %j", sql, args);
    153. -
    154. }
    155. -
    156. 8101 sql = sql.trim();
    157. -
    158. 8101 var start = new Date();
    159. -
    160. 8101 var ret = new Promise();
    161. -
    162. 8101 if (isFunction(cb)) {
    163. -
    164. 8100 this.logInfo("Executing; %s", sql);
    165. -
    166. 8100 cb().then(hitch(this, function () {
    167. -
    168. 8026 this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
    169. -
    170. 8026 ret.callback.apply(ret, arguments);
    171. -
    172. }), hitch(this, function (err) {
    173. -
    174. 74 err = new QueryError(format("%s: %s", err.message, sql));
    175. -
    176. 74 this.logError(err);
    177. -
    178. 74 ret.errback.apply(ret, [err]);
    179. -
    180. }));
    181. -
    182. } else {
    183. -
    184. 1 throw new QueryError("CB is required");
    185. -
    186. }
    187. -
    188. 8100 return ret.promise();
    189. -
    190. },
    191. -
    192. -
    193. /*Log the given SQL and then execute it on the connection, used by
    194. -
    195. *the transaction code.
    196. -
    197. * */
    198. -
    199. __logConnectionExecute:function (conn, sql) {
    200. -
    201. 2079 return this.__logAndExecute(sql, hitch(this, function () {
    202. -
    203. 2079 return conn[this.connectionExecuteMethod](sql);
    204. -
    205. }));
    206. -
    207. },
    208. -
    209. -
    210. -
    211. getters:{
    212. -
    213. /**@lends patio.Database.prototype*/
    214. -
    215. /**
    216. -
    217. * The "patio.Database" logger.
    218. -
    219. * @field
    220. -
    221. */
    222. -
    223. logger:function () {
    224. -
    225. 2 return LOGGER;
    226. -
    227. }
    228. -
    229. }
    230. -
    231. },
    232. -
    233. -
    234. "static":{
    235. -
    236. /**@lends patio.Database*/
    237. -
    238. /**
    239. -
    240. * Logs an INFO level message to the "patio.Database" logger.
    241. -
    242. */
    243. -
    244. logInfo:function () {
    245. -
    246. 1 if (LOGGER.isInfo) {
    247. -
    248. 1 LOGGER.info.apply(LOGGER, arguments);
    249. -
    250. }
    251. -
    252. },
    253. -
    254. -
    255. /**
    256. -
    257. * Logs a DEBUG level message to the "patio.Database" logger.
    258. -
    259. */
    260. -
    261. logDebug:function () {
    262. -
    263. 1 if (LOGGER.isDebug) {
    264. -
    265. 1 LOGGER.debug.apply(LOGGER, arguments);
    266. -
    267. }
    268. -
    269. },
    270. -
    271. -
    272. /**
    273. -
    274. * Logs a ERROR level message to the "patio.Database" logger.
    275. -
    276. */
    277. -
    278. logError:function () {
    279. -
    280. 1 if (LOGGER.isError) {
    281. -
    282. 1 LOGGER.error.apply(LOGGER, arguments);
    283. -
    284. }
    285. -
    286. },
    287. -
    288. -
    289. /**
    290. -
    291. * Logs a WARN level message to the "patio.Database" logger.
    292. -
    293. */
    294. -
    295. logWarn:function () {
    296. -
    297. 1 if (LOGGER.isWarn) {
    298. -
    299. 1 LOGGER.warn.apply(LOGGER, arguments);
    300. -
    301. }
    302. -
    303. },
    304. -
    305. -
    306. /**
    307. -
    308. * Logs a TRACE level message to the "patio.Database" logger.
    309. -
    310. */
    311. -
    312. logTrace:function () {
    313. -
    314. 1 if (LOGGER.isTrace) {
    315. -
    316. 1 LOGGER.trace.apply(LOGGER, arguments);
    317. -
    318. }
    319. -
    320. },
    321. -
    322. -
    323. /**
    324. -
    325. * Logs a FATAL level message to the "patio.Database" logger.
    326. +
    327. *
    328. +
    329. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    330. +
    331. *
    332. +
    333. * @throws {patio.ModelError} if name is not a function or string.
    334. +
    335. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    336. */
    337. -
    338. logFatal:function () {
    339. -
    340. 1 if (LOGGER.isFatal) {
    341. -
    342. 1 LOGGER.fatal.apply(LOGGER, arguments);
    343. -
    344. }
    345. -
    346. },
    347. -
    348. -
    349. getters:{
    350. -
    351. /**@lends patio.Database*/
    352. -
    353. /**
    354. -
    355. * The "patio.Database" logger.
    356. -
    357. * @field
    358. -
    359. */
    360. -
    361. logger:function () {
    362. -
    363. 1 return LOGGER;
    364. +
    365. validate:function (name) {
    366. +
    367. 41 this.__initValidation();
    368. +
    369. 41 var ret;
    370. +
    371. 41 if (isFunction(name)) {
    372. +
    373. 1 name.apply(this, [this.__getValidator.bind(this)]);
    374. +
    375. 1 ret = this;
    376. +
    377. 40 } else if (isString(name)) {
    378. +
    379. 40 ret = this.__getValidator(name);
    380. +
    381. } else {
    382. +
    383. 0 throw new ModelError("name is must be a string or function when validating");
    384. }
    385. +
    386. 41 return ret;
    387. }
    388. }
    389. -
    390. }).as(module);
    +
  • }).as(module);
  • +
  • @@ -18333,10 +18139,10 @@
    - Coverage98.84 - SLOC2136 - LOC431 - Missed5 + Coverage97.71 + SLOC2160 + LOC436 + Missed10
    @@ -18401,17 +18207,17 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 29542 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 29542 this._super(arguments);
  • -
  • 29542 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
  • -
  • 206794 if (!this[type + "Join"]) {
  • -
  • 206794 this[type + "Join"] = conditionedJoin.bind(this, type);
  • +
  • 29216 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 29216 this._super(arguments);
  • +
  • 29216 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
  • +
  • 204512 if (!this[type + "Join"]) {
  • +
  • 204512 this[type + "Join"] = conditionedJoin.bind(this, type);
  • }
  • }, this);
  • -
  • 29542 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
  • -
  • 147710 if (!this[type + "Join"]) {
  • -
  • 147710 this[type + "Join"] = unConditionJoin.bind(this, type);
  • +
  • 29216 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
  • +
  • 146080 if (!this[type + "Join"]) {
  • +
  • 146080 this[type + "Join"] = unConditionJoin.bind(this, type);
  • }
  • }, this);
  • @@ -19823,29 +19629,29 @@
  • * @return {patio.Dataset} a cloned dataset with the columns selected changed.
  • */
  • select:function (args) {
  • -
  • 861 args = flatten(argsToArray(arguments));
  • -
  • 861 var columns = [];
  • -
  • 861 args.forEach(function (c) {
  • -
  • 1281 if (isFunction(c)) {
  • +
  • 535 args = flatten(argsToArray(arguments));
  • +
  • 535 var columns = [];
  • +
  • 535 args.forEach(function (c) {
  • +
  • 955 if (isFunction(c)) {
  • 23 var res = c.apply(sql, [sql]);
  • 23 columns = columns.concat(isArray(res) ? res : [res]);
  • } else {
  • -
  • 1258 columns.push(c);
  • +
  • 932 columns.push(c);
  • }
  • });
  • -
  • 861 var select = [];
  • -
  • 861 columns.forEach(function (c) {
  • -
  • 1284 if (isHash(c)) {
  • +
  • 535 var select = [];
  • +
  • 535 columns.forEach(function (c) {
  • +
  • 958 if (isHash(c)) {
  • 3 for (var i in c) {
  • 4 select.push(new AliasedExpression(new Identifier(i), c[i]));
  • }
  • -
  • 1281 } else if (isString(c)) {
  • +
  • 955 } else if (isString(c)) {
  • 406 select.push(this.stringToIdentifier(c));
  • } else {
  • -
  • 875 select.push(c);
  • +
  • 549 select.push(c);
  • }
  • }, this);
  • -
  • 861 return this.mergeOptions({select:select});
  • +
  • 535 return this.mergeOptions({select:select});
  • },
  • @@ -19859,6 +19665,30 @@
  • },
  • /**
  • +
  • * Selects the columns if only if there is not already select sources.
  • +
  • *
  • +
  • * @example
  • +
  • *
  • +
  • * var ds = DB.from("items"); //SELECT * FROM items
  • +
  • *
  • +
  • * ds.select("a"); //SELECT a FROM items;
  • +
  • * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
  • +
  • * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
  • +
  • *
  • +
  • * @param cols columns to select if there is not already select sources.
  • +
  • * @return {patio.Dataset} a cloned dataset with the appropriate select sources.
  • +
  • */
  • +
  • selectIfNoSource:function (cols) {
  • +
  • 0 var ret;
  • +
  • 0 if (!this.hasSelectSource) {
  • +
  • 0 ret = this.select.apply(this, arguments);
  • +
  • } else {
  • +
  • 0 ret = this.mergeOptions();
  • +
  • }
  • +
  • 0 return ret;
  • +
  • },
  • +
  • +
  • /**
  • * Returns a copy of the dataset with the given columns added
  • * to the existing selected columns. If no columns are currently selected,
  • * it will select the columns given in addition to *.
  • @@ -20479,6 +20309,217 @@
  • as(module);
  • +
    + + + + + +
    +
    database/logging.js
    +
    +
    + Coverage97.96 + SLOC193 + LOC49 + Missed1 +
    +
    +
    1. 1var comb = require("comb"),
    2. +
    3. define = comb.define,
    4. +
    5. Promise = comb.Promise,
    6. +
    7. isFunction = comb.isFunction,
    8. +
    9. logging = comb.logging,
    10. +
    11. Logger = logging.Logger,
    12. +
    13. hitch = comb.hitch,
    14. +
    15. format = comb.string.format,
    16. +
    17. QueryError = require("../errors").QueryError;
    18. +
    19. +
    20. +
    21. 1var LOGGER = Logger.getLogger("patio.Database");
    22. +
    23. +
    24. 1define(null, {
    25. +
    26. instance:{
    27. +
    28. /**@lends patio.Database.prototype*/
    29. +
    30. +
    31. /**
    32. +
    33. * Logs an INFO level message to the "patio.Database" logger.
    34. +
    35. */
    36. +
    37. logInfo:function () {
    38. +
    39. 8337 if (LOGGER.isInfo) {
    40. +
    41. 8337 LOGGER.info.apply(LOGGER, arguments);
    42. +
    43. }
    44. +
    45. },
    46. +
    47. +
    48. /**
    49. +
    50. * Logs a DEBUG level message to the "patio.Database" logger.
    51. +
    52. */
    53. +
    54. logDebug:function () {
    55. +
    56. 8027 if (LOGGER.isDebug) {
    57. +
    58. 8027 LOGGER.debug.apply(LOGGER, arguments);
    59. +
    60. }
    61. +
    62. },
    63. +
    64. +
    65. /**
    66. +
    67. * Logs an ERROR level message to the "patio.Database" logger.
    68. +
    69. */
    70. +
    71. logError:function (error) {
    72. +
    73. 75 if (LOGGER.isError) {
    74. +
    75. 75 LOGGER.error.apply(LOGGER, arguments);
    76. +
    77. }
    78. +
    79. },
    80. +
    81. +
    82. /**
    83. +
    84. * Logs a WARN level message to the "patio.Database" logger.
    85. +
    86. */
    87. +
    88. logWarn:function () {
    89. +
    90. 1 if (LOGGER.isWarn) {
    91. +
    92. 1 LOGGER.warn.apply(LOGGER, arguments);
    93. +
    94. }
    95. +
    96. },
    97. +
    98. +
    99. /**
    100. +
    101. * Logs a TRACE level message to the "patio.Database" logger.
    102. +
    103. */
    104. +
    105. logTrace:function () {
    106. +
    107. 1 if (LOGGER.isTrace) {
    108. +
    109. 1 LOGGER.trace.apply(LOGGER, arguments);
    110. +
    111. }
    112. +
    113. },
    114. +
    115. +
    116. /**
    117. +
    118. * Logs a FATAL level message to the "patio.Database" logger.
    119. +
    120. */
    121. +
    122. logFatal:function () {
    123. +
    124. 1 if (LOGGER.isFatal) {
    125. +
    126. 1 LOGGER.fatal.apply(LOGGER, arguments);
    127. +
    128. }
    129. +
    130. },
    131. +
    132. +
    133. /* Yield to the block, logging any errors at error level to all loggers,
    134. +
    135. * and all other queries with the duration at warn or info level.
    136. +
    137. * */
    138. +
    139. __logAndExecute:function (sql, args, cb) {
    140. +
    141. 8101 if (isFunction(args)) {
    142. +
    143. 8100 cb = args;
    144. +
    145. 8100 args = null;
    146. +
    147. }
    148. +
    149. +
    150. 8101 if (args) {
    151. +
    152. 0 sql = format("%s; %j", sql, args);
    153. +
    154. }
    155. +
    156. 8101 sql = sql.trim();
    157. +
    158. 8101 var start = new Date();
    159. +
    160. 8101 var ret = new Promise();
    161. +
    162. 8101 if (isFunction(cb)) {
    163. +
    164. 8100 this.logInfo("Executing; %s", sql);
    165. +
    166. 8100 cb().then(hitch(this, function () {
    167. +
    168. 8026 this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
    169. +
    170. 8026 ret.callback.apply(ret, arguments);
    171. +
    172. }), hitch(this, function (err) {
    173. +
    174. 74 err = new QueryError(format("%s: %s", err.message, sql));
    175. +
    176. 74 this.logError(err);
    177. +
    178. 74 ret.errback.apply(ret, [err]);
    179. +
    180. }));
    181. +
    182. } else {
    183. +
    184. 1 throw new QueryError("CB is required");
    185. +
    186. }
    187. +
    188. 8100 return ret.promise();
    189. +
    190. },
    191. +
    192. +
    193. /*Log the given SQL and then execute it on the connection, used by
    194. +
    195. *the transaction code.
    196. +
    197. * */
    198. +
    199. __logConnectionExecute:function (conn, sql) {
    200. +
    201. 2079 return this.__logAndExecute(sql, hitch(this, function () {
    202. +
    203. 2079 return conn[this.connectionExecuteMethod](sql);
    204. +
    205. }));
    206. +
    207. },
    208. +
    209. +
    210. +
    211. getters:{
    212. +
    213. /**@lends patio.Database.prototype*/
    214. +
    215. /**
    216. +
    217. * The "patio.Database" logger.
    218. +
    219. * @field
    220. +
    221. */
    222. +
    223. logger:function () {
    224. +
    225. 2 return LOGGER;
    226. +
    227. }
    228. +
    229. }
    230. +
    231. },
    232. +
    233. +
    234. "static":{
    235. +
    236. /**@lends patio.Database*/
    237. +
    238. /**
    239. +
    240. * Logs an INFO level message to the "patio.Database" logger.
    241. +
    242. */
    243. +
    244. logInfo:function () {
    245. +
    246. 1 if (LOGGER.isInfo) {
    247. +
    248. 1 LOGGER.info.apply(LOGGER, arguments);
    249. +
    250. }
    251. +
    252. },
    253. +
    254. +
    255. /**
    256. +
    257. * Logs a DEBUG level message to the "patio.Database" logger.
    258. +
    259. */
    260. +
    261. logDebug:function () {
    262. +
    263. 1 if (LOGGER.isDebug) {
    264. +
    265. 1 LOGGER.debug.apply(LOGGER, arguments);
    266. +
    267. }
    268. +
    269. },
    270. +
    271. +
    272. /**
    273. +
    274. * Logs a ERROR level message to the "patio.Database" logger.
    275. +
    276. */
    277. +
    278. logError:function () {
    279. +
    280. 1 if (LOGGER.isError) {
    281. +
    282. 1 LOGGER.error.apply(LOGGER, arguments);
    283. +
    284. }
    285. +
    286. },
    287. +
    288. +
    289. /**
    290. +
    291. * Logs a WARN level message to the "patio.Database" logger.
    292. +
    293. */
    294. +
    295. logWarn:function () {
    296. +
    297. 1 if (LOGGER.isWarn) {
    298. +
    299. 1 LOGGER.warn.apply(LOGGER, arguments);
    300. +
    301. }
    302. +
    303. },
    304. +
    305. +
    306. /**
    307. +
    308. * Logs a TRACE level message to the "patio.Database" logger.
    309. +
    310. */
    311. +
    312. logTrace:function () {
    313. +
    314. 1 if (LOGGER.isTrace) {
    315. +
    316. 1 LOGGER.trace.apply(LOGGER, arguments);
    317. +
    318. }
    319. +
    320. },
    321. +
    322. +
    323. /**
    324. +
    325. * Logs a FATAL level message to the "patio.Database" logger.
    326. +
    327. */
    328. +
    329. logFatal:function () {
    330. +
    331. 1 if (LOGGER.isFatal) {
    332. +
    333. 1 LOGGER.fatal.apply(LOGGER, arguments);
    334. +
    335. }
    336. +
    337. },
    338. +
    339. +
    340. getters:{
    341. +
    342. /**@lends patio.Database*/
    343. +
    344. /**
    345. +
    346. * The "patio.Database" logger.
    347. +
    348. * @field
    349. +
    350. */
    351. +
    352. logger:function () {
    353. +
    354. 1 return LOGGER;
    355. +
    356. }
    357. +
    358. }
    359. +
    360. }
    361. +
    362. +
    363. }).as(module);
    +
    +
    @@ -21126,7 +21167,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(){
  • -
  • 49107 return this.__quoteIdentifiers;
  • +
  • 48455 return this.__quoteIdentifiers;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21134,20 +21175,20 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(){
  • -
  • 15274 return this.__providesAccurateRowsMatched;
  • +
  • 14948 return this.__providesAccurateRowsMatched;
  • },
  • //Whether the dataset requires SQL standard datetimes (false by default,
  • // as most allow strings with ISO 8601 format).
  • /**@ignore*/
  • requiresSqlStandardDateTimes:function(){
  • -
  • 15281 return this.__requiresSqlStandardDateTimes;
  • +
  • 14955 return this.__requiresSqlStandardDateTimes;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(){
  • -
  • 15287 return this.__supportsCte;
  • +
  • 14961 return this.__supportsCte;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • @@ -21159,25 +21200,25 @@
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(){
  • -
  • 15310 return this.__supportsIntersectExcept;
  • +
  • 14984 return this.__supportsIntersectExcept;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(){
  • -
  • 15286 return this.__supportsIntersectExceptAll;
  • +
  • 14960 return this.__supportsIntersectExceptAll;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(){
  • -
  • 15543 return this.__supportsIsTrue;
  • +
  • 15217 return this.__supportsIsTrue;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(){
  • -
  • 15285 return this.__supportsJoinUsing;
  • +
  • 14959 return this.__supportsJoinUsing;
  • },
  • //Whether modifying joined datasets is supported.
  • @@ -21189,7 +21230,7 @@
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(){
  • -
  • 15278 return this.__supportsMultipleColumnIn;
  • +
  • 14952 return this.__supportsMultipleColumnIn;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • @@ -21201,13 +21242,13 @@
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(){
  • -
  • 15274 return this.__supportsTimestampUsecs;
  • +
  • 14948 return this.__supportsTimestampUsecs;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(){
  • -
  • 15274 return this.__supportsWindowFunctions;
  • +
  • 14948 return this.__supportsWindowFunctions;
  • }
  • },
  • @@ -21218,7 +21259,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(val){
  • -
  • 15286 this.__quoteIdentifiers = val;
  • +
  • 14960 this.__quoteIdentifiers = val;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21226,20 +21267,20 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(val){
  • -
  • 15274 this.__providesAccurateRowsMatched = val;
  • +
  • 14948 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){
  • -
  • 15274 this.__requiresSqlStandardDateTimes = val;
  • +
  • 14948 this.__requiresSqlStandardDateTimes = val;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(val){
  • -
  • 15275 this.__supportsCte = val;
  • +
  • 14949 this.__supportsCte = val;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • @@ -21251,25 +21292,25 @@
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(val){
  • -
  • 15276 this.__supportsIntersectExcept = val;
  • +
  • 14950 this.__supportsIntersectExcept = val;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(val){
  • -
  • 15276 this.__supportsIntersectExceptAll = val;
  • +
  • 14950 this.__supportsIntersectExceptAll = val;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(val){
  • -
  • 15274 this.__supportsIsTrue = val;
  • +
  • 14948 this.__supportsIsTrue = val;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(val){
  • -
  • 15276 this.__supportsJoinUsing = val;
  • +
  • 14950 this.__supportsJoinUsing = val;
  • },
  • //Whether modifying joined datasets is supported.
  • @@ -21281,7 +21322,7 @@
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(val){
  • -
  • 15274 this.__supportsMultipleColumnIn = val;
  • +
  • 14948 this.__supportsMultipleColumnIn = val;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • @@ -21293,13 +21334,13 @@
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(val){
  • -
  • 15274 this.__supportsTimestampUsecs = val;
  • +
  • 14948 this.__supportsTimestampUsecs = val;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(val){
  • -
  • 15274 this.__supportsWindowFunctions = val;
  • +
  • 14948 this.__supportsWindowFunctions = val;
  • }
  • }
  • @@ -21393,8 +21434,8 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 29542 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 29542 this._super(arguments);
  • +
  • 29216 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 29216 this._super(arguments);
  • },
  • /**
  • diff --git a/docs/coverage.html b/docs/coverage.html index 70d5468b..21fb1ef8 100644 --- a/docs/coverage.html +++ b/docs/coverage.html @@ -256,10 +256,10 @@ @@ -2521,7 +2521,7 @@
  • },
  • _quotedIdentifier:function (c) {
  • -
  • 26239 return format('"%s"', c);
  • +
  • 25927 return format('"%s"', c);
  • },
  • __fullTextStringJoin:function (cols) {
  • @@ -2575,12 +2575,12 @@
  • 3048 var cols = [];
  • 3048 if (rows && rows.length) {
  • 2543 cols = this.__columns = fields && fields.length ? fields.map(function (f) {
  • -
  • 18560 return f.name;
  • +
  • 18922 return f.name;
  • }) : Object.keys(rows[0]);
  • //the pg driver does auto type coercion
  • 2543 cols = cols.map(function (c) {
  • -
  • 18560 return [oi(c), function (o) {
  • -
  • 27740 return o;
  • +
  • 18922 return [oi(c), function (o) {
  • +
  • 28420 return o;
  • }, c];
  • }, this);
  • }
  • @@ -2607,7 +2607,7 @@
  • 3654 var h = {};
  • 3654 var row = rows[index];
  • 3654 cols.forEach(function (col) {
  • -
  • 27740 h[col[0]] = col[1](row[col[2]]);
  • +
  • 28420 h[col[0]] = col[1](row[col[2]]);
  • }, this);
  • 3654 when(cb(h)).then(function (val) {
  • @@ -2659,15 +2659,15 @@
  • },
  • supportsDistinctOn:function () {
  • -
  • 12996 return true;
  • +
  • 12670 return true;
  • },
  • supportsModifyingJoins:function () {
  • -
  • 15440 return true;
  • +
  • 15114 return true;
  • },
  • supportsTimestampTimezones:function () {
  • -
  • 12994 return true;
  • +
  • 12668 return true;
  • }
  • }
  • @@ -5735,3296 +5735,3769 @@
  • -
    +
    - Coverage89.21 - SLOC21921 - LOC5357 - Missed578 + Coverage89.04 + SLOC21962 + LOC5365 + Missed588
    -
    associations/_Association.js
    +
    dataset/index.js
    - Coverage87.18 - SLOC515 - LOC156 - Missed20 + Coverage86.11 + SLOC457 + LOC72 + Missed10
    -
    1. 1var comb = require("comb-proxy"),
    2. -
    3. define = comb.define,
    4. +
      1. 1var comb = require("comb"),
      2. +
      3. hitch = comb.hitch,
      4. +
      5. logging = comb.logging,
      6. +
      7. Logger = logging.Logger,
      8. +
      9. errors = require("../errors"),
      10. +
      11. QueryError = errors.QueryError,
      12. +
      13. DatasetError = errors.DatasetError,
      14. +
      15. Promise = comb.Promise,
      16. +
      17. PromiseList = comb.PromiseList,
      18. isUndefined = comb.isUndefined,
      19. isUndefinedOrNull = comb.isUndefinedOrNull,
      20. -
      21. isBoolean = comb.isBoolean,
      22. isString = comb.isString,
      23. -
      24. isHash = comb.isHash,
      25. -
      26. isFunction = comb.isFunction,
      27. isInstanceOf = comb.isInstanceOf,
      28. -
      29. Promise = comb.Promise,
      30. -
      31. PromiseList = comb.PromiseList,
      32. -
      33. hitch = comb.hitch,
      34. -
      35. array = comb.array,
      36. -
      37. isArray = comb.isArray,
      38. -
      39. Middleware = comb.plugins.Middleware,
      40. -
      41. PatioError = require("../errors").PatioError;
      42. -
      43. -
      44. 1var fetch = {
      45. -
      46. LAZY:"lazy",
      47. -
      48. EAGER:"eager"
      49. -
      50. };
      51. +
      52. isString = comb.isString,
      53. +
      54. isFunction = comb.isFunction,
      55. +
      56. isNull = comb.isNull,
      57. +
      58. merge = comb.merge,
      59. +
      60. define = comb.define,
      61. +
      62. graph = require("./graph"),
      63. +
      64. actions = require("./actions"),
      65. +
      66. features = require("./features"),
      67. +
      68. query = require("./query"),
      69. +
      70. sql = require("./sql"),
      71. +
      72. SQL = require("../sql").sql,
      73. +
      74. AliasedExpression = SQL.AliasedExpression,
      75. +
      76. Identifier = SQL.Identifier,
      77. +
      78. QualifiedIdentifier = SQL.QualifiedIdentifier;
      79. -
      80. /**
      81. -
      82. * @class
      83. -
      84. * Base class for all associations.
      85. -
      86. *
      87. -
      88. * </br>
      89. -
      90. * <b>NOT to be instantiated directly</b>
      91. -
      92. * Its just documented for reference.
      93. -
      94. *
      95. -
      96. * @constructs
      97. -
      98. * @param {Object} options
      99. -
      100. * @param {String} options.model a string to look up the model that we are associated with
      101. -
      102. * @param {Function} options.filter a callback to find association if a filter is defined then
      103. -
      104. * the association is read only
      105. -
      106. * @param {Object} options.key object with left key and right key
      107. -
      108. * @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
      109. -
      110. * @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
      111. -
      112. * the associations are automatically filled, if fetch.Lazy is supplied
      113. -
      114. * then a promise is returned and is called back with the loaded models
      115. -
      116. * @property {Model} model the model associatied with this association.
      117. -
      118. * @name Association
      119. -
      120. * @memberOf patio.associations
      121. -
      122. * */
      123. -
      124. 1define(Middleware, {
      125. +
      126. 1var LOGGER = comb.logger("patio.Dataset");
      127. +
      128. 1define([actions, graph, features, query, sql], {
      129. instance:{
      130. -
      131. /**@lends patio.associations.Association.prototype*/
      132. -
      133. -
      134. type:"",
      135. -
      136. -
      137. //Our associated model
      138. -
      139. _model:null,
      140. -
      141. -
      142. /**
      143. -
      144. * Fetch type
      145. -
      146. */
      147. -
      148. fetchType:fetch.LAZY,
      149. -
      150. -
      151. /**how to order our association*/
      152. -
      153. orderBy:null,
      154. -
      155. -
      156. /**Our filter method*/
      157. -
      158. filter:null,
      159. -
      160. -
      161. __hooks:null,
      162. -
      163. -
      164. isOwner:true,
      165. -
      166. -
      167. createSetter:true,
      168. -
      169. isCascading:false,
      170. -
      171. -
      172. supportsStringKey:true,
      173. -
      174. -
      175. supportsHashKey:true,
      176. -
      177. -
      178. supportsCompositeKey:true,
      179. -
      180. -
      181. supportsLeftAndRightKey:true,
      182. +
      183. /**@lends patio.Dataset.prototype*/
      184. /**
      185. +
      186. * Class that is used for querying/retirving datasets from a database.
      187. *
      188. -
      189. *Method to call to look up association,
      190. -
      191. *called after the model has been filtered
      192. -
      193. **/
      194. -
      195. _fetchMethod:"all",
      196. -
      197. -
      198. -
      199. constructor:function (options, patio, filter) {
      200. -
      201. 55 options = options || {};
      202. -
      203. 55 if (!options.model) {
      204. -
      205. 0 throw new Error("Model is required for " + this.type + " association");
      206. -
      207. }
      208. -
      209. 55 this._model = options.model;
      210. -
      211. 55 this.patio = patio;
      212. -
      213. 55 this.__opts = options;
      214. -
      215. 55 !isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
      216. -
      217. 55 this.filter = filter;
      218. -
      219. 55 this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
      220. -
      221. 55 this.__hooks =
      222. -
      223. {before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
      224. -
      225. 55 var hooks = ["Add", "Remove", "Set", "Load"];
      226. -
      227. 55 ["before", "after"].forEach(function (h) {
      228. -
      229. 110 hooks.forEach(function (a) {
      230. -
      231. 440 var hookName = h + a, hook;
      232. -
      233. 440 if (isFunction((hook = options[hookName]))) {
      234. -
      235. 0 this.__hooks[h][a.toLowerCase()] = hook;
      236. -
      237. }
      238. -
      239. }, this);
      240. -
      241. }, this);
      242. -
      243. 55 this.fetchType = options.fetchType || fetch.LAZY;
      244. -
      245. },
      246. -
      247. -
      248. _callHook:function (hook, action, args) {
      249. -
      250. 0 var func = this.__hooks[hook][action], ret;
      251. -
      252. 0 if (isFunction(func)) {
      253. -
      254. 0 ret = func.apply(this, args);
      255. -
      256. }
      257. -
      258. 0 return ret;
      259. -
      260. },
      261. -
      262. -
      263. _clearAssociations:function (model) {
      264. -
      265. 125 if (!this.readOnly) {
      266. -
      267. 125 delete model.__associations[this.name];
      268. -
      269. }
      270. -
      271. },
      272. -
      273. -
      274. _forceReloadAssociations:function (model) {
      275. -
      276. 243 if (!this.readOnly) {
      277. -
      278. 243 delete model.__associations[this.name];
      279. -
      280. 243 return model[this.name];
      281. -
      282. }
      283. -
      284. },
      285. -
      286. -
      287. /**
      288. -
      289. * @return {Boolean} true if the association is eager.
      290. -
      291. */
      292. -
      293. isEager:function () {
      294. -
      295. 2114 return this.fetchType == fetch.EAGER;
      296. -
      297. },
      298. -
      299. -
      300. _checkAssociationKey:function (parent) {
      301. -
      302. 785 var q = {};
      303. -
      304. 785 this._setAssociationKeys(parent, q);
      305. -
      306. 785 return Object.keys(q).every(function (k) {
      307. -
      308. 785 return q[k] != null
      309. -
      310. });
      311. -
      312. },
      313. -
      314. -
      315. _getAssociationKey:function () {
      316. -
      317. 5205 var options = this.__opts, key, ret = [], lk, rk;
      318. -
      319. 5205 if (!isUndefinedOrNull((key = options.key))) {
      320. -
      321. 722 if (this.supportsStringKey && isString(key)) {
      322. -
      323. //normalize the key first!
      324. -
      325. 356 ret = [
      326. -
      327. [this.isOwner ? this.defaultLeftKey : key],
      328. -
      329. [this.isOwner ? key : this.defaultRightKey]
      330. -
      331. ];
      332. -
      333. 366 } else if (this.supportsHashKey && isHash(key)) {
      334. -
      335. 366 var leftKey = Object.keys(key)[0];
      336. -
      337. 366 var rightKey = key[leftKey];
      338. -
      339. 366 ret = [
      340. -
      341. [leftKey],
      342. -
      343. [rightKey]
      344. -
      345. ];
      346. -
      347. 0 } else if (this.supportsCompositeKey && isArray(key)) {
      348. -
      349. 0 ret = [
      350. -
      351. [key],
      352. -
      353. null
      354. -
      355. ];
      356. -
      357. }
      358. -
      359. 4483 } else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
      360. -
      361. && !isUndefinedOrNull((rk = options.rightKey)))) {
      362. -
      363. 140 ret = [
      364. -
      365. array.toArray(lk), array.toArray(rk)
      366. -
      367. ];
      368. -
      369. } else {
      370. -
      371. //todo handle composite primary keys
      372. -
      373. 4343 ret = [
      374. -
      375. [this.defaultLeftKey],
      376. -
      377. [this.defaultRightKey]
      378. -
      379. ];
      380. -
      381. }
      382. -
      383. 5205 return ret;
      384. -
      385. },
      386. -
      387. -
      388. -
      389. _setAssociationKeys:function (parent, model, val) {
      390. -
      391. 1573 var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
      392. -
      393. 1573 if (leftKey && rightKey) {
      394. -
      395. 1573 for (var i = 0; i < leftKey.length; i++) {
      396. -
      397. 1573 model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
      398. -
      399. }
      400. -
      401. } else {
      402. -
      403. 0 leftKey.forEach(function (k) {
      404. -
      405. 0 model[k] = !isUndefined(val) ? val : parent[k];
      406. -
      407. });
      408. -
      409. }
      410. -
      411. },
      412. -
      413. -
      414. _setDatasetOptions:function (ds) {
      415. -
      416. 974 var options = this.__opts || {};
      417. -
      418. 974 var order, limit, distinct, select, query;
      419. -
      420. //allow for multi key ordering
      421. -
      422. 974 if (!isUndefined((select = this.select))) {
      423. -
      424. 0 ds = ds.select.apply(ds, array.toArray(select));
      425. -
      426. }
      427. -
      428. 974 if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
      429. -
      430. 0 ds = ds.filter(query);
      431. -
      432. }
      433. -
      434. 974 if (isFunction(this.filter)) {
      435. -
      436. 334 var ret = this.filter.apply(this, [ds]);
      437. -
      438. 334 if (isInstanceOf(ret, ds._static)) {
      439. -
      440. 334 ds = ret;
      441. -
      442. }
      443. -
      444. }
      445. -
      446. 974 if (!isUndefined((distinct = options.distinct))) {
      447. -
      448. 0 ds = ds.limit.apply(ds, array.toArray(distinct));
      449. -
      450. }
      451. -
      452. 974 if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
      453. -
      454. 0 ds = ds.order.apply(ds, array.toArray(order));
      455. -
      456. }
      457. -
      458. 974 if (!isUndefined((limit = options.limit))) {
      459. -
      460. 0 ds = ds.limit.apply(ds, array.toArray(limit));
      461. -
      462. }
      463. -
      464. 974 return ds;
      465. -
      466. -
      467. },
      468. -
      469. -
      470. /**
      471. -
      472. *Filters our associated dataset to load our association.
      473. +
      474. * <p> Dynamically genertated methods include
      475. +
      476. * <ul>
      477. +
      478. * <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
      479. +
      480. * {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
      481. +
      482. * to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
      483. +
      484. * {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
      485. +
      486. * <ul>
      487. +
      488. * <li>Conditioned join types that accept conditions.
      489. +
      490. * <ul>
      491. +
      492. * <li>inner - INNER JOIN</li>
      493. +
      494. * <li>fullOuter - FULL OUTER</li>
      495. +
      496. * <li>rightOuter - RIGHT OUTER JOIN</li>
      497. +
      498. * <li>leftOuter - LEFT OUTER JOIN</li>
      499. +
      500. * <li>full - FULL JOIN</li>
      501. +
      502. * <li>right - RIGHT JOIN</li>
      503. +
      504. * <li>left - LEFT JOIN</li>
      505. +
      506. * </ul>
      507. +
      508. * </li>
      509. +
      510. * <li>Unconditioned join types that do not accept join conditions
      511. +
      512. * <ul>
      513. +
      514. * <li>natural - NATURAL JOIN</li>
      515. +
      516. * <li>naturalLeft - NATURAL LEFT JOIN</li>
      517. +
      518. * <li>naturalRight - NATURAL RIGHT JOIN</li>
      519. +
      520. * <li>naturalFull - NATURA FULLL JOIN</li>
      521. +
      522. * <li>cross - CROSS JOIN</li>
      523. +
      524. * </ul>
      525. +
      526. * </li>
      527. +
      528. * </ul>
      529. +
      530. * </li>
      531. +
      532. * </li>
      533. +
      534. * </ul>
      535. *
      536. -
      537. *@return {Dataset} the dataset with all filters applied.
      538. -
      539. **/
      540. -
      541. _filter:function (parent) {
      542. -
      543. 648 var options = this.__opts || {};
      544. -
      545. 648 var ds;
      546. -
      547. 648 if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {
      548. -
      549. 0 ds = ds.apply(parent, [parent]);
      550. -
      551. }
      552. -
      553. 648 if (!ds) {
      554. -
      555. 648 var q = {};
      556. -
      557. 648 this._setAssociationKeys(parent, q);
      558. -
      559. 648 ds = this.model.dataset.naked().filter(q);
      560. -
      561. 648 var recip = this.model._findAssociation(this);
      562. -
      563. 648 recip && (recip = recip[1]);
      564. -
      565. 648 ds.rowCb = hitch(this, function (item) {
      566. -
      567. 489 var ret = new Promise();
      568. -
      569. 489 var model = this._toModel(item, true);
      570. -
      571. 489 recip && recip.__setValue(model, parent);
      572. -
      573. //call hook to finish other model associations
      574. -
      575. 489 model._hook("post", "load").then(hitch(this, function () {
      576. -
      577. 489 ret.callback(model);
      578. -
      579. }), ret);
      580. -
      581. 489 return ret.promise();
      582. -
      583. });
      584. +
      585. * <p>
      586. +
      587. * <h4>Features:</h4>
      588. +
      589. * <p>
      590. +
      591. * Features that a particular {@link patio.Dataset} supports are shown in the example below.
      592. +
      593. * If you wish to implement an adapter please override these values depending on the database that
      594. +
      595. * you are developing the adapter for.
      596. +
      597. * </p>
      598. +
      599. * <pre class="code">
      600. +
      601. * var ds = DB.from("test");
      602. +
      603. *
      604. +
      605. * //The default values returned
      606. +
      607. *
      608. +
      609. * //Whether this dataset quotes identifiers.
      610. +
      611. * //Whether this dataset quotes identifiers.
      612. +
      613. * ds.quoteIdentifiers //=>true
      614. +
      615. *
      616. +
      617. * //Whether this dataset will provide accurate number of rows matched for
      618. +
      619. * //delete and update statements. Accurate in this case is the number of
      620. +
      621. * //rows matched by the dataset's filter.
      622. +
      623. * ds.providesAccurateRowsMatched; //=>true
      624. +
      625. *
      626. +
      627. * //Times Whether the dataset requires SQL standard datetimes (false by default,
      628. +
      629. * // as most allow strings with ISO 8601 format).
      630. +
      631. * ds.requiresSqlStandardDate; //=>false
      632. +
      633. *
      634. +
      635. * //Whether the dataset supports common table expressions (the WITH clause).
      636. +
      637. * ds.supportsCte; //=>true
      638. +
      639. *
      640. +
      641. * //Whether the dataset supports the DISTINCT ON clause, false by default.
      642. +
      643. * ds.supportsDistinctOn; //=>false
      644. +
      645. *
      646. +
      647. * //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      648. +
      649. * ds.supportsIntersectExcept; //=>true
      650. +
      651. *
      652. +
      653. * //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
      654. +
      655. * ds.supportsIntersectExceptAll; //=>true
      656. +
      657. *
      658. +
      659. * //Whether the dataset supports the IS TRUE syntax.
      660. +
      661. * ds.supportsIsTrue; //=>true
      662. +
      663. *
      664. +
      665. * //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      666. +
      667. * ds.supportsJoinUsing; //=>true
      668. +
      669. *
      670. +
      671. * //Whether modifying joined datasets is supported.
      672. +
      673. * ds.supportsModifyingJoin; //=>false
      674. +
      675. *
      676. +
      677. * //Whether the IN/NOT IN operators support multiple columns when an
      678. +
      679. * ds.supportsMultipleColumnIn; //=>true
      680. +
      681. *
      682. +
      683. * //Whether the dataset supports timezones in literal timestamps
      684. +
      685. * ds.supportsTimestampTimezone; //=>false
      686. +
      687. *
      688. +
      689. * //Whether the dataset supports fractional seconds in literal timestamps
      690. +
      691. * ds.supportsTimestampUsecs; //=>true
      692. +
      693. *
      694. +
      695. * //Whether the dataset supports window functions.
      696. +
      697. * ds.supportsWindowFunctions; //=>false
      698. +
      699. * </pre>
      700. +
      701. * <p>
      702. +
      703. * <p>
      704. +
      705. * <h4>Actions</h4>
      706. +
      707. * <p>
      708. +
      709. * Each dataset does not actually send any query to the database until an action method has
      710. +
      711. * been called upon it(with the exception of {@link patio.Dataset#graph} because columns
      712. +
      713. * from the other table might need retrived in order to set up the graph). Each action
      714. +
      715. * returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
      716. +
      717. * that you account for errors otherwise it can be difficult to track down issues.
      718. +
      719. * The list of action methods is:
      720. +
      721. * <ul>
      722. +
      723. * <li>{@link patio.Dataset#all}</li>
      724. +
      725. * <li>{@link patio.Dataset#one}</li>
      726. +
      727. * <li>{@link patio.Dataset#avg}</li>
      728. +
      729. * <li>{@link patio.Dataset#count}</li>
      730. +
      731. * <li>{@link patio.Dataset#columns}</li>
      732. +
      733. * <li>{@link patio.Dataset#remove}</li>
      734. +
      735. * <li>{@link patio.Dataset#forEach}</li>
      736. +
      737. * <li>{@link patio.Dataset#empty}</li>
      738. +
      739. * <li>{@link patio.Dataset#first}</li>
      740. +
      741. * <li>{@link patio.Dataset#get}</li>
      742. +
      743. * <li>{@link patio.Dataset#import}</li>
      744. +
      745. * <li>{@link patio.Dataset#insert}</li>
      746. +
      747. * <li>{@link patio.Dataset#save}</li>
      748. +
      749. * <li>{@link patio.Dataset#insertMultiple}</li>
      750. +
      751. * <li>{@link patio.Dataset#saveMultiple}</li>
      752. +
      753. * <li>{@link patio.Dataset#interval}</li>
      754. +
      755. * <li>{@link patio.Dataset#last}</li>
      756. +
      757. * <li>{@link patio.Dataset#map}</li>
      758. +
      759. * <li>{@link patio.Dataset#max}</li>
      760. +
      761. * <li>{@link patio.Dataset#min}</li>
      762. +
      763. * <li>{@link patio.Dataset#multiInsert}</li>
      764. +
      765. * <li>{@link patio.Dataset#range}</li>
      766. +
      767. * <li>{@link patio.Dataset#selectHash}</li>
      768. +
      769. * <li>{@link patio.Dataset#selectMap}</li>
      770. +
      771. * <li>{@link patio.Dataset#selectOrderMap}</li>
      772. +
      773. * <li>{@link patio.Dataset#set}</li>
      774. +
      775. * <li>{@link patio.Dataset#singleRecord}</li>
      776. +
      777. * <li>{@link patio.Dataset#singleValue}</li>
      778. +
      779. * <li>{@link patio.Dataset#sum}</li>
      780. +
      781. * <li>{@link patio.Dataset#toCsv}</li>
      782. +
      783. * <li>{@link patio.Dataset#toHash}</li>
      784. +
      785. * <li>{@link patio.Dataset#truncate}</li>
      786. +
      787. * <li>{@link patio.Dataset#update}</li>
      788. +
      789. * </ul>
      790. +
      791. *
      792. +
      793. * </p>
      794. +
      795. * </p>
      796. +
      797. *
      798. +
      799. * @constructs
      800. +
      801. *
      802. +
      803. *
      804. +
      805. * @param {patio.Database} db the database this dataset should use when querying for data.
      806. +
      807. * @param {Object} opts options to set on this dataset instance
      808. +
      809. *
      810. +
      811. * @property {Function} rowCb callback to be invoked for each row returned from the database.
      812. +
      813. * the return value will be used as the result of query. The rowCb can also return a promise,
      814. +
      815. * The resolved value of the promise will be used as result.
      816. +
      817. *
      818. +
      819. * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
      820. +
      821. * This value will be defaulted to whatever the identifierInputMethod
      822. +
      823. * is on the database used in initialization.
      824. +
      825. *
      826. +
      827. * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
      828. +
      829. * This value will be defaulted to whatever the identifierOutputMethod
      830. +
      831. * is on the database used in initialization.
      832. +
      833. * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
      834. +
      835. * throws a {patio.DatasetError} tf the dataset doesn't have a table.
      836. +
      837. * <pre class="code">
      838. +
      839. * DB.from("table").firstSourceAlias;
      840. +
      841. * //=> "table"
      842. +
      843. *
      844. +
      845. * DB.from("table___t").firstSourceAlias;
      846. +
      847. * //=> "t"
      848. +
      849. * </pre>
      850. +
      851. *
      852. +
      853. * @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
      854. +
      855. * have a table, raises a {@link patio.erros.DatasetError}.
      856. +
      857. *<pre class="code">
      858. +
      859. *
      860. +
      861. * DB.from("table").firstSourceTable;
      862. +
      863. * //=> "table"
      864. +
      865. *
      866. +
      867. * DB.from("table___t").firstSourceTable;
      868. +
      869. * //=> "t"
      870. +
      871. * </pre>
      872. +
      873. * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
      874. +
      875. * <pre class="code">
      876. +
      877. * DB.from("items").isSimpleSelectAll; //=> true
      878. +
      879. * DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
      880. +
      881. * </pre>
      882. +
      883. * @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
      884. +
      885. * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
      886. +
      887. * delete and update statements. Accurate in this case is the number of
      888. +
      889. * rows matched by the dataset's filter.
      890. +
      891. * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
      892. +
      893. * as most allow strings with ISO 8601 format).
      894. +
      895. * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
      896. +
      897. * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
      898. +
      899. * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
      900. +
      901. * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
      902. +
      903. * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
      904. +
      905. * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
      906. +
      907. * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
      908. +
      909. * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
      910. +
      911. * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
      912. +
      913. * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
      914. +
      915. * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
      916. +
      917. * @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset.
      918. +
      919. * @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources
      920. +
      921. * @property {Boolean} hasSelectSource true if this dataset already has a select sources.
      922. +
      923. */
      924. +
      925. constructor:function (db, opts) {
      926. +
      927. 29216 this._super(arguments);
      928. +
      929. 29216 this.db = db;
      930. +
      931. 29216 this.__opts = {};
      932. +
      933. 29216 this.__rowCb = null;
      934. +
      935. 29216 if (db) {
      936. +
      937. 15039 this.__quoteIdentifiers = db.quoteIdentifiers;
      938. +
      939. 15039 this.__identifierInputMethod = db.identifierInputMethod;
      940. +
      941. 15039 this.__identifierOutputMethod = db.identifierOutputMethod;
      942. }
      943. -
      944. -
      945. 648 return this._setDatasetOptions(ds);
      946. },
      947. -
      948. __setValue:function (parent, model) {
      949. -
      950. 2061 parent.__associations[this.name] = this._fetchMethod
      951. -
      952. == "all" ? !isArray(model) ? [model] : model : isArray(model) ? model[0] : model;
      953. -
      954. 2061 return parent.__associations[this.name];
      955. -
      956. },
      957. -
      958. fetch:function (parent) {
      959. -
      960. 785 var ret = new Promise();
      961. -
      962. 785 if (this._checkAssociationKey(parent)) {
      963. -
      964. 733 this._filter(parent)[this._fetchMethod]().then(hitch(this, function (result) {
      965. -
      966. 733 this.__setValue(parent, result);
      967. -
      968. 733 ret.callback(result);
      969. -
      970. 733 parent = null;
      971. -
      972. }), ret);
      973. -
      974. } else {
      975. -
      976. 52 this.__setValue(parent, null);
      977. -
      978. 52 ret.callback(null);
      979. +
      980. /**
      981. +
      982. * Returns a new clone of the dataset with with the given options merged into the current datasets options.
      983. +
      984. * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
      985. +
      986. * columns are deleted. This method should generally not be called
      987. +
      988. * directly by user code.
      989. +
      990. *
      991. +
      992. * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
      993. +
      994. * @return [patio.Dataset] a cloned dataset with the merged options
      995. +
      996. **/
      997. +
      998. mergeOptions:function (opts) {
      999. +
      1000. 14948 opts = isUndefined(opts) ? {} : opts;
      1001. +
      1002. 14948 var ds = new this._static(this.db, {});
      1003. +
      1004. 14948 ds.rowCb = this.rowCb;
      1005. +
      1006. 14948 this._static.FEATURES.forEach(function (f) {
      1007. +
      1008. 209272 ds[f] = this[f];
      1009. +
      1010. }, this);
      1011. +
      1012. 14948 ds.__opts = merge({}, this.__opts, opts);
      1013. +
      1014. 14948 ds.identifierInputMethod = this.identifierInputMethod;
      1015. +
      1016. 14948 ds.identifierOutputMethod = this.identifierOutputMethod;
      1017. +
      1018. 14948 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
      1019. +
      1020. 14948 if (Object.keys(opts).some(function (o) {
      1021. +
      1022. 13536 return columnChangeOpts.indexOf(o) != -1;
      1023. +
      1024. })) {
      1025. +
      1026. 2456 ds.__opts.columns = null;
      1027. }
      1028. -
      1029. 785 return ret;
      1030. +
      1031. 14948 return ds;
      1032. },
      1033. +
      1034. /**
      1035. -
      1036. * Middleware called before a model is removed.
      1037. -
      1038. * </br>
      1039. -
      1040. * <b> This is called in the scope of the model</b>
      1041. -
      1042. * @param {Function} next function to pass control up the middleware stack.
      1043. -
      1044. * @param {_Association} self reference to the Association that is being acted up.
      1045. +
      1046. * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      1047. +
      1048. * or {@link patio.sql.AliasedExpression}, depending on the format:
      1049. +
      1050. *
      1051. +
      1052. * <ul>
      1053. +
      1054. * <li>For columns : table__column___alias.</li>
      1055. +
      1056. * <li>For tables : schema__table___alias.</li>
      1057. +
      1058. * </ul>
      1059. +
      1060. * each portion of the identifier is optional. See example below
      1061. +
      1062. *
      1063. +
      1064. * @example
      1065. +
      1066. *
      1067. +
      1068. * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
      1069. +
      1070. * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
      1071. +
      1072. * ds.stringToIdentifier("table__column___alias");
      1073. +
      1074. * //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
      1075. +
      1076. *
      1077. +
      1078. * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
      1079. +
      1080. * or {@link patio.sql.AliasedExpression}.
      1081. +
      1082. *
      1083. +
      1084. * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
      1085. */
      1086. -
      1087. _preRemove:function (next, model) {
      1088. -
      1089. 223 if (this.isOwner && !this.isCascading) {
      1090. -
      1091. 46 var q = {};
      1092. -
      1093. 46 this._setAssociationKeys(model, q, null);
      1094. -
      1095. 46 model[this.associatedDatasetName].update(q).classic(next);
      1096. +
      1097. stringToIdentifier:function (name) {
      1098. +
      1099. 15093 if (isString(name)) {
      1100. +
      1101. 10138 var parts = this._splitString(name);
      1102. +
      1103. 10138 var schema = parts[0], table = parts[1], alias = parts[2];
      1104. +
      1105. 10138 return (schema && table && alias
      1106. +
      1107. ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
      1108. +
      1109. : (schema && table
      1110. +
      1111. ? new QualifiedIdentifier(schema, table)
      1112. +
      1113. : (table && alias
      1114. +
      1115. ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
      1116. } else {
      1117. -
      1118. 177 next();
      1119. +
      1120. 4955 return name;
      1121. }
      1122. },
      1123. /**
      1124. -
      1125. * Middleware called aft era model is removed.
      1126. -
      1127. * </br>
      1128. -
      1129. * <b> This is called in the scope of the model</b>
      1130. -
      1131. * @param {Function} next function to pass control up the middleware stack.
      1132. -
      1133. * @param {_Association} self reference to the Association that is being called.
      1134. +
      1135. * Can either be a string or null.
      1136. +
      1137. *
      1138. +
      1139. *
      1140. +
      1141. * @example
      1142. +
      1143. * //columns
      1144. +
      1145. * table__column___alias //=> table.column as alias
      1146. +
      1147. * table__column //=> table.column
      1148. +
      1149. * //tables
      1150. +
      1151. * schema__table___alias //=> schema.table as alias
      1152. +
      1153. * schema__table //=> schema.table
      1154. +
      1155. *
      1156. +
      1157. * //name and alias
      1158. +
      1159. * columnOrTable___alias //=> columnOrTable as alias
      1160. +
      1161. *
      1162. +
      1163. *
      1164. +
      1165. *
      1166. +
      1167. * @return {String[]} an array with the elements being:
      1168. +
      1169. * <ul>
      1170. +
      1171. * <li>For columns :[table, column, alias].</li>
      1172. +
      1173. * <li>For tables : [schema, table, alias].</li>
      1174. +
      1175. * </ul>
      1176. */
      1177. -
      1178. _postRemove:function (next, model) {
      1179. -
      1180. 514 next();
      1181. +
      1182. _splitString:function (s) {
      1183. +
      1184. 19662 var ret, m;
      1185. +
      1186. 19662 if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
      1187. +
      1188. 189 ret = m.slice(1);
      1189. +
      1190. }
      1191. +
      1192. 19473 else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
      1193. +
      1194. 28 ret = [null, m[1], m[2]];
      1195. +
      1196. }
      1197. +
      1198. 19445 else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
      1199. +
      1200. 2079 ret = [m[1], m[2], null];
      1201. +
      1202. }
      1203. +
      1204. else {
      1205. +
      1206. 17366 ret = [null, s, null];
      1207. +
      1208. }
      1209. +
      1210. 19662 return ret;
      1211. },
      1212. /**
      1213. -
      1214. * Middleware called before a model is saved.
      1215. -
      1216. * </br>
      1217. -
      1218. * <b> This is called in the scope of the model</b>
      1219. -
      1220. * @param {Function} next function to pass control up the middleware stack.
      1221. -
      1222. * @param {_Association} self reference to the Association that is being called.
      1223. -
      1224. */
      1225. -
      1226. _preSave:function (next, model) {
      1227. -
      1228. 324 next();
      1229. -
      1230. },
      1231. +
      1232. * @ignore
      1233. +
      1234. **/
      1235. +
      1236. getters:{
      1237. -
      1238. /**
      1239. -
      1240. * Middleware called after a model is saved.
      1241. -
      1242. * </br>
      1243. -
      1244. * <b> This is called in the scope of the model</b>
      1245. -
      1246. * @param {Function} next function to pass control up the middleware stack.
      1247. -
      1248. * @param {_Association} self reference to the Association that is being called.
      1249. -
      1250. */
      1251. -
      1252. _postSave:function (next, model) {
      1253. -
      1254. 198 next();
      1255. -
      1256. },
      1257. +
      1258. rowCb:function () {
      1259. +
      1260. 23052 return this.__rowCb;
      1261. +
      1262. },
      1263. -
      1264. /**
      1265. -
      1266. * Middleware called before a model is updated.
      1267. -
      1268. * </br>
      1269. -
      1270. * <b> This is called in the scope of the model</b>
      1271. -
      1272. * @param {Function} next function to pass control up the middleware stack.
      1273. -
      1274. * @param {_Association} self reference to the Association that is being called.
      1275. -
      1276. */
      1277. -
      1278. _preUpdate:function (next, model) {
      1279. -
      1280. 2 next();
      1281. -
      1282. },
      1283. +
      1284. identifierInputMethod:function () {
      1285. +
      1286. 14948 return this.__identifierInputMethod;
      1287. +
      1288. },
      1289. -
      1290. /**
      1291. -
      1292. * Middleware called before a model is updated.
      1293. -
      1294. * </br>
      1295. -
      1296. * <b> This is called in the scope of the model</b>
      1297. -
      1298. * @param {Function} next function to pass control up the middleware stack.
      1299. -
      1300. * @param {_Association} self reference to the Association that is being called.
      1301. -
      1302. */
      1303. -
      1304. _postUpdate:function (next, model) {
      1305. -
      1306. 127 next();
      1307. -
      1308. },
      1309. +
      1310. identifierOutputMethod:function () {
      1311. +
      1312. 14948 return this.__identifierOutputMethod;
      1313. +
      1314. },
      1315. -
      1316. /**
      1317. -
      1318. * Middleware called before a model is loaded.
      1319. -
      1320. * </br>
      1321. -
      1322. * <b> This is called in the scope of the model</b>
      1323. -
      1324. * @param {Function} next function to pass control up the middleware stack.
      1325. -
      1326. * @param {_Association} self reference to the Association that is being called.
      1327. -
      1328. */
      1329. -
      1330. _preLoad:function (next, model) {
      1331. -
      1332. 1570 next();
      1333. -
      1334. },
      1335. +
      1336. firstSourceAlias:function () {
      1337. +
      1338. 579 var source = this.__opts.from;
      1339. +
      1340. 579 if (isUndefinedOrNull(source) || !source.length) {
      1341. +
      1342. 2 throw new DatasetError("No source specified for the query");
      1343. +
      1344. }
      1345. +
      1346. 577 source = source[0];
      1347. +
      1348. 577 if (isInstanceOf(source, AliasedExpression)) {
      1349. +
      1350. 20 return source.alias;
      1351. +
      1352. 557 } else if (isString(source)) {
      1353. +
      1354. 0 var parts = this._splitString(source);
      1355. +
      1356. 0 var alias = parts[2];
      1357. +
      1358. 0 return alias ? alias : source;
      1359. +
      1360. } else {
      1361. +
      1362. 557 return source;
      1363. +
      1364. }
      1365. +
      1366. },
      1367. -
      1368. /**
      1369. -
      1370. * Middleware called after a model is loaded.
      1371. -
      1372. * </br>
      1373. -
      1374. * <b> This is called in the scope of the model</b>
      1375. -
      1376. * @param {Function} next function to pass control up the middleware stack.
      1377. -
      1378. * @param {_Association} self reference to the Association that is being called.
      1379. -
      1380. */
      1381. -
      1382. _postLoad:function (next, model) {
      1383. -
      1384. 0 next();
      1385. -
      1386. },
      1387. +
      1388. firstSourceTable:function () {
      1389. +
      1390. 15 var source = this.__opts.from;
      1391. +
      1392. 15 if (isUndefinedOrNull(source) || !source.length) {
      1393. +
      1394. 1 throw new QueryError("No source specified for the query");
      1395. +
      1396. }
      1397. +
      1398. 14 var source = source[0];
      1399. +
      1400. 14 if (isInstanceOf(source, AliasedExpression)) {
      1401. +
      1402. 3 return source.expression;
      1403. +
      1404. 11 } else if (isString(source)) {
      1405. +
      1406. 0 var parts = this._splitString(source);
      1407. +
      1408. 0 return source;
      1409. +
      1410. } else {
      1411. +
      1412. 11 return source;
      1413. +
      1414. }
      1415. +
      1416. },
      1417. -
      1418. /**
      1419. -
      1420. * Alias used to explicitly set an association on a model.
      1421. -
      1422. * @param {*} val the value to set the association to
      1423. -
      1424. * @param {_Association} self reference to the Association that is being called.
      1425. -
      1426. */
      1427. -
      1428. _setter:function (val, model) {
      1429. -
      1430. 0 model.__associations[this.name] = val;
      1431. -
      1432. },
      1433. +
      1434. sourceList:function () {
      1435. +
      1436. 0 return (this.__opts.from || []).map(this.stringToIdentifier, this);
      1437. +
      1438. },
      1439. -
      1440. associationLoaded:function (model) {
      1441. -
      1442. 2707 return model.__associations.hasOwnProperty(this.name);
      1443. -
      1444. },
      1445. +
      1446. joinSourceList:function () {
      1447. +
      1448. 0 return (this.__opts.join || []).map(function (join) {
      1449. +
      1450. 0 return this.stringToIdentifier(join.tableAlias || join.table);
      1451. +
      1452. }, this);
      1453. +
      1454. },
      1455. -
      1456. getAssociation:function (model) {
      1457. -
      1458. 885 return model.__associations[this.name];
      1459. +
      1460. hasSelectSource:function () {
      1461. +
      1462. 0 var select = this.__opts.select;
      1463. +
      1464. 0 return !(isUndefinedOrNull(select) || select.length === 0);
      1465. +
      1466. }
      1467. },
      1468. /**
      1469. -
      1470. * Alias used to explicitly get an association on a model.
      1471. -
      1472. * @param {_Association} self reference to the Association that is being called.
      1473. -
      1474. */
      1475. -
      1476. _getter:function (model) {
      1477. -
      1478. //if we have them return them;
      1479. -
      1480. 612 if (this.associationLoaded(model)) {
      1481. -
      1482. 220 var assoc = this.getAssociation(model);
      1483. -
      1484. 220 return this.isEager() ? assoc : new Promise().callback(assoc).promise();
      1485. -
      1486. 392 } else if (model.isNew) {
      1487. -
      1488. 0 return null;
      1489. -
      1490. } else {
      1491. -
      1492. 392 return this.fetch(model);
      1493. -
      1494. }
      1495. -
      1496. },
      1497. +
      1498. * @ignore
      1499. +
      1500. **/
      1501. +
      1502. setters:{
      1503. +
      1504. /**@lends patio.Dataset.prototype*/
      1505. -
      1506. _toModel:function (val, fromDb) {
      1507. -
      1508. 1412 var Model = this.model;
      1509. -
      1510. 1412 if (!isUndefinedOrNull(Model)) {
      1511. -
      1512. 1412 if (!isInstanceOf(val, Model)) {
      1513. -
      1514. 1167 val = new this.model(val, fromDb);
      1515. -
      1516. }
      1517. -
      1518. } else {
      1519. -
      1520. 0 throw new PatioError("Invalid model " + this.name);
      1521. -
      1522. }
      1523. -
      1524. 1412 return val;
      1525. -
      1526. },
      1527. +
      1528. identifierInputMethod:function (meth) {
      1529. +
      1530. 15038 this.__identifierInputMethod = meth;
      1531. +
      1532. },
      1533. -
      1534. /**
      1535. -
      1536. * Method to inject functionality into a model. This method alters the model
      1537. -
      1538. * to prepare it for associations, and initializes all required middleware calls
      1539. -
      1540. * to fulfill requirements needed to loaded the associations.
      1541. -
      1542. *
      1543. -
      1544. * @param {Model} parent the model that is having an associtaion set on it.
      1545. -
      1546. * @param {String} name the name of the association.
      1547. -
      1548. */
      1549. -
      1550. inject:function (parent, name) {
      1551. -
      1552. 55 this.name = name;
      1553. -
      1554. 55 var self = this;
      1555. -
      1556. 55 this.parent = parent;
      1557. -
      1558. 55 parent.prototype.__defineGetter__(name, function () {
      1559. -
      1560. 612 return self._getter(this);
      1561. -
      1562. });
      1563. -
      1564. 55 parent.prototype.__defineGetter__(this.associatedDatasetName, function () {
      1565. -
      1566. 146 return self._filter(this);
      1567. -
      1568. });
      1569. +
      1570. identifierOutputMethod:function (meth) {
      1571. +
      1572. 15038 this.__identifierOutputMethod = meth;
      1573. +
      1574. },
      1575. -
      1576. 55 if (!this.readOnly && this.createSetter) {
      1577. -
      1578. //define a setter because we arent read only
      1579. -
      1580. 55 parent.prototype.__defineSetter__(name, function (vals) {
      1581. -
      1582. 139 self._setter(vals, this);
      1583. -
      1584. });
      1585. +
      1586. rowCb:function (cb) {
      1587. +
      1588. 18564 if (isFunction(cb) || isNull(cb)) {
      1589. +
      1590. 18559 this.__rowCb = cb;
      1591. +
      1592. } else {
      1593. +
      1594. 5 throw new DatasetError("rowCb mus be a function");
      1595. +
      1596. }
      1597. }
      1598. -
      1599. -
      1600. //set up all callbacks
      1601. -
      1602. 55 ["pre", "post"].forEach(function (op) {
      1603. -
      1604. 110 ["save", "update", "remove", "load"].forEach(function (type) {
      1605. -
      1606. 440 parent[op](type, function (next) {
      1607. -
      1608. 5598 return self["_" + op + type.charAt(0).toUpperCase() + type.slice(1)](next, this);
      1609. -
      1610. });
      1611. -
      1612. }, this);
      1613. -
      1614. }, this);
      1615. -
      1616. },
      1617. -
      1618. -
      1619. getters:{
      1620. -
      1621. -
      1622. select:function () {
      1623. -
      1624. 648 return this.__opts.select;
      1625. -
      1626. },
      1627. -
      1628. -
      1629. defaultLeftKey:function () {
      1630. -
      1631. 2841 var ret = "";
      1632. -
      1633. 2841 if (this.isOwner) {
      1634. -
      1635. 2095 ret = this.__opts.primaryKey || this.parent.primaryKey[0]
      1636. -
      1637. } else {
      1638. -
      1639. 746 ret = this.model.tableName + "Id";
      1640. -
      1641. }
      1642. -
      1643. 2841 return ret;
      1644. -
      1645. },
      1646. -
      1647. -
      1648. defaultRightKey:function () {
      1649. -
      1650. 2721 return this.associatedModelKey;
      1651. -
      1652. },
      1653. -
      1654. -
      1655. //Returns our model
      1656. -
      1657. model:function () {
      1658. -
      1659. 10572 return this.patio.getModel(this._model, this.parent.db);
      1660. -
      1661. },
      1662. -
      1663. -
      1664. associatedModelKey:function () {
      1665. -
      1666. 2721 var ret = "";
      1667. -
      1668. 2721 if (this.isOwner) {
      1669. -
      1670. 1857 ret = this.__opts.primaryKey || this.parent.tableName + "Id";
      1671. -
      1672. } else {
      1673. -
      1674. 864 ret = this.model.primaryKey[0];
      1675. -
      1676. }
      1677. -
      1678. 2721 return ret;
      1679. -
      1680. },
      1681. -
      1682. -
      1683. associatedDatasetName:function () {
      1684. -
      1685. 201 return this.name + "Dataset"
      1686. -
      1687. },
      1688. -
      1689. -
      1690. removeAssociationFlagName:function () {
      1691. -
      1692. 27 return "__remove" + this.name + "association"
      1693. -
      1694. }
      1695. -
      1696. -
      1697. }
      1698. -
      1699. },
      1700. +
      1701. }
      1702. +
      1703. },
      1704. static:{
      1705. -
      1706. /**@lends patio.associations.Association*/
      1707. -
      1708. -
      1709. fetch:{
      1710. -
      1711. -
      1712. LAZY:"lazy",
      1713. -
      1714. -
      1715. EAGER:"eager"
      1716. -
      1717. }
      1718. +
      1719. COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
      1720. +
      1721. COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
      1722. +
      1723. COLUMN_REF_RE3:/^(\w+)__(\w+)$/
      1724. }
      1725. -
      1726. }).as(module);
      +
    5. }).as(module);
    6. +
    7. +
    -
    +
    -
    model.js
    +
    associations/_Association.js
    - Coverage88.51 - SLOC1067 - LOC322 - Missed37 + Coverage87.18 + SLOC515 + LOC156 + Missed20
    -
    1. 1var comb = require("comb"),
    2. -
    3. isFunction = comb.isFunction,
    4. +
      1. 1var comb = require("comb-proxy"),
      2. +
      3. define = comb.define,
      4. isUndefined = comb.isUndefined,
      5. -
      6. isDefined = comb.isDefined,
      7. +
      8. isUndefinedOrNull = comb.isUndefinedOrNull,
      9. isBoolean = comb.isBoolean,
      10. isString = comb.isString,
      11. -
      12. argsToArray = comb.argsToArray,
      13. -
      14. isInstanceOf = comb.isInstanceOf,
      15. -
      16. serial = comb.serial,
      17. isHash = comb.isHash,
      18. -
      19. when = comb.when,
      20. -
      21. merge = comb.merge,
      22. -
      23. toArray = comb.array.toArray,
      24. -
      25. ModelError = require("./errors").ModelError,
      26. -
      27. plugins = require("./plugins"),
      28. -
      29. isUndefinedOrNull = comb.isUndefinedOrNull,
      30. -
      31. AssociationPlugin = plugins.AssociationPlugin,
      32. -
      33. QueryPlugin = plugins.QueryPlugin,
      34. +
      35. isFunction = comb.isFunction,
      36. +
      37. isInstanceOf = comb.isInstanceOf,
      38. Promise = comb.Promise,
      39. PromiseList = comb.PromiseList,
      40. -
      41. HashTable = comb.collections.HashTable,
      42. hitch = comb.hitch,
      43. -
      44. hitchIgnore = comb.hitchIgnore,
      45. +
      46. array = comb.array,
      47. +
      48. isArray = comb.isArray,
      49. Middleware = comb.plugins.Middleware,
      50. -
      51. EventEmitter = require("events").EventEmitter,
      52. -
      53. util = require("util"),
      54. -
      55. define = comb.define,
      56. -
      57. patio;
      58. -
      59. -
      60. -
      61. 1var MODELS = new HashTable();
      62. +
      63. PatioError = require("../errors").PatioError;
      64. -
      65. 1var applyColumnTransformMethod = function (val, meth) {
      66. -
      67. 11464 return !isUndefinedOrNull(meth) ? isFunction(val[meth]) ? val[meth] : isFunction(comb[meth]) ? comb[meth](val) : val : val;
      68. +
      69. 1var fetch = {
      70. +
      71. LAZY:"lazy",
      72. +
      73. EAGER:"eager"
      74. };
      75. -
      76. 1var checkAndTransformName = function (name) {
      77. -
      78. 10693 return isString(name) ? applyColumnTransformMethod(name, Model.camelize === true ? "camelize" : Model.underscore === true ? "underscore" : null) : name;
      79. -
      80. };
      81. -
      82. 1var Model = define([QueryPlugin, Middleware], {
      83. +
      84. /**
      85. +
      86. * @class
      87. +
      88. * Base class for all associations.
      89. +
      90. *
      91. +
      92. * </br>
      93. +
      94. * <b>NOT to be instantiated directly</b>
      95. +
      96. * Its just documented for reference.
      97. +
      98. *
      99. +
      100. * @constructs
      101. +
      102. * @param {Object} options
      103. +
      104. * @param {String} options.model a string to look up the model that we are associated with
      105. +
      106. * @param {Function} options.filter a callback to find association if a filter is defined then
      107. +
      108. * the association is read only
      109. +
      110. * @param {Object} options.key object with left key and right key
      111. +
      112. * @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order
      113. +
      114. * @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then
      115. +
      116. * the associations are automatically filled, if fetch.Lazy is supplied
      117. +
      118. * then a promise is returned and is called back with the loaded models
      119. +
      120. * @property {Model} model the model associatied with this association.
      121. +
      122. * @name Association
      123. +
      124. * @memberOf patio.associations
      125. +
      126. * */
      127. +
      128. 1define(Middleware, {
      129. instance:{
      130. +
      131. /**@lends patio.associations.Association.prototype*/
      132. +
      133. +
      134. type:"",
      135. +
      136. +
      137. //Our associated model
      138. +
      139. _model:null,
      140. +
      141. /**
      142. -
      143. * @lends patio.Model.prototype
      144. +
      145. * Fetch type
      146. */
      147. +
      148. fetchType:fetch.LAZY,
      149. -
      150. __ignore:false,
      151. +
      152. /**how to order our association*/
      153. +
      154. orderBy:null,
      155. -
      156. __changed:null,
      157. +
      158. /**Our filter method*/
      159. +
      160. filter:null,
      161. -
      162. __values:null,
      163. +
      164. __hooks:null,
      165. -
      166. /**
      167. -
      168. * patio - read only
      169. -
      170. *
      171. -
      172. * @type patio
      173. -
      174. */
      175. -
      176. patio:null,
      177. +
      178. isOwner:true,
      179. +
      180. +
      181. createSetter:true,
      182. +
      183. +
      184. isCascading:false,
      185. +
      186. +
      187. supportsStringKey:true,
      188. +
      189. +
      190. supportsHashKey:true,
      191. +
      192. +
      193. supportsCompositeKey:true,
      194. +
      195. +
      196. supportsLeftAndRightKey:true,
      197. /**
      198. -
      199. * The database type such as mysql
      200. -
      201. *
      202. -
      203. * @type String
      204. *
      205. -
      206. * */
      207. -
      208. type:null,
      209. +
      210. *Method to call to look up association,
      211. +
      212. *called after the model has been filtered
      213. +
      214. **/
      215. +
      216. _fetchMethod:"all",
      217. -
      218. /**
      219. -
      220. * Whether or not this model is new
      221. -
      222. * */
      223. -
      224. __isNew:true,
      225. -
      226. /**
      227. -
      228. * Signifies if the model has changed
      229. -
      230. * */
      231. -
      232. __isChanged:false,
      233. +
      234. constructor:function (options, patio, filter) {
      235. +
      236. 55 options = options || {};
      237. +
      238. 55 if (!options.model) {
      239. +
      240. 0 throw new Error("Model is required for " + this.type + " association");
      241. +
      242. }
      243. +
      244. 55 this._model = options.model;
      245. +
      246. 55 this.patio = patio;
      247. +
      248. 55 this.__opts = options;
      249. +
      250. 55 !isUndefined(options.isCascading) && (this.isCascading = options.isCascading);
      251. +
      252. 55 this.filter = filter;
      253. +
      254. 55 this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;
      255. +
      256. 55 this.__hooks =
      257. +
      258. {before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};
      259. +
      260. 55 var hooks = ["Add", "Remove", "Set", "Load"];
      261. +
      262. 55 ["before", "after"].forEach(function (h) {
      263. +
      264. 110 hooks.forEach(function (a) {
      265. +
      266. 440 var hookName = h + a, hook;
      267. +
      268. 440 if (isFunction((hook = options[hookName]))) {
      269. +
      270. 0 this.__hooks[h][a.toLowerCase()] = hook;
      271. +
      272. }
      273. +
      274. }, this);
      275. +
      276. }, this);
      277. +
      278. 55 this.fetchType = options.fetchType || fetch.LAZY;
      279. +
      280. },
      281. +
      282. +
      283. _callHook:function (hook, action, args) {
      284. +
      285. 0 var func = this.__hooks[hook][action], ret;
      286. +
      287. 0 if (isFunction(func)) {
      288. +
      289. 0 ret = func.apply(this, args);
      290. +
      291. }
      292. +
      293. 0 return ret;
      294. +
      295. },
      296. +
      297. +
      298. _clearAssociations:function (model) {
      299. +
      300. 125 if (!this.readOnly) {
      301. +
      302. 125 delete model.__associations[this.name];
      303. +
      304. }
      305. +
      306. },
      307. +
      308. +
      309. _forceReloadAssociations:function (model) {
      310. +
      311. 243 if (!this.readOnly) {
      312. +
      313. 243 delete model.__associations[this.name];
      314. +
      315. 243 return model[this.name];
      316. +
      317. }
      318. +
      319. },
      320. /**
      321. -
      322. * Base class for all models.
      323. -
      324. * <p>This is used through {@link patio.addModel}, <b>NOT directly.</b></p>
      325. -
      326. *
      327. -
      328. * @constructs
      329. -
      330. * @augments comb.plugins.Middleware
      331. -
      332. *
      333. -
      334. * @param {Object} columnValues values of each column to be used by this Model.
      335. -
      336. *
      337. -
      338. * @property {patio.Dataset} dataset a dataset to use to retrieve models from the database. The dataset
      339. -
      340. * has the {@link patio.Dataset#rowCb} set to create instances of this model.
      341. -
      342. * @property {String[]} columns a list of columns this models table contains.
      343. -
      344. * @property {Object} schema the schema of this models table.
      345. -
      346. * @property {String} tableName the table name of this models table.
      347. -
      348. * @property {*} primaryKeyValue the value of this models primaryKey
      349. -
      350. * @property {Boolean} isNew true if this model is new and does not exist in the database.
      351. -
      352. * @property {Boolean} isChanged true if the model has been changed and not saved.
      353. -
      354. *
      355. -
      356. * @borrows patio.Dataset#all as all
      357. -
      358. * @borrows patio.Dataset#one as one
      359. -
      360. * @borrows patio.Dataset#avg as avg
      361. -
      362. * @borrows patio.Dataset#count as count
      363. -
      364. * @borrows patio.Dataset#columns as columns
      365. -
      366. * @borrows patio.Dataset#forEach as forEach
      367. -
      368. * @borrows patio.Dataset#isEmpty as empty
      369. -
      370. * @borrows patio.Dataset#first as first
      371. -
      372. * @borrows patio.Dataset#get as get
      373. -
      374. * @borrows patio.Dataset#import as import
      375. -
      376. * @borrows patio.Dataset#insert as insert
      377. -
      378. * @borrows patio.Dataset#insertMultiple as insertMultiple
      379. -
      380. * @borrows patio.Dataset#saveMultiple as saveMultiple
      381. -
      382. * @borrows patio.Dataset#interval as interval
      383. -
      384. * @borrows patio.Dataset#last as last
      385. -
      386. * @borrows patio.Dataset#map as map
      387. -
      388. * @borrows patio.Dataset#max as max
      389. -
      390. * @borrows patio.Dataset#min as min
      391. -
      392. * @borrows patio.Dataset#multiInsert as multiInsert
      393. -
      394. * @borrows patio.Dataset#range as range
      395. -
      396. * @borrows patio.Dataset#selectHash as selectHash
      397. -
      398. * @borrows patio.Dataset#selectMap as selectMap
      399. -
      400. * @borrows patio.Dataset#selectOrderMap as selectOrderMap
      401. -
      402. * @borrows patio.Dataset#set as set
      403. -
      404. * @borrows patio.Dataset#singleRecord as singleRecord
      405. -
      406. * @borrows patio.Dataset#singleValue as singleValue
      407. -
      408. * @borrows patio.Dataset#sum as sum
      409. -
      410. * @borrows patio.Dataset#toCsv as toCsv
      411. -
      412. * @borrows patio.Dataset#toHash as toHash
      413. -
      414. * @borrows patio.Dataset#truncate as truncate
      415. -
      416. * @borrows patio.Dataset#addGraphAliases as addGraphAliases
      417. -
      418. * @borrows patio.Dataset#and as and
      419. -
      420. * @borrows patio.Dataset#distinct as distinct
      421. -
      422. * @borrows patio.Dataset#except as except
      423. -
      424. * @borrows patio.Dataset#exclude as exclude
      425. -
      426. * @borrows patio.Dataset#is as is
      427. -
      428. * @borrows patio.Dataset#isNot as isNot
      429. -
      430. * @borrows patio.Dataset#eq as eq
      431. -
      432. * @borrows patio.Dataset#neq as neq
      433. -
      434. * @borrows patio.Dataset#lt as lt
      435. -
      436. * @borrows patio.Dataset#lte as lte
      437. -
      438. * @borrows patio.Dataset#gt as gt
      439. -
      440. * @borrows patio.Dataset#gte as gte
      441. -
      442. * @borrows patio.Dataset#forUpdate as forUpdate
      443. -
      444. * @borrows patio.Dataset#from as from
      445. -
      446. * @borrows patio.Dataset#fromSelf as fromSelf
      447. -
      448. * @borrows patio.Dataset#graph as graph
      449. -
      450. * @borrows patio.Dataset#grep as grep
      451. -
      452. * @borrows patio.Dataset#group as group
      453. -
      454. * @borrows patio.Dataset#groupAndCount as groupAndCount
      455. -
      456. * @borrows patio.Dataset#groupBy as groupBy
      457. -
      458. * @borrows patio.Dataset#having as having
      459. -
      460. * @borrows patio.Dataset#intersect as intersect
      461. -
      462. * @borrows patio.Dataset#invert as invert
      463. -
      464. * @borrows patio.Dataset#limit as limit
      465. -
      466. * @borrows patio.Dataset#lockStyle as lockStyle
      467. -
      468. * @borrows patio.Dataset#naked as naked
      469. -
      470. * @borrows patio.Dataset#or as or
      471. -
      472. * @borrows patio.Dataset#order as order
      473. -
      474. * @borrows patio.Dataset#orderAppend as orderAppend
      475. -
      476. * @borrows patio.Dataset#orderBy as orderBy
      477. -
      478. * @borrows patio.Dataset#orderMore as orderMore
      479. -
      480. * @borrows patio.Dataset#orderPrepend as orderPrepend
      481. -
      482. * @borrows patio.Dataset#qualify as qualify
      483. -
      484. * @borrows patio.Dataset#reverse as reverse
      485. -
      486. * @borrows patio.Dataset#reverseOrder as reverseOrder
      487. -
      488. * @borrows patio.Dataset#select as select
      489. -
      490. * @borrows patio.Dataset#selectAll as selectAll
      491. -
      492. * @borrows patio.Dataset#selectAppend as selectAppend
      493. -
      494. * @borrows patio.Dataset#selectMore as selectMore
      495. -
      496. * @borrows patio.Dataset#setDefaults as setDefaults
      497. -
      498. * @borrows patio.Dataset#setGraphAliases as setGraphAliases
      499. -
      500. * @borrows patio.Dataset#setOverrides as setOverrides
      501. -
      502. * @borrows patio.Dataset#unfiltered as unfiltered
      503. -
      504. * @borrows patio.Dataset#ungraphed as ungraphed
      505. -
      506. * @borrows patio.Dataset#ungrouped as ungrouped
      507. -
      508. * @borrows patio.Dataset#union as union
      509. -
      510. * @borrows patio.Dataset#unlimited as unlimited
      511. -
      512. * @borrows patio.Dataset#unordered as unordered
      513. -
      514. * @borrows patio.Dataset#where as where
      515. -
      516. * @borrows patio.Dataset#with as with
      517. -
      518. * @borrows patio.Dataset#withRecursive as withRecursive
      519. -
      520. * @borrows patio.Dataset#withSql as withSql
      521. -
      522. * @borrows patio.Dataset#naturalJoin as naturalJoin
      523. -
      524. * @borrows patio.Dataset#naturalLeftJoin as naturalLeftJoin
      525. -
      526. * @borrows patio.Dataset#naturalRightJoin as naturalRightJoin
      527. -
      528. * @borrows patio.Dataset#naturalFullJoin as naturalFullJoin
      529. -
      530. * @borrows patio.Dataset#crossJoin as crossJoin
      531. -
      532. * @borrows patio.Dataset#innerJoin as innerJoin
      533. -
      534. * @borrows patio.Dataset#fullOuterJoin as fullOuterJoin
      535. -
      536. * @borrows patio.Dataset#rightOuterJoin as rightOuterJoin
      537. -
      538. * @borrows patio.Dataset#leftOuterJoin as leftOuterJoin
      539. -
      540. * @borrows patio.Dataset#fullJoin as fullJoin
      541. -
      542. * @borrows patio.Dataset#rightJoin as rightJoin
      543. -
      544. * @borrows patio.Dataset#leftJoin as leftJoin
      545. -
      546. * */
      547. -
      548. constructor:function (options, fromDb) {
      549. -
      550. 3111 if (this.synced) {
      551. -
      552. 3111 this.__emitter = new EventEmitter();
      553. -
      554. 3111 this._super(arguments);
      555. -
      556. 3111 this.patio = patio || require("./index");
      557. -
      558. 3111 fromDb = isBoolean(fromDb) ? fromDb : false;
      559. -
      560. 3111 this.__changed = {};
      561. -
      562. 3111 this.__values = {};
      563. -
      564. 3111 if (fromDb) {
      565. -
      566. 1754 this._hook("pre", "load");
      567. -
      568. 1754 this.__isNew = false;
      569. -
      570. 1754 this.__setFromDb(options, true);
      571. -
      572. 1754 if (this._static.emitOnLoad) {
      573. -
      574. 1754 this.emit("load", this);
      575. -
      576. 1754 this._static.emit("load", this);
      577. -
      578. }
      579. -
      580. } else {
      581. -
      582. 1357 this.__isNew = true;
      583. -
      584. 1357 this.__set(options);
      585. -
      586. }
      587. -
      588. } else {
      589. -
      590. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
      591. -
      592. }
      593. +
      594. * @return {Boolean} true if the association is eager.
      595. +
      596. */
      597. +
      598. isEager:function () {
      599. +
      600. 2114 return this.fetchType == fetch.EAGER;
      601. },
      602. -
      603. __set:function (values, ignore) {
      604. -
      605. 1479 values = values || {};
      606. -
      607. 1479 this.__ignore = ignore === true;
      608. -
      609. 1479 Object.keys(values).forEach(function (attribute) {
      610. -
      611. 6632 var value = values[attribute];
      612. -
      613. //check if the column is a constrained value and is allowed to be set
      614. -
      615. 6632 !ignore && this._checkIfColumnIsConstrained(attribute);
      616. -
      617. 6632 this[attribute] = value;
      618. -
      619. }, this);
      620. -
      621. 1479 this.__ignore = false;
      622. +
      623. _checkAssociationKey:function (parent) {
      624. +
      625. 785 var q = {};
      626. +
      627. 785 this._setAssociationKeys(parent, q);
      628. +
      629. 785 return Object.keys(q).every(function (k) {
      630. +
      631. 785 return q[k] != null
      632. +
      633. });
      634. },
      635. -
      636. __setFromDb:function (values, ignore) {
      637. -
      638. 3057 values = values || {};
      639. -
      640. 3057 this.__ignore = ignore === true;
      641. -
      642. 3057 var schema = this.schema;
      643. -
      644. 3057 Object.keys(values).forEach(function (column) {
      645. -
      646. 25150 var value = values[column];
      647. -
      648. // Typecast value retrieved from db
      649. -
      650. 25150 if (schema.hasOwnProperty(column)) {
      651. -
      652. 25149 this.__values[column] = this._typeCastValue(column, value, ignore);
      653. -
      654. } else {
      655. -
      656. 1 console.log(column);
      657. -
      658. 1 this[column] = value;
      659. +
      660. _getAssociationKey:function () {
      661. +
      662. 5205 var options = this.__opts, key, ret = [], lk, rk;
      663. +
      664. 5205 if (!isUndefinedOrNull((key = options.key))) {
      665. +
      666. 722 if (this.supportsStringKey && isString(key)) {
      667. +
      668. //normalize the key first!
      669. +
      670. 356 ret = [
      671. +
      672. [this.isOwner ? this.defaultLeftKey : key],
      673. +
      674. [this.isOwner ? key : this.defaultRightKey]
      675. +
      676. ];
      677. +
      678. 366 } else if (this.supportsHashKey && isHash(key)) {
      679. +
      680. 366 var leftKey = Object.keys(key)[0];
      681. +
      682. 366 var rightKey = key[leftKey];
      683. +
      684. 366 ret = [
      685. +
      686. [leftKey],
      687. +
      688. [rightKey]
      689. +
      690. ];
      691. +
      692. 0 } else if (this.supportsCompositeKey && isArray(key)) {
      693. +
      694. 0 ret = [
      695. +
      696. [key],
      697. +
      698. null
      699. +
      700. ];
      701. }
      702. -
      703. }, this);
      704. -
      705. 3057 this.__ignore = false;
      706. -
      707. +
      708. 4483 } else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey))
      709. +
      710. && !isUndefinedOrNull((rk = options.rightKey)))) {
      711. +
      712. 140 ret = [
      713. +
      714. array.toArray(lk), array.toArray(rk)
      715. +
      716. ];
      717. +
      718. } else {
      719. +
      720. //todo handle composite primary keys
      721. +
      722. 4343 ret = [
      723. +
      724. [this.defaultLeftKey],
      725. +
      726. [this.defaultRightKey]
      727. +
      728. ];
      729. +
      730. }
      731. +
      732. 5205 return ret;
      733. },
      734. -
      735. /**
      736. -
      737. * Set multiple values at once. Useful if you have a hash of properties that you want to set.
      738. -
      739. *
      740. -
      741. * <b>NOTE:</b> This method will use the static restrictedColumns property of the model.
      742. -
      743. *
      744. -
      745. * @example
      746. -
      747. *
      748. -
      749. * myModel.setValues({firstName : "Bob", lastName : "yukon"});
      750. -
      751. *
      752. -
      753. * //this will throw an error by default, assuming id is a pk.
      754. -
      755. * myModel.setValues({id : 1, firstName : "Bob", lastName : "yukon"});
      756. -
      757. *
      758. -
      759. * @param {Object} values value to set on the model.
      760. -
      761. *
      762. -
      763. * @return {patio.Model} return this for chaining.
      764. -
      765. */
      766. -
      767. setValues:function (values) {
      768. -
      769. 17 this.__set(values, false);
      770. -
      771. 17 return this;
      772. -
      773. },
      774. -
      775. _toObject:function () {
      776. -
      777. 1124 if (this.synced) {
      778. -
      779. 1124 var columns = this._static.columns, ret = {};
      780. -
      781. 1124 for (var i in columns) {
      782. -
      783. 10220 var col = columns[i];
      784. -
      785. 10220 var val = this.__values[col];
      786. -
      787. 10220 if (!isUndefined(val)) {
      788. -
      789. 6169 ret[col] = val;
      790. -
      791. }
      792. +
      793. _setAssociationKeys:function (parent, model, val) {
      794. +
      795. 1573 var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1];
      796. +
      797. 1573 if (leftKey && rightKey) {
      798. +
      799. 1573 for (var i = 0; i < leftKey.length; i++) {
      800. +
      801. 1573 model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]];
      802. }
      803. -
      804. 1124 return ret;
      805. } else {
      806. -
      807. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
      808. +
      809. 0 leftKey.forEach(function (k) {
      810. +
      811. 0 model[k] = !isUndefined(val) ? val : parent[k];
      812. +
      813. });
      814. }
      815. },
      816. -
      817. _addColumnToIsChanged:function (name, val) {
      818. -
      819. 7556 if (!this.isNew && !this.__ignore) {
      820. -
      821. 165 this.__isChanged = true;
      822. -
      823. 165 this.__changed[name] = val;
      824. +
      825. _setDatasetOptions:function (ds) {
      826. +
      827. 974 var options = this.__opts || {};
      828. +
      829. 974 var order, limit, distinct, select, query;
      830. +
      831. //allow for multi key ordering
      832. +
      833. 974 if (!isUndefined((select = this.select))) {
      834. +
      835. 0 ds = ds.select.apply(ds, array.toArray(select));
      836. }
      837. -
      838. },
      839. -
      840. -
      841. _checkIfColumnIsConstrained:function (name) {
      842. -
      843. 6632 if (this.synced && !this.__ignore) {
      844. -
      845. 6632 var col = this.schema[name], restrictedCols = this._static.restrictedColumns || [];
      846. -
      847. 6632 if (!isUndefined(col) && (col.primaryKey && this._static.isRestrictedPrimaryKey) || restrictedCols.indexOf(name) != -1) {
      848. -
      849. 0 throw new ModelError("Cannot set primary key of model " + this._static.tableName);
      850. +
      851. 974 if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {
      852. +
      853. 0 ds = ds.filter(query);
      854. +
      855. }
      856. +
      857. 974 if (isFunction(this.filter)) {
      858. +
      859. 334 var ret = this.filter.apply(this, [ds]);
      860. +
      861. 334 if (isInstanceOf(ret, ds._static)) {
      862. +
      863. 334 ds = ret;
      864. }
      865. }
      866. -
      867. },
      868. -
      869. -
      870. _getColumnValue:function (name) {
      871. -
      872. 5427 var val = this.__values[name];
      873. -
      874. 5427 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
      875. -
      876. 5427 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
      877. +
      878. 974 if (!isUndefined((distinct = options.distinct))) {
      879. +
      880. 0 ds = ds.limit.apply(ds, array.toArray(distinct));
      881. +
      882. }
      883. +
      884. 974 if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {
      885. +
      886. 0 ds = ds.order.apply(ds, array.toArray(order));
      887. +
      888. }
      889. +
      890. 974 if (!isUndefined((limit = options.limit))) {
      891. +
      892. 0 ds = ds.limit.apply(ds, array.toArray(limit));
      893. +
      894. }
      895. +
      896. 974 return ds;
      897. -
      898. 5427 return columnValue;
      899. },
      900. -
      901. _setColumnValue:function (name, val) {
      902. -
      903. 7556 var ignore = this.__ignore;
      904. -
      905. 7556 val = this._typeCastValue(name, val, ignore);
      906. -
      907. 7556 this._addColumnToIsChanged(name, val);
      908. -
      909. 7556 var setterFunc = this["_set" + name.charAt(0).toUpperCase() + name.substr(1)];
      910. -
      911. 7556 var columnValue = isFunction(setterFunc) ? setterFunc.call(this, val, ignore) : val;
      912. -
      913. 7556 this.__values[name] = columnValue;
      914. -
      915. 7556 if (this._static.emitOnColumnSet) {
      916. -
      917. 7556 this.emit("column", name, columnValue, ignore);
      918. +
      919. /**
      920. +
      921. *Filters our associated dataset to load our association.
      922. +
      923. *
      924. +
      925. *@return {Dataset} the dataset with all filters applied.
      926. +
      927. **/
      928. +
      929. _filter:function (parent) {
      930. +
      931. 648 var options = this.__opts || {};
      932. +
      933. 648 var ds;
      934. +
      935. 648 if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {
      936. +
      937. 0 ds = ds.apply(parent, [parent]);
      938. +
      939. }
      940. +
      941. 648 if (!ds) {
      942. +
      943. 648 var q = {};
      944. +
      945. 648 this._setAssociationKeys(parent, q);
      946. +
      947. 648 ds = this.model.dataset.naked().filter(q);
      948. +
      949. 648 var recip = this.model._findAssociation(this);
      950. +
      951. 648 recip && (recip = recip[1]);
      952. +
      953. 648 ds.rowCb = hitch(this, function (item) {
      954. +
      955. 489 var ret = new Promise();
      956. +
      957. 489 var model = this._toModel(item, true);
      958. +
      959. 489 recip && recip.__setValue(model, parent);
      960. +
      961. //call hook to finish other model associations
      962. +
      963. 489 model._hook("post", "load").then(hitch(this, function () {
      964. +
      965. 489 ret.callback(model);
      966. +
      967. }), ret);
      968. +
      969. 489 return ret.promise();
      970. +
      971. });
      972. }
      973. +
      974. +
      975. 648 return this._setDatasetOptions(ds);
      976. },
      977. +
      978. __setValue:function (parent, model) {
      979. +
      980. 2061 parent.__associations[this.name] = this._fetchMethod
      981. +
      982. == "all" ? !isArray(model) ? [model] : model : isArray(model) ? model[0] : model;
      983. +
      984. 2061 return parent.__associations[this.name];
      985. +
      986. },
      987. -
      988. //Typecast the value to the column's type if typecasting. Calls the database's
      989. -
      990. //typecast_value method, so database adapters can override/augment the handling
      991. -
      992. //for database specific column types.
      993. -
      994. _typeCastValue:function (column, value, fromDatabase) {
      995. -
      996. 32705 var colSchema, clazz = this._static;
      997. -
      998. 32705 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
      999. -
      1000. 32705 var type = colSchema.type;
      1001. -
      1002. 32705 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
      1003. -
      1004. 3 value = null;
      1005. -
      1006. }
      1007. -
      1008. 32705 var raiseOnError = clazz.raiseOnTypecastError;
      1009. -
      1010. 32705 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
      1011. -
      1012. 0 throw new ModelError("null is not allowed for the " + column + " column.");
      1013. -
      1014. }
      1015. -
      1016. 32705 try {
      1017. -
      1018. 32705 value = clazz.db.typecastValue(type, value);
      1019. -
      1020. } catch (e) {
      1021. -
      1022. 0 if (raiseOnError === true) {
      1023. -
      1024. 0 throw e;
      1025. -
      1026. }
      1027. -
      1028. }
      1029. +
      1030. fetch:function (parent) {
      1031. +
      1032. 785 var ret = new Promise();
      1033. +
      1034. 785 if (this._checkAssociationKey(parent)) {
      1035. +
      1036. 733 this._filter(parent)[this._fetchMethod]().then(hitch(this, function (result) {
      1037. +
      1038. 733 this.__setValue(parent, result);
      1039. +
      1040. 733 ret.callback(result);
      1041. +
      1042. 733 parent = null;
      1043. +
      1044. }), ret);
      1045. +
      1046. } else {
      1047. +
      1048. 52 this.__setValue(parent, null);
      1049. +
      1050. 52 ret.callback(null);
      1051. }
      1052. -
      1053. 32705 return value;
      1054. +
      1055. 785 return ret;
      1056. },
      1057. /**
      1058. -
      1059. * Convert this model to an object, containing column, value pairs.
      1060. -
      1061. *
      1062. -
      1063. * @return {Object} the object version of this model.
      1064. -
      1065. **/
      1066. -
      1067. toObject:function () {
      1068. -
      1069. 0 return this._toObject(false);
      1070. +
      1071. * Middleware called before a model is removed.
      1072. +
      1073. * </br>
      1074. +
      1075. * <b> This is called in the scope of the model</b>
      1076. +
      1077. * @param {Function} next function to pass control up the middleware stack.
      1078. +
      1079. * @param {_Association} self reference to the Association that is being acted up.
      1080. +
      1081. */
      1082. +
      1083. _preRemove:function (next, model) {
      1084. +
      1085. 223 if (this.isOwner && !this.isCascading) {
      1086. +
      1087. 46 var q = {};
      1088. +
      1089. 46 this._setAssociationKeys(model, q, null);
      1090. +
      1091. 46 model[this.associatedDatasetName].update(q).classic(next);
      1092. +
      1093. } else {
      1094. +
      1095. 177 next();
      1096. +
      1097. }
      1098. },
      1099. /**
      1100. -
      1101. * Convert this model to JSON, containing column, value pairs.
      1102. -
      1103. *
      1104. -
      1105. * @return {JSON} the JSON version of this model.
      1106. -
      1107. **/
      1108. -
      1109. toJSON:function () {
      1110. -
      1111. 0 return this.toObject();
      1112. +
      1113. * Middleware called aft era model is removed.
      1114. +
      1115. * </br>
      1116. +
      1117. * <b> This is called in the scope of the model</b>
      1118. +
      1119. * @param {Function} next function to pass control up the middleware stack.
      1120. +
      1121. * @param {_Association} self reference to the Association that is being called.
      1122. +
      1123. */
      1124. +
      1125. _postRemove:function (next, model) {
      1126. +
      1127. 514 next();
      1128. },
      1129. /**
      1130. -
      1131. * Convert this model to a string, containing column, value pairs.
      1132. -
      1133. *
      1134. -
      1135. * @return {String} the string version of this model.
      1136. -
      1137. **/
      1138. -
      1139. toString:function () {
      1140. -
      1141. 0 return JSON.stringify(this.toObject(), null, 4);
      1142. +
      1143. * Middleware called before a model is saved.
      1144. +
      1145. * </br>
      1146. +
      1147. * <b> This is called in the scope of the model</b>
      1148. +
      1149. * @param {Function} next function to pass control up the middleware stack.
      1150. +
      1151. * @param {_Association} self reference to the Association that is being called.
      1152. +
      1153. */
      1154. +
      1155. _preSave:function (next, model) {
      1156. +
      1157. 324 next();
      1158. },
      1159. /**
      1160. -
      1161. * Convert this model to a string, containing column, value pairs.
      1162. -
      1163. *
      1164. -
      1165. * @return {String} the string version of this model.
      1166. -
      1167. **/
      1168. -
      1169. valueOf:function () {
      1170. -
      1171. 0 return this.toObject();
      1172. +
      1173. * Middleware called after a model is saved.
      1174. +
      1175. * </br>
      1176. +
      1177. * <b> This is called in the scope of the model</b>
      1178. +
      1179. * @param {Function} next function to pass control up the middleware stack.
      1180. +
      1181. * @param {_Association} self reference to the Association that is being called.
      1182. +
      1183. */
      1184. +
      1185. _postSave:function (next, model) {
      1186. +
      1187. 198 next();
      1188. },
      1189. -
      1190. _checkTransaction:function (options, cb) {
      1191. -
      1192. 2493 return this._static._checkTransaction(options, cb);
      1193. +
      1194. /**
      1195. +
      1196. * Middleware called before a model is updated.
      1197. +
      1198. * </br>
      1199. +
      1200. * <b> This is called in the scope of the model</b>
      1201. +
      1202. * @param {Function} next function to pass control up the middleware stack.
      1203. +
      1204. * @param {_Association} self reference to the Association that is being called.
      1205. +
      1206. */
      1207. +
      1208. _preUpdate:function (next, model) {
      1209. +
      1210. 2 next();
      1211. },
      1212. -
      1213. addListener:function () {
      1214. -
      1215. 0 var emitter = this.__emitter;
      1216. -
      1217. 0 return emitter.addListener.apply(emitter, arguments);
      1218. -
      1219. },
      1220. -
      1221. on:function () {
      1222. -
      1223. 6 var emitter = this.__emitter;
      1224. -
      1225. 6 return emitter.on.apply(emitter, arguments);
      1226. -
      1227. },
      1228. -
      1229. once:function () {
      1230. -
      1231. 0 var emitter = this.__emitter;
      1232. -
      1233. 0 return emitter.once.apply(emitter, arguments);
      1234. -
      1235. },
      1236. -
      1237. removeListener:function () {
      1238. -
      1239. 6 var emitter = this.__emitter;
      1240. -
      1241. 6 return emitter.removeListener.apply(emitter, arguments);
      1242. -
      1243. },
      1244. -
      1245. removeAllListeners:function () {
      1246. -
      1247. 0 var emitter = this.__emitter;
      1248. -
      1249. 0 return emitter.removeAllListeners.apply(emitter, arguments);
      1250. +
      1251. /**
      1252. +
      1253. * Middleware called before a model is updated.
      1254. +
      1255. * </br>
      1256. +
      1257. * <b> This is called in the scope of the model</b>
      1258. +
      1259. * @param {Function} next function to pass control up the middleware stack.
      1260. +
      1261. * @param {_Association} self reference to the Association that is being called.
      1262. +
      1263. */
      1264. +
      1265. _postUpdate:function (next, model) {
      1266. +
      1267. 127 next();
      1268. },
      1269. -
      1270. setMaxListeners:function () {
      1271. -
      1272. 0 var emitter = this.__emitter;
      1273. -
      1274. 0 return emitter.setMaxListeners.apply(emitter, arguments);
      1275. +
      1276. +
      1277. /**
      1278. +
      1279. * Middleware called before a model is loaded.
      1280. +
      1281. * </br>
      1282. +
      1283. * <b> This is called in the scope of the model</b>
      1284. +
      1285. * @param {Function} next function to pass control up the middleware stack.
      1286. +
      1287. * @param {_Association} self reference to the Association that is being called.
      1288. +
      1289. */
      1290. +
      1291. _preLoad:function (next, model) {
      1292. +
      1293. 1570 next();
      1294. },
      1295. -
      1296. listeners:function () {
      1297. -
      1298. 0 var emitter = this.__emitter;
      1299. -
      1300. 0 return emitter.listeners.apply(emitter, arguments);
      1301. +
      1302. +
      1303. /**
      1304. +
      1305. * Middleware called after a model is loaded.
      1306. +
      1307. * </br>
      1308. +
      1309. * <b> This is called in the scope of the model</b>
      1310. +
      1311. * @param {Function} next function to pass control up the middleware stack.
      1312. +
      1313. * @param {_Association} self reference to the Association that is being called.
      1314. +
      1315. */
      1316. +
      1317. _postLoad:function (next, model) {
      1318. +
      1319. 0 next();
      1320. },
      1321. -
      1322. emit:function () {
      1323. -
      1324. 11120 var emitter = this.__emitter;
      1325. -
      1326. 11120 return emitter.emit.apply(emitter, arguments);
      1327. +
      1328. +
      1329. /**
      1330. +
      1331. * Alias used to explicitly set an association on a model.
      1332. +
      1333. * @param {*} val the value to set the association to
      1334. +
      1335. * @param {_Association} self reference to the Association that is being called.
      1336. +
      1337. */
      1338. +
      1339. _setter:function (val, model) {
      1340. +
      1341. 0 model.__associations[this.name] = val;
      1342. },
      1343. +
      1344. associationLoaded:function (model) {
      1345. +
      1346. 2707 return model.__associations.hasOwnProperty(this.name);
      1347. +
      1348. },
      1349. -
      1350. getters:{
      1351. -
      1352. /**@lends patio.Model.prototype*/
      1353. +
      1354. getAssociation:function (model) {
      1355. +
      1356. 885 return model.__associations[this.name];
      1357. +
      1358. },
      1359. -
      1360. /*Returns my actual primary key value*/
      1361. -
      1362. primaryKeyValue:function () {
      1363. -
      1364. 47 return this[this.primaryKey];
      1365. -
      1366. },
      1367. +
      1368. /**
      1369. +
      1370. * Alias used to explicitly get an association on a model.
      1371. +
      1372. * @param {_Association} self reference to the Association that is being called.
      1373. +
      1374. */
      1375. +
      1376. _getter:function (model) {
      1377. +
      1378. //if we have them return them;
      1379. +
      1380. 612 if (this.associationLoaded(model)) {
      1381. +
      1382. 220 var assoc = this.getAssociation(model);
      1383. +
      1384. 220 return this.isEager() ? assoc : new Promise().callback(assoc).promise();
      1385. +
      1386. 392 } else if (model.isNew) {
      1387. +
      1388. 0 return null;
      1389. +
      1390. } else {
      1391. +
      1392. 392 return this.fetch(model);
      1393. +
      1394. }
      1395. +
      1396. },
      1397. -
      1398. /*Return if Im a new object*/
      1399. -
      1400. isNew:function () {
      1401. -
      1402. 9644 return this.__isNew;
      1403. -
      1404. },
      1405. +
      1406. _toModel:function (val, fromDb) {
      1407. +
      1408. 1412 var Model = this.model;
      1409. +
      1410. 1412 if (!isUndefinedOrNull(Model)) {
      1411. +
      1412. 1412 if (!isInstanceOf(val, Model)) {
      1413. +
      1414. 1167 val = new this.model(val, fromDb);
      1415. +
      1416. }
      1417. +
      1418. } else {
      1419. +
      1420. 0 throw new PatioError("Invalid model " + this.name);
      1421. +
      1422. }
      1423. +
      1424. 1412 return val;
      1425. +
      1426. },
      1427. -
      1428. /*Return if Im changed*/
      1429. -
      1430. isChanged:function () {
      1431. -
      1432. 0 return this.__isChanged;
      1433. -
      1434. },
      1435. +
      1436. /**
      1437. +
      1438. * Method to inject functionality into a model. This method alters the model
      1439. +
      1440. * to prepare it for associations, and initializes all required middleware calls
      1441. +
      1442. * to fulfill requirements needed to loaded the associations.
      1443. +
      1444. *
      1445. +
      1446. * @param {Model} parent the model that is having an associtaion set on it.
      1447. +
      1448. * @param {String} name the name of the association.
      1449. +
      1450. */
      1451. +
      1452. inject:function (parent, name) {
      1453. +
      1454. 55 this.name = name;
      1455. +
      1456. 55 var self = this;
      1457. +
      1458. 55 this.parent = parent;
      1459. +
      1460. 55 parent.prototype.__defineGetter__(name, function () {
      1461. +
      1462. 612 return self._getter(this);
      1463. +
      1464. });
      1465. +
      1466. 55 parent.prototype.__defineGetter__(this.associatedDatasetName, function () {
      1467. +
      1468. 146 return self._filter(this);
      1469. +
      1470. });
      1471. -
      1472. /**@lends patio.Model.prototype*/
      1473. +
      1474. 55 if (!this.readOnly && this.createSetter) {
      1475. +
      1476. //define a setter because we arent read only
      1477. +
      1478. 55 parent.prototype.__defineSetter__(name, function (vals) {
      1479. +
      1480. 139 self._setter(vals, this);
      1481. +
      1482. });
      1483. +
      1484. }
      1485. -
      1486. primaryKey:function () {
      1487. -
      1488. 2568 return this._static.primaryKey;
      1489. +
      1490. //set up all callbacks
      1491. +
      1492. 55 ["pre", "post"].forEach(function (op) {
      1493. +
      1494. 110 ["save", "update", "remove", "load"].forEach(function (type) {
      1495. +
      1496. 440 parent[op](type, function (next) {
      1497. +
      1498. 5598 return self["_" + op + type.charAt(0).toUpperCase() + type.slice(1)](next, this);
      1499. +
      1500. });
      1501. +
      1502. }, this);
      1503. +
      1504. }, this);
      1505. +
      1506. },
      1507. +
      1508. +
      1509. getters:{
      1510. +
      1511. +
      1512. select:function () {
      1513. +
      1514. 648 return this.__opts.select;
      1515. },
      1516. -
      1517. tableName:function () {
      1518. -
      1519. 40 return this._static.tableName;
      1520. +
      1521. defaultLeftKey:function () {
      1522. +
      1523. 2841 var ret = "";
      1524. +
      1525. 2841 if (this.isOwner) {
      1526. +
      1527. 2095 ret = this.__opts.primaryKey || this.parent.primaryKey[0]
      1528. +
      1529. } else {
      1530. +
      1531. 746 ret = this.model.tableName + "Id";
      1532. +
      1533. }
      1534. +
      1535. 2841 return ret;
      1536. },
      1537. -
      1538. dataset:function () {
      1539. -
      1540. 3085 return this.__dataset || this._static.dataset;
      1541. +
      1542. defaultRightKey:function () {
      1543. +
      1544. 2721 return this.associatedModelKey;
      1545. },
      1546. -
      1547. db:function () {
      1548. -
      1549. 28 return this._static.db;
      1550. +
      1551. //Returns our model
      1552. +
      1553. model:function () {
      1554. +
      1555. 10572 return this.patio.getModel(this._model, this.parent.db);
      1556. },
      1557. -
      1558. schema:function () {
      1559. -
      1560. 75099 return this._static.schema;
      1561. +
      1562. associatedModelKey:function () {
      1563. +
      1564. 2721 var ret = "";
      1565. +
      1566. 2721 if (this.isOwner) {
      1567. +
      1568. 1857 ret = this.__opts.primaryKey || this.parent.tableName + "Id";
      1569. +
      1570. } else {
      1571. +
      1572. 864 ret = this.model.primaryKey[0];
      1573. +
      1574. }
      1575. +
      1576. 2721 return ret;
      1577. },
      1578. -
      1579. columns:function () {
      1580. -
      1581. 0 return this._static.columns;
      1582. +
      1583. associatedDatasetName:function () {
      1584. +
      1585. 201 return this.name + "Dataset"
      1586. },
      1587. -
      1588. synced:function () {
      1589. -
      1590. 12784 return this._static.synced;
      1591. +
      1592. removeAssociationFlagName:function () {
      1593. +
      1594. 27 return "__remove" + this.name + "association"
      1595. }
      1596. }
      1597. },
      1598. static:{
      1599. -
      1600. /**
      1601. -
      1602. * @lends patio.Model
      1603. -
      1604. */
      1605. -
      1606. -
      1607. synced:false,
      1608. +
      1609. /**@lends patio.associations.Association*/
      1610. -
      1611. /**
      1612. -
      1613. * Set to false to prevent the emitting of an event on load
      1614. -
      1615. * @default true
      1616. -
      1617. */
      1618. -
      1619. emitOnLoad:true,
      1620. +
      1621. fetch:{
      1622. -
      1623. /**
      1624. -
      1625. * Set to false to prevent the emitting of an event on the setting of a column value
      1626. -
      1627. * @default true
      1628. -
      1629. */
      1630. -
      1631. emitOnColumnSet:true,
      1632. +
      1633. LAZY:"lazy",
      1634. +
      1635. EAGER:"eager"
      1636. +
      1637. }
      1638. +
      1639. }
      1640. +
      1641. }).as(module);
      +
    + +
    + + + + + +
    +
    model.js
    +
    +
    + Coverage88.47 + SLOC1066 + LOC321 + Missed37 +
    +
    +
    1. 1var comb = require("comb"),
    2. +
    3. isFunction = comb.isFunction,
    4. +
    5. isUndefined = comb.isUndefined,
    6. +
    7. isDefined = comb.isDefined,
    8. +
    9. isBoolean = comb.isBoolean,
    10. +
    11. isString = comb.isString,
    12. +
    13. argsToArray = comb.argsToArray,
    14. +
    15. isInstanceOf = comb.isInstanceOf,
    16. +
    17. serial = comb.serial,
    18. +
    19. isHash = comb.isHash,
    20. +
    21. when = comb.when,
    22. +
    23. merge = comb.merge,
    24. +
    25. toArray = comb.array.toArray,
    26. +
    27. ModelError = require("./errors").ModelError,
    28. +
    29. plugins = require("./plugins"),
    30. +
    31. isUndefinedOrNull = comb.isUndefinedOrNull,
    32. +
    33. AssociationPlugin = plugins.AssociationPlugin,
    34. +
    35. QueryPlugin = plugins.QueryPlugin,
    36. +
    37. Promise = comb.Promise,
    38. +
    39. PromiseList = comb.PromiseList,
    40. +
    41. HashTable = comb.collections.HashTable,
    42. +
    43. hitch = comb.hitch,
    44. +
    45. hitchIgnore = comb.hitchIgnore,
    46. +
    47. Middleware = comb.plugins.Middleware,
    48. +
    49. EventEmitter = require("events").EventEmitter,
    50. +
    51. util = require("util"),
    52. +
    53. define = comb.define,
    54. +
    55. patio;
    56. -
    57. /**
    58. -
    59. * Set to false to prevent empty strings from being type casted to null
    60. -
    61. * @default true
    62. -
    63. */
    64. -
    65. typecastEmptyStringToNull:true,
    66. -
    67. /**
    68. -
    69. * Set to false to prevent properties from being type casted when loaded from the database.
    70. -
    71. * See {@link patio.Database#typecastValue}
    72. -
    73. * @default true
    74. -
    75. */
    76. -
    77. typecastOnLoad:true,
    78. +
    79. 1var MODELS = new HashTable();
    80. -
    81. /**
    82. -
    83. * Set to false to prevent properties from being type casted when manually set.
    84. -
    85. * See {@link patio.Database#typecastValue}
    86. -
    87. * @default true
    88. -
    89. */
    90. -
    91. typecastOnAssignment:true,
    92. +
    93. 1var applyColumnTransformMethod = function (val, meth) {
    94. +
    95. 11464 return !isUndefinedOrNull(meth) ? isFunction(val[meth]) ? val[meth] : isFunction(comb[meth]) ? comb[meth](val) : val : val;
    96. +
    97. };
    98. -
    99. /**
    100. -
    101. * Set to false to prevent errors thrown while type casting a value from being propogated.
    102. -
    103. * @default true
    104. -
    105. */
    106. -
    107. raiseOnTypecastError:true,
    108. +
    109. 1var checkAndTransformName = function (name) {
    110. +
    111. 10693 return isString(name) ? applyColumnTransformMethod(name, Model.camelize === true ? "camelize" : Model.underscore === true ? "underscore" : null) : name;
    112. +
    113. };
    114. +
    115. 1var Model = define([QueryPlugin, Middleware], {
    116. +
    117. instance:{
    118. /**
    119. -
    120. * Set to false to allow the setting of primary keys.
    121. -
    122. * @default false
    123. +
    124. * @lends patio.Model.prototype
    125. */
    126. -
    127. isRestrictedPrimaryKey:true,
    128. -
    129. /**
    130. -
    131. * Set to false to prevent models from using transactions when saving, deleting, or updating.
    132. -
    133. * This applies to the model associations also.
    134. -
    135. */
    136. -
    137. useTransactions:true,
    138. +
    139. __ignore:false,
    140. -
    141. /**
    142. -
    143. * See {@link patio.Dataset#identifierOutputMethod}
    144. -
    145. * @default null
    146. -
    147. */
    148. -
    149. identifierOutputMethod:null,
    150. +
    151. __changed:null,
    152. -
    153. /**
    154. -
    155. * See {@link patio.Dataset#identifierInputMethod}
    156. -
    157. * @default null
    158. -
    159. */
    160. -
    161. identifierInputMethod:null,
    162. +
    163. __values:null,
    164. /**
    165. -
    166. * Set to false to prevent the reload of a model after saving.
    167. -
    168. * @default true
    169. +
    170. * patio - read only
    171. +
    172. *
    173. +
    174. * @type patio
    175. */
    176. -
    177. reloadOnSave:true,
    178. +
    179. patio:null,
    180. /**
    181. -
    182. * Columns that should be restriced when setting values through the {@link patio.Model#set} method.
    183. +
    184. * The database type such as mysql
    185. *
    186. -
    187. */
    188. -
    189. restrictedColumns:null,
    190. +
    191. * @type String
    192. +
    193. *
    194. +
    195. * */
    196. +
    197. type:null,
    198. /**
    199. -
    200. * Set to false to prevent the reload of a model after updating.
    201. -
    202. * @default true
    203. -
    204. */
    205. -
    206. reloadOnUpdate:true,
    207. -
    208. -
    209. -
    210. __camelize:false,
    211. -
    212. -
    213. __underscore:false,
    214. -
    215. -
    216. __columns:null,
    217. -
    218. -
    219. __schema:null,
    220. -
    221. -
    222. __primaryKey:null,
    223. -
    224. -
    225. __dataset:null,
    226. -
    227. -
    228. __db:null,
    229. -
    230. -
    231. __tableName:null,
    232. -
    233. +
    234. * Whether or not this model is new
    235. +
    236. * */
    237. +
    238. __isNew:true,
    239. /**
    240. -
    241. * The table that this Model represents.
    242. -
    243. * <b>READ ONLY</b>
    244. -
    245. */
    246. -
    247. table:null,
    248. +
    249. * Signifies if the model has changed
    250. +
    251. * */
    252. +
    253. __isChanged:false,
    254. /**
    255. -
    256. * patio - read only
    257. +
    258. * Base class for all models.
    259. +
    260. * <p>This is used through {@link patio.addModel}, <b>NOT directly.</b></p>
    261. *
    262. -
    263. * @type patio
    264. -
    265. */
    266. -
    267. patio:null,
    268. -
    269. -
    270. init:function () {
    271. -
    272. 92 var emitter = new EventEmitter();
    273. -
    274. 92 ["addListener", "on", "once", "removeListener",
    275. -
    276. "removeAllListeners", "setMaxListeners", "listeners", "emit"].forEach(function (name) {
    277. -
    278. 736 this[name] = emitter[name].bind(emitter);
    279. -
    280. }, this);
    281. -
    282. 92 if (this.__tableName) {
    283. -
    284. 91 this._setTableName(this.__tableName);
    285. -
    286. }
    287. -
    288. 92 if (this.__db) {
    289. -
    290. 7 this._setDb(this.__db);
    291. -
    292. }
    293. -
    294. },
    295. -
    296. -
    297. -
    298. sync:function (cb) {
    299. -
    300. 3968 var ret = new Promise();
    301. -
    302. 3968 if (!this.synced) {
    303. -
    304. 93 var db = this.db, tableName = this.tableName, supers = this.__supers;
    305. -
    306. 93 db.schema(tableName).then(hitch(this, function (schema) {
    307. -
    308. 93 if (!this.synced && schema) {
    309. -
    310. 93 this._setSchema(schema);
    311. -
    312. 93 if (supers && supers.length) {
    313. -
    314. 3 new PromiseList(supers.map(function (sup) {
    315. -
    316. 3 return sup.sync();
    317. -
    318. })).then(hitch(this, function () {
    319. -
    320. 3 this.synced = true;
    321. -
    322. 3 supers.forEach(this.inherits, this);
    323. -
    324. 3 ret.callback(this);
    325. -
    326. }), ret);
    327. -
    328. } else {
    329. -
    330. 90 this.synced = true;
    331. -
    332. 90 ret.callback(this);
    333. -
    334. }
    335. -
    336. 93 this.emit("sync", this);
    337. -
    338. } else {
    339. -
    340. 0 var error = new ModelError("Unable to find schema for " + tableName);
    341. -
    342. 0 this.emit("error", error);
    343. -
    344. 0 ret.errback(error);
    345. +
    346. * @constructs
    347. +
    348. * @augments comb.plugins.Middleware
    349. +
    350. *
    351. +
    352. * @param {Object} columnValues values of each column to be used by this Model.
    353. +
    354. *
    355. +
    356. * @property {patio.Dataset} dataset a dataset to use to retrieve models from the database. The dataset
    357. +
    358. * has the {@link patio.Dataset#rowCb} set to create instances of this model.
    359. +
    360. * @property {String[]} columns a list of columns this models table contains.
    361. +
    362. * @property {Object} schema the schema of this models table.
    363. +
    364. * @property {String} tableName the table name of this models table.
    365. +
    366. * @property {*} primaryKeyValue the value of this models primaryKey
    367. +
    368. * @property {Boolean} isNew true if this model is new and does not exist in the database.
    369. +
    370. * @property {Boolean} isChanged true if the model has been changed and not saved.
    371. +
    372. *
    373. +
    374. * @borrows patio.Dataset#all as all
    375. +
    376. * @borrows patio.Dataset#one as one
    377. +
    378. * @borrows patio.Dataset#avg as avg
    379. +
    380. * @borrows patio.Dataset#count as count
    381. +
    382. * @borrows patio.Dataset#columns as columns
    383. +
    384. * @borrows patio.Dataset#forEach as forEach
    385. +
    386. * @borrows patio.Dataset#isEmpty as empty
    387. +
    388. * @borrows patio.Dataset#first as first
    389. +
    390. * @borrows patio.Dataset#get as get
    391. +
    392. * @borrows patio.Dataset#import as import
    393. +
    394. * @borrows patio.Dataset#insert as insert
    395. +
    396. * @borrows patio.Dataset#insertMultiple as insertMultiple
    397. +
    398. * @borrows patio.Dataset#saveMultiple as saveMultiple
    399. +
    400. * @borrows patio.Dataset#interval as interval
    401. +
    402. * @borrows patio.Dataset#last as last
    403. +
    404. * @borrows patio.Dataset#map as map
    405. +
    406. * @borrows patio.Dataset#max as max
    407. +
    408. * @borrows patio.Dataset#min as min
    409. +
    410. * @borrows patio.Dataset#multiInsert as multiInsert
    411. +
    412. * @borrows patio.Dataset#range as range
    413. +
    414. * @borrows patio.Dataset#selectHash as selectHash
    415. +
    416. * @borrows patio.Dataset#selectMap as selectMap
    417. +
    418. * @borrows patio.Dataset#selectOrderMap as selectOrderMap
    419. +
    420. * @borrows patio.Dataset#set as set
    421. +
    422. * @borrows patio.Dataset#singleRecord as singleRecord
    423. +
    424. * @borrows patio.Dataset#singleValue as singleValue
    425. +
    426. * @borrows patio.Dataset#sum as sum
    427. +
    428. * @borrows patio.Dataset#toCsv as toCsv
    429. +
    430. * @borrows patio.Dataset#toHash as toHash
    431. +
    432. * @borrows patio.Dataset#truncate as truncate
    433. +
    434. * @borrows patio.Dataset#addGraphAliases as addGraphAliases
    435. +
    436. * @borrows patio.Dataset#and as and
    437. +
    438. * @borrows patio.Dataset#distinct as distinct
    439. +
    440. * @borrows patio.Dataset#except as except
    441. +
    442. * @borrows patio.Dataset#exclude as exclude
    443. +
    444. * @borrows patio.Dataset#is as is
    445. +
    446. * @borrows patio.Dataset#isNot as isNot
    447. +
    448. * @borrows patio.Dataset#eq as eq
    449. +
    450. * @borrows patio.Dataset#neq as neq
    451. +
    452. * @borrows patio.Dataset#lt as lt
    453. +
    454. * @borrows patio.Dataset#lte as lte
    455. +
    456. * @borrows patio.Dataset#gt as gt
    457. +
    458. * @borrows patio.Dataset#gte as gte
    459. +
    460. * @borrows patio.Dataset#forUpdate as forUpdate
    461. +
    462. * @borrows patio.Dataset#from as from
    463. +
    464. * @borrows patio.Dataset#fromSelf as fromSelf
    465. +
    466. * @borrows patio.Dataset#graph as graph
    467. +
    468. * @borrows patio.Dataset#grep as grep
    469. +
    470. * @borrows patio.Dataset#group as group
    471. +
    472. * @borrows patio.Dataset#groupAndCount as groupAndCount
    473. +
    474. * @borrows patio.Dataset#groupBy as groupBy
    475. +
    476. * @borrows patio.Dataset#having as having
    477. +
    478. * @borrows patio.Dataset#intersect as intersect
    479. +
    480. * @borrows patio.Dataset#invert as invert
    481. +
    482. * @borrows patio.Dataset#limit as limit
    483. +
    484. * @borrows patio.Dataset#lockStyle as lockStyle
    485. +
    486. * @borrows patio.Dataset#naked as naked
    487. +
    488. * @borrows patio.Dataset#or as or
    489. +
    490. * @borrows patio.Dataset#order as order
    491. +
    492. * @borrows patio.Dataset#orderAppend as orderAppend
    493. +
    494. * @borrows patio.Dataset#orderBy as orderBy
    495. +
    496. * @borrows patio.Dataset#orderMore as orderMore
    497. +
    498. * @borrows patio.Dataset#orderPrepend as orderPrepend
    499. +
    500. * @borrows patio.Dataset#qualify as qualify
    501. +
    502. * @borrows patio.Dataset#reverse as reverse
    503. +
    504. * @borrows patio.Dataset#reverseOrder as reverseOrder
    505. +
    506. * @borrows patio.Dataset#select as select
    507. +
    508. * @borrows patio.Dataset#selectAll as selectAll
    509. +
    510. * @borrows patio.Dataset#selectAppend as selectAppend
    511. +
    512. * @borrows patio.Dataset#selectMore as selectMore
    513. +
    514. * @borrows patio.Dataset#setDefaults as setDefaults
    515. +
    516. * @borrows patio.Dataset#setGraphAliases as setGraphAliases
    517. +
    518. * @borrows patio.Dataset#setOverrides as setOverrides
    519. +
    520. * @borrows patio.Dataset#unfiltered as unfiltered
    521. +
    522. * @borrows patio.Dataset#ungraphed as ungraphed
    523. +
    524. * @borrows patio.Dataset#ungrouped as ungrouped
    525. +
    526. * @borrows patio.Dataset#union as union
    527. +
    528. * @borrows patio.Dataset#unlimited as unlimited
    529. +
    530. * @borrows patio.Dataset#unordered as unordered
    531. +
    532. * @borrows patio.Dataset#where as where
    533. +
    534. * @borrows patio.Dataset#with as with
    535. +
    536. * @borrows patio.Dataset#withRecursive as withRecursive
    537. +
    538. * @borrows patio.Dataset#withSql as withSql
    539. +
    540. * @borrows patio.Dataset#naturalJoin as naturalJoin
    541. +
    542. * @borrows patio.Dataset#naturalLeftJoin as naturalLeftJoin
    543. +
    544. * @borrows patio.Dataset#naturalRightJoin as naturalRightJoin
    545. +
    546. * @borrows patio.Dataset#naturalFullJoin as naturalFullJoin
    547. +
    548. * @borrows patio.Dataset#crossJoin as crossJoin
    549. +
    550. * @borrows patio.Dataset#innerJoin as innerJoin
    551. +
    552. * @borrows patio.Dataset#fullOuterJoin as fullOuterJoin
    553. +
    554. * @borrows patio.Dataset#rightOuterJoin as rightOuterJoin
    555. +
    556. * @borrows patio.Dataset#leftOuterJoin as leftOuterJoin
    557. +
    558. * @borrows patio.Dataset#fullJoin as fullJoin
    559. +
    560. * @borrows patio.Dataset#rightJoin as rightJoin
    561. +
    562. * @borrows patio.Dataset#leftJoin as leftJoin
    563. +
    564. * */
    565. +
    566. constructor:function (options, fromDb) {
    567. +
    568. 3107 if (this.synced) {
    569. +
    570. 3107 this.__emitter = new EventEmitter();
    571. +
    572. 3107 this._super(arguments);
    573. +
    574. 3107 this.patio = patio || require("./index");
    575. +
    576. 3107 fromDb = isBoolean(fromDb) ? fromDb : false;
    577. +
    578. 3107 this.__changed = {};
    579. +
    580. 3107 this.__values = {};
    581. +
    582. 3107 if (fromDb) {
    583. +
    584. 1754 this._hook("pre", "load");
    585. +
    586. 1754 this.__isNew = false;
    587. +
    588. 1754 this.__setFromDb(options, true);
    589. +
    590. 1754 if (this._static.emitOnLoad) {
    591. +
    592. 1754 this.emit("load", this);
    593. +
    594. 1754 this._static.emit("load", this);
    595. }
    596. -
    597. }), ret);
    598. +
    599. } else {
    600. +
    601. 1353 this.__isNew = true;
    602. +
    603. 1353 this.__set(options);
    604. +
    605. }
    606. } else {
    607. -
    608. 3875 ret.callback(this);
    609. -
    610. }
    611. -
    612. 3968 if (isFunction(cb)) {
    613. -
    614. 0 ret.classic(cb);
    615. +
    616. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
    617. }
    618. -
    619. 3968 return ret.promise();
    620. },
    621. -
    622. /**
    623. -
    624. * Stub for plugins to notified of model inheritance
    625. -
    626. *
    627. -
    628. * @param {patio.Model} model a model class to inherit from
    629. -
    630. */
    631. -
    632. inherits:function (model) {
    633. +
    634. __set:function (values, ignore) {
    635. +
    636. 1475 values = values || {};
    637. +
    638. 1475 this.__ignore = ignore === true;
    639. +
    640. 1475 Object.keys(values).forEach(function (attribute) {
    641. +
    642. 6632 var value = values[attribute];
    643. +
    644. //check if the column is a constrained value and is allowed to be set
    645. +
    646. 6632 !ignore && this._checkIfColumnIsConstrained(attribute);
    647. +
    648. 6632 this[attribute] = value;
    649. +
    650. }, this);
    651. +
    652. 1475 this.__ignore = false;
    653. },
    654. +
    655. __setFromDb:function (values, ignore) {
    656. +
    657. 3057 values = values || {};
    658. +
    659. 3057 this.__ignore = ignore === true;
    660. +
    661. 3057 var schema = this.schema;
    662. +
    663. 3057 Object.keys(values).forEach(function (column) {
    664. +
    665. 25830 var value = values[column];
    666. +
    667. // Typecast value retrieved from db
    668. +
    669. 25830 if (schema.hasOwnProperty(column)) {
    670. +
    671. 25149 this.__values[column] = this._typeCastValue(column, value, ignore);
    672. +
    673. } else {
    674. +
    675. 681 this[column] = value;
    676. +
    677. }
    678. +
    679. }, this);
    680. +
    681. 3057 this.__ignore = false;
    682. +
    683. +
    684. },
    685. /**
    686. -
    687. * Create a new model initialized with the specified values.
    688. +
    689. * Set multiple values at once. Useful if you have a hash of properties that you want to set.
    690. *
    691. -
    692. * @param {Object} values the values to initialize the model with.
    693. +
    694. * <b>NOTE:</b> This method will use the static restrictedColumns property of the model.
    695. *
    696. -
    697. * @returns {Model} instantiated model initialized with the values passed in.
    698. +
    699. * @example
    700. +
    701. *
    702. +
    703. * myModel.setValues({firstName : "Bob", lastName : "yukon"});
    704. +
    705. *
    706. +
    707. * //this will throw an error by default, assuming id is a pk.
    708. +
    709. * myModel.setValues({id : 1, firstName : "Bob", lastName : "yukon"});
    710. +
    711. *
    712. +
    713. * @param {Object} values value to set on the model.
    714. +
    715. *
    716. +
    717. * @return {patio.Model} return this for chaining.
    718. */
    719. -
    720. create:function (values) {
    721. -
    722. //load an object from an object
    723. -
    724. 0 return new this(values, false);
    725. +
    726. setValues:function (values) {
    727. +
    728. 17 this.__set(values, false);
    729. +
    730. 17 return this;
    731. },
    732. -
    733. load:function (vals) {
    734. -
    735. 924 var ret = new Promise();
    736. -
    737. //sync our model
    738. -
    739. 924 this.sync().then(hitch(this, function () {
    740. -
    741. 924 var m = new this(vals, true);
    742. -
    743. //call the hooks!
    744. -
    745. 924 m._hook("post", "load").then(function () {
    746. -
    747. 924 ret.callback(m);
    748. -
    749. }, ret);
    750. -
    751. }), ret);
    752. -
    753. 924 return ret.promise();
    754. +
    755. _toObject:function () {
    756. +
    757. 1124 if (this.synced) {
    758. +
    759. 1124 var columns = this._static.columns, ret = {};
    760. +
    761. 1124 for (var i in columns) {
    762. +
    763. 10220 var col = columns[i];
    764. +
    765. 10220 var val = this.__values[col];
    766. +
    767. 10220 if (!isUndefined(val)) {
    768. +
    769. 6169 ret[col] = val;
    770. +
    771. }
    772. +
    773. }
    774. +
    775. 1124 return ret;
    776. +
    777. } else {
    778. +
    779. 0 throw new ModelError("Model " + this.tableName + " has not been synced");
    780. +
    781. }
    782. },
    783. -
    784. _checkTransaction:function (opts, cb) {
    785. -
    786. 2892 if (isFunction(opts)) {
    787. -
    788. 639 cb = opts;
    789. -
    790. 639 opts = {};
    791. -
    792. } else {
    793. -
    794. 2253 opts = opts || {};
    795. +
    796. _addColumnToIsChanged:function (name, val) {
    797. +
    798. 7556 if (!this.isNew && !this.__ignore) {
    799. +
    800. 165 this.__isChanged = true;
    801. +
    802. 165 this.__changed[name] = val;
    803. }
    804. -
    805. 2892 var ret = new Promise(), retVal = null, errored = false;
    806. -
    807. 2892 this.sync().then(hitch(this, function () {
    808. -
    809. 2892 if (this.useTransaction(opts)) {
    810. -
    811. 2892 this.db.transaction(opts, hitch(this, function () {
    812. -
    813. 2892 return when(cb()).then(function () {
    814. -
    815. 2848 retVal = argsToArray(arguments);
    816. -
    817. }, function () {
    818. -
    819. 44 retVal = argsToArray(arguments);
    820. -
    821. 44 errored = true;
    822. -
    823. });
    824. -
    825. })).then(hitch(this, function () {
    826. -
    827. 2848 ret[errored ? "errback" : "callback"].apply(ret, retVal);
    828. -
    829. }), hitch(this, function () {
    830. -
    831. 44 if (errored) {
    832. -
    833. 44 ret.errback.apply(ret, retVal);
    834. -
    835. } else {
    836. -
    837. 0 ret.errback.apply(ret, arguments);
    838. -
    839. }
    840. -
    841. }));
    842. -
    843. } else {
    844. -
    845. 0 when(cb()).then(ret);
    846. +
    847. },
    848. +
    849. +
    850. _checkIfColumnIsConstrained:function (name) {
    851. +
    852. 6632 if (this.synced && !this.__ignore) {
    853. +
    854. 6632 var col = this.schema[name], restrictedCols = this._static.restrictedColumns || [];
    855. +
    856. 6632 if (!isUndefined(col) && (col.primaryKey && this._static.isRestrictedPrimaryKey) || restrictedCols.indexOf(name) != -1) {
    857. +
    858. 0 throw new ModelError("Cannot set primary key of model " + this._static.tableName);
    859. }
    860. -
    861. }), ret);
    862. -
    863. 2892 return ret.promise();
    864. +
    865. }
    866. },
    867. -
    868. /**
    869. -
    870. * @private
    871. -
    872. * Returns a boolean indicating whether or not to use a transaction.
    873. -
    874. * @param {Object} [opts] set a transaction property to override the {@link patio.Model#useTransaction}.
    875. -
    876. */
    877. -
    878. useTransaction:function (opts) {
    879. -
    880. 2892 opts = opts || {};
    881. -
    882. 2892 return isBoolean(opts.transaction) ? opts.transaction === true : this.useTransactions === true;
    883. +
    884. _getColumnValue:function (name) {
    885. +
    886. 5427 var val = this.__values[name];
    887. +
    888. 5427 var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];
    889. +
    890. 5427 var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;
    891. +
    892. +
    893. 5427 return columnValue;
    894. },
    895. -
    896. _setDataset:function (ds) {
    897. -
    898. 3 this.__dataset = ds;
    899. -
    900. 3 if (ds.db) {
    901. -
    902. 3 this._setDb(ds.db);
    903. +
    904. _setColumnValue:function (name, val) {
    905. +
    906. 7556 var ignore = this.__ignore;
    907. +
    908. 7556 val = this._typeCastValue(name, val, ignore);
    909. +
    910. 7556 this._addColumnToIsChanged(name, val);
    911. +
    912. 7556 var setterFunc = this["_set" + name.charAt(0).toUpperCase() + name.substr(1)];
    913. +
    914. 7556 var columnValue = isFunction(setterFunc) ? setterFunc.call(this, val, ignore) : val;
    915. +
    916. 7556 this.__values[name] = columnValue;
    917. +
    918. 7556 if (this._static.emitOnColumnSet) {
    919. +
    920. 7556 this.emit("column", name, columnValue, ignore);
    921. }
    922. },
    923. -
    924. _setDb:function (db) {
    925. -
    926. 10 this.__db = db;
    927. +
    928. +
    929. //Typecast the value to the column's type if typecasting. Calls the database's
    930. +
    931. //typecast_value method, so database adapters can override/augment the handling
    932. +
    933. //for database specific column types.
    934. +
    935. _typeCastValue:function (column, value, fromDatabase) {
    936. +
    937. 32705 var colSchema, clazz = this._static;
    938. +
    939. 32705 if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {
    940. +
    941. 32705 var type = colSchema.type;
    942. +
    943. 32705 if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {
    944. +
    945. 3 value = null;
    946. +
    947. }
    948. +
    949. 32705 var raiseOnError = clazz.raiseOnTypecastError;
    950. +
    951. 32705 if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {
    952. +
    953. 0 throw new ModelError("null is not allowed for the " + column + " column.");
    954. +
    955. }
    956. +
    957. 32705 try {
    958. +
    959. 32705 value = clazz.db.typecastValue(type, value);
    960. +
    961. } catch (e) {
    962. +
    963. 0 if (raiseOnError === true) {
    964. +
    965. 0 throw e;
    966. +
    967. }
    968. +
    969. }
    970. +
    971. }
    972. +
    973. 32705 return value;
    974. },
    975. -
    976. _setTableName:function (name) {
    977. -
    978. 91 this.__tableName = name;
    979. +
    980. /**
    981. +
    982. * Convert this model to an object, containing column, value pairs.
    983. +
    984. *
    985. +
    986. * @return {Object} the object version of this model.
    987. +
    988. **/
    989. +
    990. toObject:function () {
    991. +
    992. 0 return this._toObject(false);
    993. },
    994. -
    995. _setColumns:function (cols) {
    996. -
    997. 96 var proto = this.prototype;
    998. -
    999. 96 if (this.__columns) {
    1000. -
    1001. 6 this.__columns.forEach(function (name) {
    1002. -
    1003. 16 delete proto[name];
    1004. -
    1005. });
    1006. -
    1007. }
    1008. -
    1009. 96 this.__columns = cols;
    1010. -
    1011. 96 cols.forEach(function (name) {
    1012. -
    1013. 779 this._defineColumnSetter(name);
    1014. -
    1015. 779 this._defineColumnGetter(name);
    1016. -
    1017. }, this);
    1018. +
    1019. /**
    1020. +
    1021. * Convert this model to JSON, containing column, value pairs.
    1022. +
    1023. *
    1024. +
    1025. * @return {JSON} the JSON version of this model.
    1026. +
    1027. **/
    1028. +
    1029. toJSON:function () {
    1030. +
    1031. 0 return this.toObject();
    1032. },
    1033. -
    1034. _setPrimaryKey:function (pks) {
    1035. -
    1036. 99 this.__primaryKey = pks || [];
    1037. +
    1038. /**
    1039. +
    1040. * Convert this model to a string, containing column, value pairs.
    1041. +
    1042. *
    1043. +
    1044. * @return {String} the string version of this model.
    1045. +
    1046. **/
    1047. +
    1048. toString:function () {
    1049. +
    1050. 0 return JSON.stringify(this.toObject(), null, 4);
    1051. },
    1052. -
    1053. _setSchema:function (schema) {
    1054. -
    1055. 96 var columns = [];
    1056. -
    1057. 96 var pks = [];
    1058. -
    1059. 96 for (var i in schema) {
    1060. -
    1061. 779 var col = schema[i];
    1062. -
    1063. 779 var name = applyColumnTransformMethod(i, this.identifierOutputMethod);
    1064. -
    1065. 779 schema[name] = col;
    1066. -
    1067. 779 columns.push(name);
    1068. -
    1069. 779 col.primaryKey && pks.push(name);
    1070. -
    1071. }
    1072. -
    1073. 96 this.__schema = schema;
    1074. -
    1075. 96 this._setPrimaryKey(pks);
    1076. -
    1077. 96 this._setColumns(columns);
    1078. +
    1079. /**
    1080. +
    1081. * Convert this model to a string, containing column, value pairs.
    1082. +
    1083. *
    1084. +
    1085. * @return {String} the string version of this model.
    1086. +
    1087. **/
    1088. +
    1089. valueOf:function () {
    1090. +
    1091. 0 return this.toObject();
    1092. },
    1093. -
    1094. _defineColumnSetter:function (name) {
    1095. -
    1096. /*Adds a setter to an object*/
    1097. -
    1098. 779 this.prototype.__defineSetter__(name, function (val) {
    1099. -
    1100. 7556 this._setColumnValue(name, val);
    1101. -
    1102. });
    1103. +
    1104. _checkTransaction:function (options, cb) {
    1105. +
    1106. 2493 return this._static._checkTransaction(options, cb);
    1107. },
    1108. -
    1109. _defineColumnGetter:function (name) {
    1110. -
    1111. 779 this.prototype.__defineGetter__(name, function () {
    1112. -
    1113. 5427 return this._getColumnValue(name);
    1114. -
    1115. });
    1116. +
    1117. addListener:function () {
    1118. +
    1119. 0 var emitter = this.__emitter;
    1120. +
    1121. 0 return emitter.addListener.apply(emitter, arguments);
    1122. +
    1123. },
    1124. +
    1125. on:function () {
    1126. +
    1127. 6 var emitter = this.__emitter;
    1128. +
    1129. 6 return emitter.on.apply(emitter, arguments);
    1130. +
    1131. },
    1132. +
    1133. once:function () {
    1134. +
    1135. 0 var emitter = this.__emitter;
    1136. +
    1137. 0 return emitter.once.apply(emitter, arguments);
    1138. +
    1139. },
    1140. +
    1141. removeListener:function () {
    1142. +
    1143. 6 var emitter = this.__emitter;
    1144. +
    1145. 6 return emitter.removeListener.apply(emitter, arguments);
    1146. +
    1147. },
    1148. +
    1149. removeAllListeners:function () {
    1150. +
    1151. 0 var emitter = this.__emitter;
    1152. +
    1153. 0 return emitter.removeAllListeners.apply(emitter, arguments);
    1154. +
    1155. },
    1156. +
    1157. setMaxListeners:function () {
    1158. +
    1159. 0 var emitter = this.__emitter;
    1160. +
    1161. 0 return emitter.setMaxListeners.apply(emitter, arguments);
    1162. +
    1163. },
    1164. +
    1165. listeners:function () {
    1166. +
    1167. 0 var emitter = this.__emitter;
    1168. +
    1169. 0 return emitter.listeners.apply(emitter, arguments);
    1170. +
    1171. },
    1172. +
    1173. emit:function () {
    1174. +
    1175. 11120 var emitter = this.__emitter;
    1176. +
    1177. 11120 return emitter.emit.apply(emitter, arguments);
    1178. },
    1179. -
    1180. /**
    1181. -
    1182. * @ignore
    1183. -
    1184. */
    1185. getters:{
    1186. -
    1187. /**@lends patio.Model*/
    1188. -
    1189. -
    1190. /**
    1191. -
    1192. * Set to true if this models column names should be use the "underscore" method when sending
    1193. -
    1194. * keys to the database and to "camelize" method on columns returned from the database. If set to false see
    1195. -
    1196. * {@link patio.Model#underscore}.
    1197. -
    1198. * @field
    1199. -
    1200. * @default false
    1201. -
    1202. * @type {Boolean}
    1203. -
    1204. */
    1205. -
    1206. camelize:function (camelize) {
    1207. -
    1208. 10686 return this.__camelize;
    1209. +
    1210. /**@lends patio.Model.prototype*/
    1211. +
    1212. /*Returns my actual primary key value*/
    1213. +
    1214. primaryKeyValue:function () {
    1215. +
    1216. 47 return this[this.primaryKey];
    1217. },
    1218. -
    1219. /**
    1220. -
    1221. * Set to true if this models column names should be use the "camelize" method when sending
    1222. -
    1223. * keys to the database and to "underscore" method on columns returned from the database. If set to false see
    1224. -
    1225. * {@link patio.Model#underscore}.
    1226. -
    1227. * @field
    1228. -
    1229. * @default false
    1230. -
    1231. * @type {Boolean}
    1232. -
    1233. */
    1234. -
    1235. underscore:function (underscore) {
    1236. -
    1237. 36 return this.__underscore;
    1238. +
    1239. /*Return if Im a new object*/
    1240. +
    1241. isNew:function () {
    1242. +
    1243. 9644 return this.__isNew;
    1244. +
    1245. },
    1246. +
    1247. /*Return if Im changed*/
    1248. +
    1249. isChanged:function () {
    1250. +
    1251. 0 return this.__isChanged;
    1252. },
    1253. -
    1254. /**@lends patio.Model*/
    1255. +
    1256. /**@lends patio.Model.prototype*/
    1257. -
    1258. /**
    1259. -
    1260. * The name of the table all instances of the this {@link patio.Model} use.
    1261. -
    1262. * @field
    1263. -
    1264. * @ignoreCode
    1265. -
    1266. * @type String
    1267. -
    1268. */
    1269. -
    1270. tableName:function () {
    1271. -
    1272. 6318 return this.__tableName;
    1273. +
    1274. primaryKey:function () {
    1275. +
    1276. 2568 return this._static.primaryKey;
    1277. },
    1278. -
    1279. /**
    1280. -
    1281. * The database all instances of this {@link patio.Model} use.
    1282. -
    1283. * @field
    1284. -
    1285. * @ignoreCode
    1286. -
    1287. * @type patio.Database
    1288. -
    1289. */
    1290. -
    1291. db:function () {
    1292. -
    1293. 46374 var db = this.__db;
    1294. -
    1295. 46374 if (!db) {
    1296. -
    1297. 86 db = this.__db = patio.defaultDatabase;
    1298. -
    1299. }
    1300. -
    1301. 46374 if (!db) {
    1302. -
    1303. 0 throw new ModelError("patio has not been connected to a database");
    1304. -
    1305. }
    1306. -
    1307. 46374 return db;
    1308. +
    1309. tableName:function () {
    1310. +
    1311. 40 return this._static.tableName;
    1312. },
    1313. -
    1314. /**
    1315. -
    1316. * A dataset to use to retrieve instances of this {@link patio.Model{ from the database. The dataset
    1317. -
    1318. * has the {@link patio.Dataset#rowCb} set to create instances of this model.
    1319. -
    1320. * @field
    1321. -
    1322. * @ignoreCode
    1323. -
    1324. * @type patio.Dataset
    1325. -
    1326. */
    1327. dataset:function () {
    1328. -
    1329. 4836 var ds = this.__dataset;
    1330. -
    1331. 4836 if (!ds) {
    1332. -
    1333. 84 ds = this.db.from(this.tableName);
    1334. -
    1335. 84 ds.rowCb = hitch(this, function (vals) {
    1336. -
    1337. 873 return this.load(vals);
    1338. -
    1339. });
    1340. -
    1341. 84 this.identifierInputMethod && (ds.identifierInputMethod = this.identifierInputMethod);
    1342. -
    1343. 84 this.identifierOutputMethod && (ds.identifierOutputMethod = this.identifierOutputMethod);
    1344. -
    1345. 84 this.__dataset = ds;
    1346. -
    1347. 4752 } else if (!ds.rowCb) {
    1348. -
    1349. 6 ds.rowCb = hitch(this, function rowCb(vals) {
    1350. -
    1351. 25 return this.load(vals);
    1352. -
    1353. });
    1354. -
    1355. }
    1356. -
    1357. 4836 return ds;
    1358. +
    1359. 3085 return this.__dataset || this._static.dataset;
    1360. },
    1361. -
    1362. /**
    1363. -
    1364. * A list of columns this models table contains.
    1365. -
    1366. * @field
    1367. -
    1368. * @ignoreCode
    1369. -
    1370. * @type String[]
    1371. -
    1372. -
    1373. */
    1374. -
    1375. columns:function () {
    1376. -
    1377. 1128 return this.__columns;
    1378. +
    1379. db:function () {
    1380. +
    1381. 28 return this._static.db;
    1382. },
    1383. -
    1384. /**
    1385. -
    1386. * The schema of this {@link patio.Model}'s table. See {@link patio.Database#schema} for details
    1387. -
    1388. * on the schema object.
    1389. -
    1390. * @field
    1391. -
    1392. * @ignoreCode
    1393. -
    1394. * @type Object
    1395. -
    1396. */
    1397. schema:function () {
    1398. -
    1399. 75103 if (this.synced) {
    1400. -
    1401. 75103 return this.__schema;
    1402. -
    1403. } else {
    1404. -
    1405. 0 throw new ModelError("Model has not been synced yet");
    1406. -
    1407. }
    1408. +
    1409. 75099 return this._static.schema;
    1410. },
    1411. -
    1412. /**
    1413. -
    1414. * The primaryKey column/s of this {@link patio.Model}
    1415. -
    1416. * @field
    1417. -
    1418. * @ignoreCode
    1419. -
    1420. */
    1421. -
    1422. primaryKey:function () {
    1423. -
    1424. 8221 if (this.synced) {
    1425. -
    1426. 8221 return this.__primaryKey.slice(0);
    1427. -
    1428. } else {
    1429. -
    1430. 0 throw new ModelError("Model has not been synced yet");
    1431. -
    1432. }
    1433. +
    1434. columns:function () {
    1435. +
    1436. 0 return this._static.columns;
    1437. },
    1438. -
    1439. /**
    1440. -
    1441. * A reference to the global {@link patio}.
    1442. -
    1443. * @field
    1444. -
    1445. * @ignoreCode
    1446. -
    1447. */
    1448. -
    1449. patio:function () {
    1450. -
    1451. 81 return patio || require("./index");
    1452. +
    1453. synced:function () {
    1454. +
    1455. 12780 return this._static.synced;
    1456. }
    1457. -
    1458. },
    1459. -
    1460. /**@ignore*/
    1461. -
    1462. setters:{
    1463. -
    1464. /**@lends patio.Model*/
    1465. -
    1466. /**@ignore*/
    1467. -
    1468. camelize:function (camelize) {
    1469. -
    1470. 26 camelize = camelize === true;
    1471. -
    1472. 26 if (camelize) {
    1473. -
    1474. 26 this.identifierOutputMethod = "camelize";
    1475. -
    1476. 26 this.identifierInputMethod = "underscore";
    1477. -
    1478. }
    1479. -
    1480. 26 this.__camelize = camelize;
    1481. -
    1482. 26 this.__underscore = !camelize;
    1483. +
    1484. }
    1485. +
    1486. },
    1487. -
    1488. },
    1489. -
    1490. /**@ignore*/
    1491. -
    1492. underscore:function (underscore) {
    1493. -
    1494. 1 underscore = underscore === true;
    1495. -
    1496. 1 if (underscore) {
    1497. -
    1498. 1 this.identifierOutputMethod = "underscore";
    1499. -
    1500. 1 this.identifierInputMethod = "camelize";
    1501. -
    1502. }
    1503. -
    1504. 1 this.__underscore = underscore;
    1505. -
    1506. 1 this.__camelize = !underscore;
    1507. +
    1508. static:{
    1509. +
    1510. /**
    1511. +
    1512. * @lends patio.Model
    1513. +
    1514. */
    1515. -
    1516. }
    1517. -
    1518. }
    1519. -
    1520. }
    1521. +
    1522. synced:false,
    1523. -
    1524. }).as(exports, "Model");
    1525. +
    1526. /**
    1527. +
    1528. * Set to false to prevent the emitting of an event on load
    1529. +
    1530. * @default true
    1531. +
    1532. */
    1533. +
    1534. emitOnLoad:true,
    1535. +
    1536. /**
    1537. +
    1538. * Set to false to prevent the emitting of an event on the setting of a column value
    1539. +
    1540. * @default true
    1541. +
    1542. */
    1543. +
    1544. emitOnColumnSet:true,
    1545. -
    1546. 1function checkAndAddDBToTable(db, table) {
    1547. -
    1548. 93 if (!table.contains(db)) {
    1549. -
    1550. 34 table.set(db, new HashTable());
    1551. -
    1552. }
    1553. -
    1554. }
    1555. -
    1556. /**@ignore*/
    1557. -
    1558. 1exports.create = function (name, supers, modelOptions) {
    1559. -
    1560. 93 if (!patio) {
    1561. -
    1562. 1 (patio = require("./index"));
    1563. -
    1564. 1 patio.on("disconnect", function () {
    1565. -
    1566. 40 MODELS.clear();
    1567. -
    1568. });
    1569. -
    1570. }
    1571. -
    1572. 93 var db, ds, tableName;
    1573. -
    1574. 93 var key, modelKey;
    1575. -
    1576. 93 if (isString(name)) {
    1577. -
    1578. 87 tableName = name;
    1579. -
    1580. 87 key = db = patio.defaultDatabase || "default";
    1581. -
    1582. 6 } else if (isInstanceOf(name, patio.Dataset)) {
    1583. -
    1584. 6 ds = name;
    1585. -
    1586. 6 tableName = ds.firstSourceAlias;
    1587. -
    1588. 6 key = db = ds.db;
    1589. -
    1590. }
    1591. -
    1592. 93 var hasSuper = false;
    1593. -
    1594. 93 if (isHash(supers) || isUndefinedOrNull(supers)) {
    1595. -
    1596. 90 modelOptions = supers;
    1597. -
    1598. 90 supers = [Model];
    1599. -
    1600. } else {
    1601. -
    1602. 3 supers = toArray(supers);
    1603. -
    1604. 3 supers = supers.map(function (sup) {
    1605. -
    1606. 3 return exports.getModel(sup, db);
    1607. -
    1608. });
    1609. -
    1610. 3 hasSuper = true;
    1611. -
    1612. }
    1613. +
    1614. /**
    1615. +
    1616. * Set to false to prevent empty strings from being type casted to null
    1617. +
    1618. * @default true
    1619. +
    1620. */
    1621. +
    1622. typecastEmptyStringToNull:true,
    1623. -
    1624. 93 var model;
    1625. -
    1626. 93 checkAndAddDBToTable(key, MODELS);
    1627. +
    1628. /**
    1629. +
    1630. * Set to false to prevent properties from being type casted when loaded from the database.
    1631. +
    1632. * See {@link patio.Database#typecastValue}
    1633. +
    1634. * @default true
    1635. +
    1636. */
    1637. +
    1638. typecastOnLoad:true,
    1639. -
    1640. 93 var DEFAULT_PROTO = {instance:{}, "static":{}};
    1641. -
    1642. 93 modelOptions = merge(DEFAULT_PROTO, modelOptions || {});
    1643. -
    1644. 93 modelOptions.instance._hooks = ["save", "update", "remove", "load"];
    1645. -
    1646. 93 modelOptions.instance.__hooks = {pre:{}, post:{}};
    1647. -
    1648. //Mixin the column setter/getters
    1649. -
    1650. 93 modelOptions["static"].synced = false;
    1651. -
    1652. 93 modelOptions["static"].__tableName = tableName;
    1653. -
    1654. 93 modelOptions["static"].__db = (db === "default" ? null : db);
    1655. -
    1656. 93 modelOptions["static"].__supers = hasSuper ? supers : [];
    1657. -
    1658. 93 modelOptions["static"].__dataset = ds;
    1659. -
    1660. 93 model = define(supers.concat(modelOptions.plugins || []).concat([AssociationPlugin]), modelOptions);
    1661. -
    1662. 93 ["pre", "post"].forEach(function (op) {
    1663. -
    1664. 186 var optionsOp = modelOptions[op];
    1665. -
    1666. 186 if (optionsOp) {
    1667. -
    1668. 0 for (var i in optionsOp) {
    1669. -
    1670. 0 model[op](i, optionsOp[i]);
    1671. -
    1672. }
    1673. -
    1674. }
    1675. -
    1676. });
    1677. -
    1678. 93 if (!(MODELS.get(key).contains(checkAndTransformName(name)))) {
    1679. -
    1680. 61 MODELS.get(key).set(name, model);
    1681. -
    1682. }
    1683. -
    1684. 93 return model;
    1685. -
    1686. };
    1687. +
    1688. /**
    1689. +
    1690. * Set to false to prevent properties from being type casted when manually set.
    1691. +
    1692. * See {@link patio.Database#typecastValue}
    1693. +
    1694. * @default true
    1695. +
    1696. */
    1697. +
    1698. typecastOnAssignment:true,
    1699. -
    1700. 1exports.syncModels = function (cb) {
    1701. -
    1702. 35 var ret = new Promise();
    1703. -
    1704. 35 serial(MODELS.entrySet.map(function (entry) {
    1705. -
    1706. 34 return function () {
    1707. -
    1708. 34 var value = entry.value;
    1709. -
    1710. 34 return serial(value.entrySet.map(function (m) {
    1711. -
    1712. 61 return hitch(m.value, "sync");
    1713. -
    1714. }));
    1715. -
    1716. };
    1717. -
    1718. })).then(hitchIgnore(ret, "callback", true), ret);
    1719. -
    1720. 35 if (isFunction(cb)) {
    1721. -
    1722. 0 ret.classic(cb);
    1723. -
    1724. }
    1725. -
    1726. 35 return ret.promise();
    1727. -
    1728. };
    1729. +
    1730. /**
    1731. +
    1732. * Set to false to prevent errors thrown while type casting a value from being propogated.
    1733. +
    1734. * @default true
    1735. +
    1736. */
    1737. +
    1738. raiseOnTypecastError:true,
    1739. -
    1740. 1var checkAndGetModel = function (db, name) {
    1741. -
    1742. 21198 var ret;
    1743. -
    1744. 21198 if (MODELS.contains(db)) {
    1745. -
    1746. 10600 ret = MODELS.get(db).get(checkAndTransformName(name));
    1747. -
    1748. }
    1749. -
    1750. 21198 return ret;
    1751. -
    1752. };
    1753. +
    1754. /**
    1755. +
    1756. * Set to false to allow the setting of primary keys.
    1757. +
    1758. * @default false
    1759. +
    1760. */
    1761. +
    1762. isRestrictedPrimaryKey:true,
    1763. +
    1764. /**
    1765. +
    1766. * Set to false to prevent models from using transactions when saving, deleting, or updating.
    1767. +
    1768. * This applies to the model associations also.
    1769. +
    1770. */
    1771. +
    1772. useTransactions:true,
    1773. -
    1774. 1exports.getModel = function (name, db) {
    1775. -
    1776. 10603 var ret = null;
    1777. -
    1778. 10603 if (isDefined(name)) {
    1779. -
    1780. 10603 !patio && (patio = require("./index"));
    1781. -
    1782. 10603 if (isFunction(name)) {
    1783. -
    1784. 3 ret = name;
    1785. -
    1786. } else {
    1787. -
    1788. 10600 if (!db && isInstanceOf(name, patio.Dataset)) {
    1789. -
    1790. 2 db = name.db;
    1791. -
    1792. }
    1793. -
    1794. 10600 var defaultDb = patio.defaultDatabase;
    1795. -
    1796. 10600 if (db) {
    1797. -
    1798. 10574 ret = checkAndGetModel(db, name);
    1799. -
    1800. 10574 if (!ret && db === defaultDb) {
    1801. -
    1802. 10572 ret = checkAndGetModel("default", name);
    1803. -
    1804. }
    1805. -
    1806. } else {
    1807. -
    1808. 26 db = patio.defaultDatabase;
    1809. -
    1810. 26 ret = checkAndGetModel(db, name);
    1811. -
    1812. 26 if (!ret) {
    1813. -
    1814. 26 ret = checkAndGetModel("default", name);
    1815. -
    1816. }
    1817. -
    1818. }
    1819. -
    1820. }
    1821. -
    1822. } else {
    1823. -
    1824. 0 ret = name;
    1825. -
    1826. }
    1827. -
    1828. 10603 if (isUndefinedOrNull(ret)) {
    1829. -
    1830. 0 throw new ModelError("Model " + name + " has not been registered with patio");
    1831. -
    1832. }
    1833. -
    1834. 10603 return ret;
    1835. -
    1836. };
    1837. +
    1838. /**
    1839. +
    1840. * See {@link patio.Dataset#identifierOutputMethod}
    1841. +
    1842. * @default null
    1843. +
    1844. */
    1845. +
    1846. identifierOutputMethod:null,
    1847. -
    -
    - -
    - - - - - -
    -
    associations/manyToMany.js
    -
    -
    - Coverage89.13 - SLOC309 - LOC138 - Missed15 -
    -
    -
    1. 1var comb = require("comb-proxy"),
    2. -
    3. define = comb.define,
    4. -
    5. isUndefined = comb.isUndefined,
    6. -
    7. isUndefinedOrNull = comb.isUndefinedOrNull,
    8. -
    9. isFunction = comb.isFunction,
    10. -
    11. isInstanceOf = comb.isInstanceOf,
    12. -
    13. sql = require("../sql").sql,
    14. -
    15. hitch = comb.hitch,
    16. -
    17. array = comb.array,
    18. -
    19. isBoolean = comb.isBoolean,
    20. -
    21. serial = comb.serial,
    22. -
    23. when = comb.when,
    24. -
    25. zip = array.zip,
    26. -
    27. Promise = comb.Promise,
    28. -
    29. PromiseList = comb.PromiseList,
    30. -
    31. hitchIgnore = comb.hitchIgnore,
    32. -
    33. OneToMany = require("./oneToMany"),
    34. -
    35. pluralize = comb.pluralize,
    36. -
    37. AssociationError = require("../errors").AssociationError;
    38. +
    39. /**
    40. +
    41. * See {@link patio.Dataset#identifierInputMethod}
    42. +
    43. * @default null
    44. +
    45. */
    46. +
    47. identifierInputMethod:null,
    48. -
    49. 1var LOGGER = comb.logger("comb.associations.ManyToMany");
    50. -
    51. /**
    52. -
    53. * @class Class to define a manyToMany association.
    54. -
    55. *
    56. -
    57. * </br>
    58. -
    59. * <b>NOT to be instantiated directly</b>
    60. -
    61. * Its just documented for reference.
    62. -
    63. *
    64. -
    65. * @name ManyToMany
    66. -
    67. * @augments patio.associations.OneToMany
    68. -
    69. * @memberOf patio.associations
    70. -
    71. *
    72. -
    73. * @param {String} options.joinTable the joinTable of the association.
    74. -
    75. *
    76. -
    77. *
    78. -
    79. * @property {String} joinTable the join table used in the relation.
    80. -
    81. * */
    82. -
    83. 1module.exports = define(OneToMany, {
    84. -
    85. instance:{
    86. -
    87. /**@lends patio.associations.ManyToMany.prototype*/
    88. -
    89. type:"manyToMany",
    90. +
    91. /**
    92. +
    93. * Set to false to prevent the reload of a model after saving.
    94. +
    95. * @default true
    96. +
    97. */
    98. +
    99. reloadOnSave:true,
    100. -
    101. _fetchMethod:"all",
    102. +
    103. /**
    104. +
    105. * Columns that should be restriced when setting values through the {@link patio.Model#set} method.
    106. +
    107. *
    108. +
    109. */
    110. +
    111. restrictedColumns:null,
    112. -
    113. supportsStringKey:false,
    114. +
    115. /**
    116. +
    117. * Set to false to prevent the reload of a model after updating.
    118. +
    119. * @default true
    120. +
    121. */
    122. +
    123. reloadOnUpdate:true,
    124. -
    125. supportsCompositeKey:false,
    126. +
    127. __camelize:false,
    128. +
    129. +
    130. __underscore:false,
    131. -
    132. _filter:function (parent) {
    133. -
    134. 326 var keys = this._getAssociationKey(parent);
    135. -
    136. 326 var options = this.__opts || {};
    137. -
    138. 326 var ds;
    139. -
    140. 326 if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {
    141. -
    142. 0 ds = ds.apply(parent, [parent]);
    143. -
    144. }
    145. -
    146. 326 if (!ds) {
    147. -
    148. 326 ds = this.model.dataset;
    149. -
    150. 326 ds = ds.select(ds.firstSourceAlias.all()).naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {
    151. -
    152. 326 return sql.stringToIdentifier(k);
    153. -
    154. })).concat(zip(keys[0], this.parentPrimaryKey.map(function (k) {
    155. -
    156. 326 return parent[k];
    157. -
    158. }))));
    159. -
    160. 326 var recip = this.model._findAssociation(this);
    161. -
    162. 326 if (recip) {
    163. -
    164. 326 recip = recip[1];
    165. -
    166. }
    167. -
    168. 326 ds.rowCb = hitch(this, function (item) {
    169. -
    170. 340 var ret = new Promise();
    171. -
    172. 340 var model = this._toModel(item, true);
    173. -
    174. 340 if (recip) {
    175. -
    176. 340 recip.__setValue(model, parent);
    177. -
    178. }
    179. -
    180. //call hook to finish other model associations
    181. -
    182. 340 model._hook("post", "load").then(hitch(this, function () {
    183. -
    184. 340 ret.callback(model);
    185. -
    186. }), ret);
    187. -
    188. 340 return ret.promise();
    189. -
    190. });
    191. +
    192. __columns:null,
    193. -
    194. }
    195. +
    196. __schema:null,
    197. -
    198. 326 return this._setDatasetOptions(ds);
    199. -
    200. },
    201. +
    202. __primaryKey:null,
    203. -
    204. _setAssociationKeys:function (parent, model, val) {
    205. -
    206. 534 var keys = this._getAssociationKey(parent),
    207. -
    208. leftKey = keys[0],
    209. -
    210. parentPk = this.parentPrimaryKey;
    211. -
    212. 534 if (!(leftKey && leftKey.length === parentPk.length)) {
    213. -
    214. 0 throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);
    215. +
    216. __dataset:null,
    217. +
    218. +
    219. __db:null,
    220. +
    221. +
    222. __tableName:null,
    223. +
    224. +
    225. +
    226. /**
    227. +
    228. * The table that this Model represents.
    229. +
    230. * <b>READ ONLY</b>
    231. +
    232. */
    233. +
    234. table:null,
    235. +
    236. +
    237. /**
    238. +
    239. * patio - read only
    240. +
    241. *
    242. +
    243. * @type patio
    244. +
    245. */
    246. +
    247. patio:null,
    248. +
    249. +
    250. init:function () {
    251. +
    252. 92 var emitter = new EventEmitter();
    253. +
    254. 92 ["addListener", "on", "once", "removeListener",
    255. +
    256. "removeAllListeners", "setMaxListeners", "listeners", "emit"].forEach(function (name) {
    257. +
    258. 736 this[name] = emitter[name].bind(emitter);
    259. +
    260. }, this);
    261. +
    262. 92 if (this.__tableName) {
    263. +
    264. 91 this._setTableName(this.__tableName);
    265. }
    266. -
    267. 534 for (var i = 0; i < leftKey.length; i++) {
    268. -
    269. 534 model[leftKey[i]] = !isUndefined(val) ? val : parent[parentPk[i]];
    270. +
    271. 92 if (this.__db) {
    272. +
    273. 7 this._setDb(this.__db);
    274. }
    275. },
    276. -
    277. __createJoinTableInsertRemoveQuery:function (model, item) {
    278. -
    279. 176 var q = {};
    280. -
    281. 176 var keys = this._getAssociationKey(model),
    282. -
    283. leftKey = keys[0],
    284. -
    285. rightKey = keys[1],
    286. -
    287. parentPk = this.parentPrimaryKey,
    288. -
    289. modelPk = this.modelPrimaryKey;
    290. -
    291. 176 if (!(leftKey && leftKey.length === parentPk.length)) {
    292. -
    293. 0 throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);
    294. -
    295. }
    296. -
    297. 176 if (!(rightKey && rightKey.length === modelPk.length)) {
    298. -
    299. 0 throw new AssociationError("Invalid rightKey for " + this.name + " : " + rightKey);
    300. -
    301. }
    302. -
    303. 176 for (var i = 0; i < leftKey.length; i++) {
    304. -
    305. 176 q[leftKey[i]] = model[parentPk[i]];
    306. +
    307. sync:function (cb) {
    308. +
    309. 3968 var ret = new Promise();
    310. +
    311. 3968 if (!this.synced) {
    312. +
    313. 93 var db = this.db, tableName = this.tableName, supers = this.__supers;
    314. +
    315. 93 db.schema(tableName).then(hitch(this, function (schema) {
    316. +
    317. 93 if (!this.synced && schema) {
    318. +
    319. 93 this._setSchema(schema);
    320. +
    321. 93 if (supers && supers.length) {
    322. +
    323. 3 new PromiseList(supers.map(function (sup) {
    324. +
    325. 3 return sup.sync();
    326. +
    327. })).then(hitch(this, function () {
    328. +
    329. 3 this.synced = true;
    330. +
    331. 3 supers.forEach(this.inherits, this);
    332. +
    333. 3 ret.callback(this);
    334. +
    335. }), ret);
    336. +
    337. } else {
    338. +
    339. 90 this.synced = true;
    340. +
    341. 90 ret.callback(this);
    342. +
    343. }
    344. +
    345. 93 this.emit("sync", this);
    346. +
    347. } else {
    348. +
    349. 0 var error = new ModelError("Unable to find schema for " + tableName);
    350. +
    351. 0 this.emit("error", error);
    352. +
    353. 0 ret.errback(error);
    354. +
    355. }
    356. +
    357. }), ret);
    358. +
    359. } else {
    360. +
    361. 3875 ret.callback(this);
    362. }
    363. -
    364. 176 for (i = 0; i < rightKey.length; i++) {
    365. -
    366. 176 q[rightKey[i]] = item[modelPk[i]];
    367. +
    368. 3968 if (isFunction(cb)) {
    369. +
    370. 0 ret.classic(cb);
    371. }
    372. -
    373. 176 return q;
    374. +
    375. 3968 return ret.promise();
    376. },
    377. -
    378. _preRemove:function (next, model) {
    379. -
    380. 202 if (this.isOwner && !this.isCascading) {
    381. -
    382. 202 var q = {};
    383. -
    384. 202 this._setAssociationKeys(model, q);
    385. -
    386. 202 this.joinTable.where(q).remove().classic(next);
    387. -
    388. } else {
    389. -
    390. 0 next();
    391. -
    392. }
    393. +
    394. /**
    395. +
    396. * Stub for plugins to notified of model inheritance
    397. +
    398. *
    399. +
    400. * @param {patio.Model} model a model class to inherit from
    401. +
    402. */
    403. +
    404. inherits:function (model) {
    405. },
    406. -
    407. addAssociation:function (item, model, reload) {
    408. -
    409. 260 reload = isBoolean(reload) ? reload : false;
    410. -
    411. 260 var ret = new Promise().callback(model);
    412. -
    413. 260 if (!isUndefinedOrNull(item)) {
    414. -
    415. 260 if (!model.isNew) {
    416. -
    417. 148 item = this._toModel(item);
    418. -
    419. 148 var loaded = this.associationLoaded(model);
    420. -
    421. 148 var recip = this.model._findAssociation(this), save = item.isNew;
    422. -
    423. 148 ret = model._checkTransaction(hitch(this, function () {
    424. -
    425. 148 var joinTable = this.joinTable, ret = new Promise();
    426. -
    427. 148 serial([
    428. -
    429. function () {
    430. -
    431. 148 return save ? item.save() : null;
    432. -
    433. },
    434. -
    435. function () {
    436. -
    437. 148 return joinTable.insert(this.__createJoinTableInsertRemoveQuery(model, item));
    438. -
    439. }.bind(this),
    440. -
    441. function () {
    442. -
    443. 148 if (recip) {
    444. -
    445. 148 recip[1].__setValue(item, [model]);
    446. -
    447. }
    448. -
    449. },
    450. -
    451. function () {
    452. -
    453. 148 if (loaded && reload) {
    454. -
    455. 3 return this.parent._reloadAssociationsForType(this.type, this.model, model);
    456. -
    457. } else {
    458. -
    459. 145 return model;
    460. -
    461. }
    462. -
    463. }.bind(this)
    464. -
    465. ]).then(hitchIgnore(ret, "callback", model), ret);
    466. -
    467. 148 return ret.promise();
    468. -
    469. }));
    470. -
    471. } else {
    472. -
    473. 112 item = this._toModel(item);
    474. -
    475. 112 var items = this.getAssociation(model);
    476. -
    477. 112 if (isUndefinedOrNull(items)) {
    478. -
    479. 39 this.__setValue(model, [item]);
    480. -
    481. } else {
    482. -
    483. 73 items.push(item);
    484. -
    485. }
    486. -
    487. }
    488. -
    489. }
    490. -
    491. 260 return ret.promise();
    492. +
    493. /**
    494. +
    495. * Create a new model initialized with the specified values.
    496. +
    497. *
    498. +
    499. * @param {Object} values the values to initialize the model with.
    500. +
    501. *
    502. +
    503. * @returns {Model} instantiated model initialized with the values passed in.
    504. +
    505. */
    506. +
    507. create:function (values) {
    508. +
    509. //load an object from an object
    510. +
    511. 0 return new this(values, false);
    512. },
    513. -
    514. removeItem:function (item, model, remove, reload) {
    515. -
    516. 56 reload = isBoolean(reload) ? reload : false;
    517. -
    518. 56 remove = isBoolean(remove) ? remove : false;
    519. -
    520. 56 var ret = new Promise().callback(model);
    521. -
    522. 56 if (!isUndefinedOrNull(item)) {
    523. -
    524. 56 if (!model.isNew) {
    525. -
    526. 56 if (isInstanceOf(item, this.model) && !item.isNew) {
    527. -
    528. 56 var loaded = this.associationLoaded(model);
    529. -
    530. 56 remove = remove && !item.isNew;
    531. -
    532. 56 ret = new Promise();
    533. -
    534. 56 model._checkTransaction(hitch(this, function () {
    535. -
    536. 56 return remove ? item.remove() : this.joinTable.where(this.__createJoinTableInsertRemoveQuery(model, item)).remove();
    537. -
    538. })).then(hitch(this, function () {
    539. -
    540. 56 if (loaded && reload) {
    541. -
    542. 18 return this.parent._reloadAssociationsForType(this.type, this.model, model).then(ret);
    543. -
    544. } else {
    545. -
    546. 38 ret.callback(model);
    547. -
    548. }
    549. -
    550. }), ret);
    551. -
    552. 56 return ret.promise();
    553. -
    554. }
    555. +
    556. load:function (vals) {
    557. +
    558. 924 var ret = new Promise();
    559. +
    560. //sync our model
    561. +
    562. 924 this.sync().then(hitch(this, function () {
    563. +
    564. 924 var m = new this(vals, true);
    565. +
    566. //call the hooks!
    567. +
    568. 924 m._hook("post", "load").then(function () {
    569. +
    570. 924 ret.callback(m);
    571. +
    572. }, ret);
    573. +
    574. }), ret);
    575. +
    576. 924 return ret.promise();
    577. +
    578. },
    579. +
    580. +
    581. _checkTransaction:function (opts, cb) {
    582. +
    583. 2892 if (isFunction(opts)) {
    584. +
    585. 639 cb = opts;
    586. +
    587. 639 opts = {};
    588. +
    589. } else {
    590. +
    591. 2253 opts = opts || {};
    592. +
    593. }
    594. +
    595. 2892 var ret = new Promise(), retVal = null, errored = false;
    596. +
    597. 2892 this.sync().then(hitch(this, function () {
    598. +
    599. 2892 if (this.useTransaction(opts)) {
    600. +
    601. 2892 this.db.transaction(opts, hitch(this, function () {
    602. +
    603. 2892 return when(cb()).then(function () {
    604. +
    605. 2848 retVal = argsToArray(arguments);
    606. +
    607. }, function () {
    608. +
    609. 44 retVal = argsToArray(arguments);
    610. +
    611. 44 errored = true;
    612. +
    613. });
    614. +
    615. })).then(hitch(this, function () {
    616. +
    617. 2848 ret[errored ? "errback" : "callback"].apply(ret, retVal);
    618. +
    619. }), hitch(this, function () {
    620. +
    621. 44 if (errored) {
    622. +
    623. 44 ret.errback.apply(ret, retVal);
    624. +
    625. } else {
    626. +
    627. 0 ret.errback.apply(ret, arguments);
    628. +
    629. }
    630. +
    631. }));
    632. } else {
    633. -
    634. 0 item = this._toModel(item);
    635. -
    636. 0 var items = this.getAssociation(model), index;
    637. -
    638. 0 if (!isUndefinedOrNull(items) && (index = items.indexOf(item)) !== -1) {
    639. -
    640. 0 items.splice(index, 1);
    641. -
    642. }
    643. +
    644. 0 when(cb()).then(ret);
    645. }
    646. +
    647. }), ret);
    648. +
    649. 2892 return ret.promise();
    650. +
    651. },
    652. +
    653. +
    654. /**
    655. +
    656. * @private
    657. +
    658. * Returns a boolean indicating whether or not to use a transaction.
    659. +
    660. * @param {Object} [opts] set a transaction property to override the {@link patio.Model#useTransaction}.
    661. +
    662. */
    663. +
    664. useTransaction:function (opts) {
    665. +
    666. 2892 opts = opts || {};
    667. +
    668. 2892 return isBoolean(opts.transaction) ? opts.transaction === true : this.useTransactions === true;
    669. +
    670. },
    671. +
    672. +
    673. _setDataset:function (ds) {
    674. +
    675. 3 this.__dataset = ds;
    676. +
    677. 3 if (ds.db) {
    678. +
    679. 3 this._setDb(ds.db);
    680. }
    681. -
    682. 0 return ret.promise();
    683. },
    684. -
    685. removeAllItems:function (model, remove) {
    686. -
    687. 4 remove = isBoolean(remove) ? remove : false;
    688. -
    689. 4 var ret = new Promise();
    690. -
    691. 4 if (!model.isNew) {
    692. -
    693. 4 var q = {}, removeQ = {};
    694. -
    695. 4 this._setAssociationKeys(model, q);
    696. -
    697. 4 this._setAssociationKeys(model, removeQ, null);
    698. -
    699. 4 var loaded = this.associationLoaded(model);
    700. -
    701. 4 return model._checkTransaction(hitch(this, function () {
    702. -
    703. 4 var ds = this.model.dataset, ret = new Promise();
    704. -
    705. 4 when(remove ? this._filter(model).forEach(function (m) {
    706. -
    707. 6 return m.remove();
    708. -
    709. }) : this.joinTable.filter(q).update(removeQ)).then(function () {
    710. -
    711. 4 if (loaded) {
    712. -
    713. 2 this.parent._reloadAssociationsForType(this.type, this.model, model)
    714. -
    715. .then(hitchIgnore(ret, "callback", model), ret);
    716. -
    717. } else {
    718. -
    719. 2 ret.callback(model);
    720. -
    721. }
    722. -
    723. }.bind(this), ret);
    724. -
    725. 4 return ret.promise();
    726. -
    727. }));
    728. -
    729. } else {
    730. -
    731. //todo we may want to check if any of the items were previously saved items;
    732. -
    733. 0 this._clearAssociations(model);
    734. -
    735. 0 ret.callback(model);
    736. +
    737. _setDb:function (db) {
    738. +
    739. 10 this.__db = db;
    740. +
    741. },
    742. +
    743. +
    744. _setTableName:function (name) {
    745. +
    746. 91 this.__tableName = name;
    747. +
    748. },
    749. +
    750. +
    751. _setColumns:function (cols) {
    752. +
    753. 96 var proto = this.prototype;
    754. +
    755. 96 if (this.__columns) {
    756. +
    757. 6 this.__columns.forEach(function (name) {
    758. +
    759. 16 delete proto[name];
    760. +
    761. });
    762. }
    763. -
    764. 0 return ret.promise();
    765. +
    766. 96 this.__columns = cols;
    767. +
    768. 96 cols.forEach(function (name) {
    769. +
    770. 779 this._defineColumnSetter(name);
    771. +
    772. 779 this._defineColumnGetter(name);
    773. +
    774. }, this);
    775. +
    776. },
    777. +
    778. +
    779. _setPrimaryKey:function (pks) {
    780. +
    781. 99 this.__primaryKey = pks || [];
    782. +
    783. },
    784. +
    785. +
    786. _setSchema:function (schema) {
    787. +
    788. 96 var columns = [];
    789. +
    790. 96 var pks = [];
    791. +
    792. 96 for (var i in schema) {
    793. +
    794. 779 var col = schema[i];
    795. +
    796. 779 var name = applyColumnTransformMethod(i, this.identifierOutputMethod);
    797. +
    798. 779 schema[name] = col;
    799. +
    800. 779 columns.push(name);
    801. +
    802. 779 col.primaryKey && pks.push(name);
    803. +
    804. }
    805. +
    806. 96 this.__schema = schema;
    807. +
    808. 96 this._setPrimaryKey(pks);
    809. +
    810. 96 this._setColumns(columns);
    811. +
    812. },
    813. +
    814. +
    815. _defineColumnSetter:function (name) {
    816. +
    817. /*Adds a setter to an object*/
    818. +
    819. 779 this.prototype.__defineSetter__(name, function (val) {
    820. +
    821. 7556 this._setColumnValue(name, val);
    822. +
    823. });
    824. +
    825. },
    826. +
    827. +
    828. _defineColumnGetter:function (name) {
    829. +
    830. 779 this.prototype.__defineGetter__(name, function () {
    831. +
    832. 5427 return this._getColumnValue(name);
    833. +
    834. });
    835. },
    836. +
    837. /**
    838. +
    839. * @ignore
    840. +
    841. */
    842. getters:{
    843. +
    844. /**@lends patio.Model*/
    845. -
    846. select:function () {
    847. -
    848. 326 return this.__select;
    849. -
    850. },
    851. +
    852. /**
    853. +
    854. * Set to true if this models column names should be use the "underscore" method when sending
    855. +
    856. * keys to the database and to "camelize" method on columns returned from the database. If set to false see
    857. +
    858. * {@link patio.Model#underscore}.
    859. +
    860. * @field
    861. +
    862. * @default false
    863. +
    864. * @type {Boolean}
    865. +
    866. */
    867. +
    868. camelize:function (camelize) {
    869. +
    870. 10686 return this.__camelize;
    871. -
    872. defaultLeftKey:function () {
    873. -
    874. 1740 return this.__opts.leftKey || this.parent.tableName + "Id";
    875. },
    876. -
    877. defaultRightKey:function () {
    878. -
    879. 1740 return this.__opts.rightKey || this.model.tableName + "Id";
    880. -
    881. },
    882. +
    883. /**
    884. +
    885. * Set to true if this models column names should be use the "camelize" method when sending
    886. +
    887. * keys to the database and to "underscore" method on columns returned from the database. If set to false see
    888. +
    889. * {@link patio.Model#underscore}.
    890. +
    891. * @field
    892. +
    893. * @default false
    894. +
    895. * @type {Boolean}
    896. +
    897. */
    898. +
    899. underscore:function (underscore) {
    900. +
    901. 36 return this.__underscore;
    902. -
    903. parentPrimaryKey:function () {
    904. -
    905. 1036 return this.__opts.leftPrimaryKey || this.parent.primaryKey;
    906. },
    907. -
    908. modelPrimaryKey:function () {
    909. -
    910. 502 return this.__opts.rightPrimaryKey || this.model.primaryKey;
    911. -
    912. },
    913. +
    914. /**@lends patio.Model*/
    915. -
    916. joinTableName:function () {
    917. -
    918. 344 if (!this._joinTable) {
    919. -
    920. 18 var options = this.__opts;
    921. -
    922. 18 var joinTable = options.joinTable;
    923. -
    924. 18 if (isUndefined(joinTable)) {
    925. -
    926. 18 var defaultJoinTable = this.defaultJoinTable;
    927. -
    928. 18 if (isUndefined(defaultJoinTable)) {
    929. -
    930. 0 throw new Error("Unable to determine jointable for " + this.name);
    931. -
    932. } else {
    933. -
    934. 18 this._joinTable = defaultJoinTable;
    935. -
    936. }
    937. -
    938. } else {
    939. -
    940. 0 this._joinTable = joinTable;
    941. -
    942. }
    943. -
    944. }
    945. -
    946. 344 return this._joinTable;
    947. +
    948. /**
    949. +
    950. * The name of the table all instances of the this {@link patio.Model} use.
    951. +
    952. * @field
    953. +
    954. * @ignoreCode
    955. +
    956. * @type String
    957. +
    958. */
    959. +
    960. tableName:function () {
    961. +
    962. 6318 return this.__tableName;
    963. },
    964. -
    965. //returns our join table model
    966. -
    967. joinTable:function () {
    968. -
    969. 380 if (!this.__joinTableDataset) {
    970. -
    971. 18 var ds = this.__joinTableDataset = this.model.dataset.db.from(this.joinTableName), model = this.model, options = this.__opts;
    972. -
    973. 18 var identifierInputMethod = isUndefined(options.identifierInputMethod) ? model.identifierInputMethod : options.identifierInputMethod,
    974. -
    975. identifierOutputMethod = isUndefined(options.identifierOutputMethod) ? model.identifierOutputMethod : options.identifierOutputMethod;
    976. -
    977. 18 if (identifierInputMethod) {
    978. -
    979. 14 ds.identifierInputMethod = identifierInputMethod;
    980. -
    981. }
    982. -
    983. 18 if (identifierOutputMethod) {
    984. -
    985. 14 ds.identifierOutputMethod = identifierOutputMethod;
    986. -
    987. }
    988. +
    989. /**
    990. +
    991. * The database all instances of this {@link patio.Model} use.
    992. +
    993. * @field
    994. +
    995. * @ignoreCode
    996. +
    997. * @type patio.Database
    998. +
    999. */
    1000. +
    1001. db:function () {
    1002. +
    1003. 46374 var db = this.__db;
    1004. +
    1005. 46374 if (!db) {
    1006. +
    1007. 86 db = this.__db = patio.defaultDatabase;
    1008. }
    1009. -
    1010. 380 return this.__joinTableDataset;
    1011. +
    1012. 46374 if (!db) {
    1013. +
    1014. 0 throw new ModelError("patio has not been connected to a database");
    1015. +
    1016. }
    1017. +
    1018. 46374 return db;
    1019. },
    1020. -
    1021. defaultJoinTable:function () {
    1022. -
    1023. 18 var ret;
    1024. -
    1025. 18 var recip = this.model._findAssociation(this);
    1026. -
    1027. 18 if (recip && recip.length) {
    1028. -
    1029. 18 var names = [pluralize(this._model), pluralize(recip[1]._model)].sort();
    1030. -
    1031. 18 names[1] = names[1].charAt(0).toUpperCase() + names[1].substr(1);
    1032. -
    1033. 18 ret = names.join("");
    1034. +
    1035. /**
    1036. +
    1037. * A dataset to use to retrieve instances of this {@link patio.Model{ from the database. The dataset
    1038. +
    1039. * has the {@link patio.Dataset#rowCb} set to create instances of this model.
    1040. +
    1041. * @field
    1042. +
    1043. * @ignoreCode
    1044. +
    1045. * @type patio.Dataset
    1046. +
    1047. */
    1048. +
    1049. dataset:function () {
    1050. +
    1051. 4836 var ds = this.__dataset;
    1052. +
    1053. 4836 if (!ds) {
    1054. +
    1055. 84 ds = this.db.from(this.tableName);
    1056. +
    1057. 84 ds.rowCb = hitch(this, function (vals) {
    1058. +
    1059. 873 return this.load(vals);
    1060. +
    1061. });
    1062. +
    1063. 84 this.identifierInputMethod && (ds.identifierInputMethod = this.identifierInputMethod);
    1064. +
    1065. 84 this.identifierOutputMethod && (ds.identifierOutputMethod = this.identifierOutputMethod);
    1066. +
    1067. 84 this.__dataset = ds;
    1068. +
    1069. 4752 } else if (!ds.rowCb) {
    1070. +
    1071. 6 ds.rowCb = hitch(this, function rowCb(vals) {
    1072. +
    1073. 25 return this.load(vals);
    1074. +
    1075. });
    1076. }
    1077. -
    1078. 18 return ret;
    1079. +
    1080. 4836 return ds;
    1081. +
    1082. },
    1083. +
    1084. +
    1085. /**
    1086. +
    1087. * A list of columns this models table contains.
    1088. +
    1089. * @field
    1090. +
    1091. * @ignoreCode
    1092. +
    1093. * @type String[]
    1094. +
    1095. +
    1096. */
    1097. +
    1098. columns:function () {
    1099. +
    1100. 1128 return this.__columns;
    1101. +
    1102. },
    1103. +
    1104. +
    1105. /**
    1106. +
    1107. * The schema of this {@link patio.Model}'s table. See {@link patio.Database#schema} for details
    1108. +
    1109. * on the schema object.
    1110. +
    1111. * @field
    1112. +
    1113. * @ignoreCode
    1114. +
    1115. * @type Object
    1116. +
    1117. */
    1118. +
    1119. schema:function () {
    1120. +
    1121. 75103 if (this.synced) {
    1122. +
    1123. 75103 return this.__schema;
    1124. +
    1125. } else {
    1126. +
    1127. 0 throw new ModelError("Model has not been synced yet");
    1128. +
    1129. }
    1130. +
    1131. },
    1132. +
    1133. +
    1134. /**
    1135. +
    1136. * The primaryKey column/s of this {@link patio.Model}
    1137. +
    1138. * @field
    1139. +
    1140. * @ignoreCode
    1141. +
    1142. */
    1143. +
    1144. primaryKey:function () {
    1145. +
    1146. 8221 if (this.synced) {
    1147. +
    1148. 8221 return this.__primaryKey.slice(0);
    1149. +
    1150. } else {
    1151. +
    1152. 0 throw new ModelError("Model has not been synced yet");
    1153. +
    1154. }
    1155. +
    1156. },
    1157. +
    1158. +
    1159. /**
    1160. +
    1161. * A reference to the global {@link patio}.
    1162. +
    1163. * @field
    1164. +
    1165. * @ignoreCode
    1166. +
    1167. */
    1168. +
    1169. patio:function () {
    1170. +
    1171. 81 return patio || require("./index");
    1172. +
    1173. }
    1174. +
    1175. },
    1176. +
    1177. +
    1178. /**@ignore*/
    1179. +
    1180. setters:{
    1181. +
    1182. /**@lends patio.Model*/
    1183. +
    1184. /**@ignore*/
    1185. +
    1186. camelize:function (camelize) {
    1187. +
    1188. 26 camelize = camelize === true;
    1189. +
    1190. 26 if (camelize) {
    1191. +
    1192. 26 this.identifierOutputMethod = "camelize";
    1193. +
    1194. 26 this.identifierInputMethod = "underscore";
    1195. +
    1196. }
    1197. +
    1198. 26 this.__camelize = camelize;
    1199. +
    1200. 26 this.__underscore = !camelize;
    1201. +
    1202. +
    1203. },
    1204. +
    1205. /**@ignore*/
    1206. +
    1207. underscore:function (underscore) {
    1208. +
    1209. 1 underscore = underscore === true;
    1210. +
    1211. 1 if (underscore) {
    1212. +
    1213. 1 this.identifierOutputMethod = "underscore";
    1214. +
    1215. 1 this.identifierInputMethod = "camelize";
    1216. +
    1217. }
    1218. +
    1219. 1 this.__underscore = underscore;
    1220. +
    1221. 1 this.__camelize = !underscore;
    1222. +
    1223. }
    1224. }
    1225. }
    1226. -
    1227. });
    1228. +
    1229. +
    1230. }).as(exports, "Model");
    1231. +
    1232. +
    1233. +
    1234. 1function checkAndAddDBToTable(db, table) {
    1235. +
    1236. 93 if (!table.contains(db)) {
    1237. +
    1238. 34 table.set(db, new HashTable());
    1239. +
    1240. }
    1241. +
    1242. }
    1243. +
    1244. +
    1245. /**@ignore*/
    1246. +
    1247. 1exports.create = function (name, supers, modelOptions) {
    1248. +
    1249. 93 if (!patio) {
    1250. +
    1251. 1 (patio = require("./index"));
    1252. +
    1253. 1 patio.on("disconnect", function () {
    1254. +
    1255. 40 MODELS.clear();
    1256. +
    1257. });
    1258. +
    1259. }
    1260. +
    1261. 93 var db, ds, tableName;
    1262. +
    1263. 93 var key, modelKey;
    1264. +
    1265. 93 if (isString(name)) {
    1266. +
    1267. 87 tableName = name;
    1268. +
    1269. 87 key = db = patio.defaultDatabase || "default";
    1270. +
    1271. 6 } else if (isInstanceOf(name, patio.Dataset)) {
    1272. +
    1273. 6 ds = name;
    1274. +
    1275. 6 tableName = ds.firstSourceAlias;
    1276. +
    1277. 6 key = db = ds.db;
    1278. +
    1279. }
    1280. +
    1281. 93 var hasSuper = false;
    1282. +
    1283. 93 if (isHash(supers) || isUndefinedOrNull(supers)) {
    1284. +
    1285. 90 modelOptions = supers;
    1286. +
    1287. 90 supers = [Model];
    1288. +
    1289. } else {
    1290. +
    1291. 3 supers = toArray(supers);
    1292. +
    1293. 3 supers = supers.map(function (sup) {
    1294. +
    1295. 3 return exports.getModel(sup, db);
    1296. +
    1297. });
    1298. +
    1299. 3 hasSuper = true;
    1300. +
    1301. }
    1302. +
    1303. +
    1304. 93 var model;
    1305. +
    1306. 93 checkAndAddDBToTable(key, MODELS);
    1307. +
    1308. +
    1309. 93 var DEFAULT_PROTO = {instance:{}, "static":{}};
    1310. +
    1311. 93 modelOptions = merge(DEFAULT_PROTO, modelOptions || {});
    1312. +
    1313. 93 modelOptions.instance._hooks = ["save", "update", "remove", "load"];
    1314. +
    1315. 93 modelOptions.instance.__hooks = {pre:{}, post:{}};
    1316. +
    1317. //Mixin the column setter/getters
    1318. +
    1319. 93 modelOptions["static"].synced = false;
    1320. +
    1321. 93 modelOptions["static"].__tableName = tableName;
    1322. +
    1323. 93 modelOptions["static"].__db = (db === "default" ? null : db);
    1324. +
    1325. 93 modelOptions["static"].__supers = hasSuper ? supers : [];
    1326. +
    1327. 93 modelOptions["static"].__dataset = ds;
    1328. +
    1329. 93 model = define(supers.concat(modelOptions.plugins || []).concat([AssociationPlugin]), modelOptions);
    1330. +
    1331. 93 ["pre", "post"].forEach(function (op) {
    1332. +
    1333. 186 var optionsOp = modelOptions[op];
    1334. +
    1335. 186 if (optionsOp) {
    1336. +
    1337. 0 for (var i in optionsOp) {
    1338. +
    1339. 0 model[op](i, optionsOp[i]);
    1340. +
    1341. }
    1342. +
    1343. }
    1344. +
    1345. });
    1346. +
    1347. 93 if (!(MODELS.get(key).contains(checkAndTransformName(name)))) {
    1348. +
    1349. 61 MODELS.get(key).set(name, model);
    1350. +
    1351. }
    1352. +
    1353. 93 return model;
    1354. +
    1355. };
    1356. +
    1357. +
    1358. 1exports.syncModels = function (cb) {
    1359. +
    1360. 35 var ret = new Promise();
    1361. +
    1362. 35 serial(MODELS.entrySet.map(function (entry) {
    1363. +
    1364. 34 return function () {
    1365. +
    1366. 34 var value = entry.value;
    1367. +
    1368. 34 return serial(value.entrySet.map(function (m) {
    1369. +
    1370. 61 return hitch(m.value, "sync");
    1371. +
    1372. }));
    1373. +
    1374. };
    1375. +
    1376. })).then(hitchIgnore(ret, "callback", true), ret);
    1377. +
    1378. 35 if (isFunction(cb)) {
    1379. +
    1380. 0 ret.classic(cb);
    1381. +
    1382. }
    1383. +
    1384. 35 return ret.promise();
    1385. +
    1386. };
    1387. +
    1388. +
    1389. 1var checkAndGetModel = function (db, name) {
    1390. +
    1391. 21198 var ret;
    1392. +
    1393. 21198 if (MODELS.contains(db)) {
    1394. +
    1395. 10600 ret = MODELS.get(db).get(checkAndTransformName(name));
    1396. +
    1397. }
    1398. +
    1399. 21198 return ret;
    1400. +
    1401. };
    1402. +
    1403. +
    1404. +
    1405. 1exports.getModel = function (name, db) {
    1406. +
    1407. 10603 var ret = null;
    1408. +
    1409. 10603 if (isDefined(name)) {
    1410. +
    1411. 10603 !patio && (patio = require("./index"));
    1412. +
    1413. 10603 if (isFunction(name)) {
    1414. +
    1415. 3 ret = name;
    1416. +
    1417. } else {
    1418. +
    1419. 10600 if (!db && isInstanceOf(name, patio.Dataset)) {
    1420. +
    1421. 2 db = name.db;
    1422. +
    1423. }
    1424. +
    1425. 10600 var defaultDb = patio.defaultDatabase;
    1426. +
    1427. 10600 if (db) {
    1428. +
    1429. 10574 ret = checkAndGetModel(db, name);
    1430. +
    1431. 10574 if (!ret && db === defaultDb) {
    1432. +
    1433. 10572 ret = checkAndGetModel("default", name);
    1434. +
    1435. }
    1436. +
    1437. } else {
    1438. +
    1439. 26 db = patio.defaultDatabase;
    1440. +
    1441. 26 ret = checkAndGetModel(db, name);
    1442. +
    1443. 26 if (!ret) {
    1444. +
    1445. 26 ret = checkAndGetModel("default", name);
    1446. +
    1447. }
    1448. +
    1449. }
    1450. +
    1451. }
    1452. +
    1453. } else {
    1454. +
    1455. 0 ret = name;
    1456. +
    1457. }
    1458. +
    1459. 10603 if (isUndefinedOrNull(ret)) {
    1460. +
    1461. 0 throw new ModelError("Model " + name + " has not been registered with patio");
    1462. +
    1463. }
    1464. +
    1465. 10603 return ret;
    1466. +
    1467. };
    1468. +
    -
    +
    -
    database/schema.js
    +
    associations/manyToMany.js
    - Coverage89.45 - SLOC1230 - LOC275 - Missed29 + Coverage89.05 + SLOC308 + LOC137 + Missed15
    -
    1. 1"use strict";
    2. -
    3. 1var comb = require("comb"),
    4. -
    5. isFunction = comb.isFunction,
    6. -
    7. argsToArray = comb.argsToArray,
    8. -
    9. array = comb.array,
    10. -
    11. isArray = comb.isArray,
    12. -
    13. isString = comb.isString,
    14. +
      1. 1var comb = require("comb-proxy"),
      2. +
      3. define = comb.define,
      4. isUndefined = comb.isUndefined,
      5. -
      6. isNumber = comb.isNumber,
      7. -
      8. toArray = comb.array.toArray,
      9. +
      10. isUndefinedOrNull = comb.isUndefinedOrNull,
      11. +
      12. isFunction = comb.isFunction,
      13. +
      14. isInstanceOf = comb.isInstanceOf,
      15. +
      16. sql = require("../sql").sql,
      17. hitch = comb.hitch,
      18. -
      19. format = comb.string.format,
      20. -
      21. Dataset = require("../dataset"),
      22. -
      23. Promise = comb.Promise,
      24. -
      25. PromiseList = comb.PromiseList,
      26. -
      27. errors = require("../errors"),
      28. -
      29. DatabaseError = errors.DatabaseError,
      30. -
      31. generators = require("./schemaGenerators"),
      32. -
      33. SchemaGenerator = generators.SchemaGenerator,
      34. -
      35. AlterTableGenerator = generators.AlterTableGenerator,
      36. -
      37. sql = require("../sql").sql,
      38. -
      39. Time = sql.Time,
      40. -
      41. TimeStamp = sql.TimeStamp,
      42. -
      43. DateTime = sql.DateTime,
      44. -
      45. Year = sql.Year,
      46. -
      47. Float = sql.Float,
      48. -
      49. Decimal = sql.Decimal,
      50. -
      51. isInstanceOf = comb.isInstanceOf,
      52. -
      53. Identifier = sql.Identifier,
      54. -
      55. QualifiedIdentifier = sql.QualifiedIdentifier,
      56. -
      57. define = comb.define;
      58. -
      59. +
      60. array = comb.array,
      61. +
      62. isBoolean = comb.isBoolean,
      63. +
      64. serial = comb.serial,
      65. +
      66. when = comb.when,
      67. +
      68. zip = array.zip,
      69. +
      70. Promise = comb.Promise,
      71. +
      72. PromiseList = comb.PromiseList,
      73. +
      74. hitchIgnore = comb.hitchIgnore,
      75. +
      76. OneToMany = require("./oneToMany"),
      77. +
      78. pluralize = comb.pluralize,
      79. +
      80. AssociationError = require("../errors").AssociationError;
      81. -
      82. 1define(null, {
      83. +
      84. 1var LOGGER = comb.logger("comb.associations.ManyToMany");
      85. +
      86. /**
      87. +
      88. * @class Class to define a manyToMany association.
      89. +
      90. *
      91. +
      92. * </br>
      93. +
      94. * <b>NOT to be instantiated directly</b>
      95. +
      96. * Its just documented for reference.
      97. +
      98. *
      99. +
      100. * @name ManyToMany
      101. +
      102. * @augments patio.associations.OneToMany
      103. +
      104. * @memberOf patio.associations
      105. +
      106. *
      107. +
      108. * @param {String} options.joinTable the joinTable of the association.
      109. +
      110. *
      111. +
      112. *
      113. +
      114. * @property {String} joinTable the join table used in the relation.
      115. +
      116. * */
      117. +
      118. 1module.exports = define(OneToMany, {
      119. instance:{
      120. -
      121. /**@lends patio.Database.prototype*/
      122. +
      123. /**@lends patio.associations.ManyToMany.prototype*/
      124. +
      125. type:"manyToMany",
      126. -
      127. /**@ignore*/
      128. -
      129. constructor:function () {
      130. -
      131. 123 this._super(arguments);
      132. -
      133. 123 this.schemas = {};
      134. -
      135. },
      136. +
      137. _fetchMethod:"all",
      138. -
      139. /**
      140. -
      141. * Adds a column to the specified table. This method expects a column name,
      142. -
      143. * a datatype and optionally a hash with additional constraints and options:
      144. -
      145. *
      146. -
      147. * <p>
      148. -
      149. * This method is a shortcut to {@link patio.Database#alterTable} with an
      150. -
      151. * addColumn call.
      152. -
      153. * </p>
      154. -
      155. *
      156. -
      157. *
      158. -
      159. * @example
      160. -
      161. * //Outside of a table
      162. -
      163. * //ALTER TABLE test ADD COLUMN name text UNIQUE'
      164. -
      165. * DB.addColumn("test", "name", "text", {unique : true});
      166. -
      167. *
      168. -
      169. * @param {String} table the table to add the column to.
      170. -
      171. * @param {String} column the name of the column to add.
      172. -
      173. * @param type datatype of the column
      174. -
      175. * @param {Object} [opts] additional options that can be used when adding a column.
      176. -
      177. * @param {Boolean} [opts.primaryKey] set to true if this column is a primary key.
      178. -
      179. * @param {Boolean} [opts.allowNull] whether or not this column should allow null.
      180. -
      181. * @param {Boolean} [opts.unique] set to true to add a UNIQUE constraint to a column,
      182. -
      183. *
      184. -
      185. * @return {Promise} a promise that is resolved when the ADD COLUMN action is complete.
      186. -
      187. **/
      188. -
      189. addColumn:function (table, column, type, opts) {
      190. -
      191. 9 var args = argsToArray(arguments).slice(1);
      192. -
      193. 9 return this.alterTable(table, function () {
      194. -
      195. 9 this.addColumn.apply(this, args);
      196. -
      197. });
      198. -
      199. },
      200. +
      201. supportsStringKey:false,
      202. -
      203. /**
      204. -
      205. * Adds an index to a table for the given columns
      206. -
      207. *
      208. -
      209. * <p>
      210. -
      211. * This method is a shortcut to {@link patio.Database#alterTable} with an
      212. -
      213. * addIndex call.
      214. -
      215. * </p>
      216. -
      217. * @example
      218. -
      219. * DB.addIndex("test", "name", {unique : true});
      220. -
      221. * //=> 'CREATE UNIQUE INDEX test_name_index ON test (name)'
      222. -
      223. * DB.addIndex("test", ["one", "two"]);
      224. -
      225. * //=> ''CREATE INDEX test_one_two_index ON test (one, two)''
      226. -
      227. *
      228. -
      229. * @param {String} table the table to add the index to.
      230. -
      231. * @param {String|String[]} columns the name of the column/s to create an index for.
      232. -
      233. * @param {Object} [options] additional options that can be used when adding an index.
      234. -
      235. * @param {Boolean} [options.unique] set to true if this this index should have a UNIQUE constraint.
      236. -
      237. * @param {Boolean} [options.ignoreErrors] set to true to ignore errors.
      238. -
      239. *
      240. -
      241. * @return {Promise} a promise that is resolved when the CREATE INDEX action is complete.
      242. -
      243. * */
      244. -
      245. addIndex:function (table, columns, options) {
      246. -
      247. 4 options = options || {};
      248. -
      249. 4 var ignoreErrors = options.ignoreErrors === true;
      250. -
      251. 4 var ret = new Promise();
      252. -
      253. 4 this.alterTable(table,function () {
      254. -
      255. 4 this.addIndex(columns, options);
      256. -
      257. }).then(ret, function (err) {
      258. -
      259. 0 if (!ignoreErrors) {
      260. -
      261. 0 ret.errback(err);
      262. -
      263. } else {
      264. -
      265. 0 ret.callback();
      266. +
      267. supportsCompositeKey:false,
      268. +
      269. +
      270. +
      271. _filter:function (parent) {
      272. +
      273. 326 var keys = this._getAssociationKey(parent);
      274. +
      275. 326 var options = this.__opts || {};
      276. +
      277. 326 var ds;
      278. +
      279. 326 if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {
      280. +
      281. 0 ds = ds.apply(parent, [parent]);
      282. +
      283. }
      284. +
      285. 326 if (!ds) {
      286. +
      287. 326 ds = this.model.dataset.naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {
      288. +
      289. 326 return sql.stringToIdentifier(k);
      290. +
      291. })).concat(zip(keys[0], this.parentPrimaryKey.map(function (k) {
      292. +
      293. 326 return parent[k];
      294. +
      295. }))));
      296. +
      297. 326 var recip = this.model._findAssociation(this);
      298. +
      299. 326 if (recip) {
      300. +
      301. 326 recip = recip[1];
      302. +
      303. }
      304. +
      305. 326 ds.rowCb = hitch(this, function (item) {
      306. +
      307. 340 var ret = new Promise();
      308. +
      309. 340 var model = this._toModel(item, true);
      310. +
      311. 340 if (recip) {
      312. +
      313. 340 recip.__setValue(model, parent);
      314. }
      315. +
      316. //call hook to finish other model associations
      317. +
      318. 340 model._hook("post", "load").then(hitch(this, function () {
      319. +
      320. 340 ret.callback(model);
      321. +
      322. }), ret);
      323. +
      324. 340 return ret.promise();
      325. });
      326. -
      327. 4 return ret.promise();
      328. -
      329. },
      330. -
      331. -
      332. /**
      333. -
      334. * Removes a column from the specified table.
      335. -
      336. * <p>
      337. -
      338. * This method is a shortcut to {@link patio.Database#alterTable} with an
      339. -
      340. * dropColumn call.
      341. -
      342. * </p>
      343. -
      344. *
      345. -
      346. * @example
      347. -
      348. * DB.dropColumn("items", "category");
      349. -
      350. * //=> 'ALTER TABLE items DROP COLUMN category',
      351. -
      352. *
      353. -
      354. * @param {String|patio.sql.Identifier} table the table to alter.
      355. -
      356. * @param {String|patio.sql.Identifier} column the column to drop.
      357. -
      358. *
      359. -
      360. * @return {Promise} a promise that is resolved once the DROP COLUMN action is complete.
      361. -
      362. * */
      363. -
      364. dropColumn:function (table, column) {
      365. -
      366. 3 column = argsToArray(arguments).slice(1);
      367. -
      368. 3 return this.alterTable(table, function () {
      369. -
      370. 3 this.dropColumn.apply(this, column);
      371. -
      372. });
      373. -
      374. },
      375. -
      376. /**
      377. -
      378. * Removes an index for the given table and column/s.
      379. -
      380. *
      381. -
      382. * <p>
      383. -
      384. * This method is a shortcut to {@link patio.Database#alterTable} with an
      385. -
      386. * dropIndex call.
      387. -
      388. * </p>
      389. -
      390. *
      391. -
      392. * @example
      393. -
      394. * DB.dropIndex("posts", "title");
      395. -
      396. * //=>'DROP INDEX posts_title_index
      397. -
      398. * DB.dropIndex("posts", ["author", "title"]);
      399. -
      400. * //'DROP INDEX posts_author_title_index'
      401. -
      402. *
      403. -
      404. * @param {String|patio.sql.Identifier} table the table to alter.
      405. -
      406. * @param {String|patio.sql.Identifier} column the name of the column/s the index was created from.
      407. -
      408. *
      409. -
      410. * @return {Promise} a promise that is resolved once the DROP INDEX action is complete.
      411. -
      412. * */
      413. -
      414. dropIndex:function (table, columns, options) {
      415. -
      416. 1 var args = argsToArray(arguments).slice(1);
      417. -
      418. 1 return this.alterTable(table, function () {
      419. -
      420. 1 this.dropIndex.apply(this, args);
      421. -
      422. });
      423. -
      424. },
      425. +
      426. }
      427. -
      428. /**
      429. -
      430. * Renames a column in the specified table.
      431. -
      432. *
      433. -
      434. * <p>
      435. -
      436. * This method is a shortcut to {@link patio.Database#alterTable} with an
      437. -
      438. * renameColumn call.
      439. -
      440. * </p>
      441. -
      442. *
      443. -
      444. * @example
      445. -
      446. * DB.renameColumn("items", "cntr", "counter");
      447. -
      448. * //=> ALTER TABLE items RENAME COLUMN cntr TO counter
      449. -
      450. *
      451. -
      452. * @param {String|patio.sql.Identifier} table the table to alter.
      453. -
      454. * @param {String|patio.sql.Identifier} column the name of the column to rename.
      455. -
      456. * @param {String|patio.sql.Identifier} newColumn the new name of the column.
      457. -
      458. *
      459. -
      460. * @return {Promise} a promise that is resolved once the RENAME COLUMN action is complete.
      461. -
      462. * */
      463. -
      464. renameColumn:function (table, column, newColumn) {
      465. -
      466. 4 var args = argsToArray(arguments).slice(1);
      467. -
      468. 4 return this.alterTable(table, function () {
      469. -
      470. 4 this.renameColumn.apply(this, args);
      471. -
      472. });
      473. +
      474. 326 return this._setDatasetOptions(ds);
      475. },
      476. -
      477. -
      478. /**
      479. -
      480. *Sets the default value for the given column in the given table:
      481. -
      482. *
      483. -
      484. * <p>
      485. -
      486. * This method is a shortcut to {@link patio.Database#alterTable} with an
      487. -
      488. * setColumnDefault call.
      489. -
      490. * </p>
      491. -
      492. *
      493. -
      494. * @example
      495. -
      496. * DB.setColumnDefault("items", "category", "misc");
      497. -
      498. * //=> ALTER TABLE items ALTER COLUMN category SET DEFAULT 'misc'
      499. -
      500. *
      501. -
      502. * @param {String|patio.sql.Identifier} table the table to alter.
      503. -
      504. * @param {String|patio.sql.Identifier} column the name of the column to set the DEFAULT on.
      505. -
      506. * @param def the new default value of the column.
      507. -
      508. *
      509. -
      510. * @return {Promise} a promise that is resolved once the SET DEFAULT action is complete.
      511. -
      512. * */
      513. -
      514. setColumnDefault:function (table, column, def) {
      515. -
      516. 1 var args = argsToArray(arguments).slice(1);
      517. -
      518. 1 return this.alterTable(table, function () {
      519. -
      520. 1 this.setColumnDefault.apply(this, args);
      521. -
      522. });
      523. +
      524. _setAssociationKeys:function (parent, model, val) {
      525. +
      526. 534 var keys = this._getAssociationKey(parent),
      527. +
      528. leftKey = keys[0],
      529. +
      530. parentPk = this.parentPrimaryKey;
      531. +
      532. 534 if (!(leftKey && leftKey.length === parentPk.length)) {
      533. +
      534. 0 throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);
      535. +
      536. }
      537. +
      538. 534 for (var i = 0; i < leftKey.length; i++) {
      539. +
      540. 534 model[leftKey[i]] = !isUndefined(val) ? val : parent[parentPk[i]];
      541. +
      542. }
      543. },
      544. -
      545. /**
      546. -
      547. * Set the data type for the given column in the given table:
      548. -
      549. * <p>
      550. -
      551. * This method is a shortcut to {@link patio.Database#alterTable} with an
      552. -
      553. * setColumnType call.
      554. -
      555. * </p>
      556. -
      557. *
      558. -
      559. * @example
      560. -
      561. * DB.setColumnType("items", "category", String);
      562. -
      563. * //=> ALTER TABLE items ALTER COLUMN category TYPE varchar(255)
      564. -
      565. *
      566. -
      567. * @param {String|patio.sql.Identifier} table the table to alter.
      568. -
      569. * @param {String|patio.sql.Identifier} column the name of the column to set the TYPE on.
      570. -
      571. * @param type the datatype of the column.
      572. -
      573. *
      574. -
      575. * @return {Promise} a promise that is resolved once the SET TYPE action is complete.
      576. -
      577. * */
      578. -
      579. setColumnType:function (table, column, type) {
      580. -
      581. 3 var args = argsToArray(arguments).slice(1);
      582. -
      583. 3 return this.alterTable(table, function () {
      584. -
      585. 3 this.setColumnType.apply(this, args);
      586. -
      587. });
      588. -
      589. },
      590. -
      591. -
      592. -
      593. /**
      594. -
      595. * Alters the given table with the specified block.
      596. -
      597. * <p>
      598. -
      599. * <b>NOTE:</b> The block is invoked in the scope of the table that is being altered. The block
      600. -
      601. * is also called with the table as the first argument. Within the block you must use
      602. -
      603. * <b>this</b>(If the block has not been bound to a different scope), or the table object
      604. -
      605. * that is passed in for all alter table operations. See {@link patio.AlterTableGenerator} for
      606. -
      607. * avaiable operations.
      608. -
      609. * </p>
      610. -
      611. *
      612. -
      613. * <p>
      614. -
      615. * <b>Note</b> that addColumn accepts all the options available for column
      616. -
      617. * definitions using createTable, and addIndex accepts all the options
      618. -
      619. * available for index definition.
      620. -
      621. * </p>
      622. -
      623. *
      624. -
      625. * @example
      626. -
      627. * //using the table object
      628. -
      629. * DB.alterTable("items", function(table){
      630. -
      631. * //you must use the passed in table object.
      632. -
      633. * table.addColumn("category", "text", {default : 'javascript'});
      634. -
      635. * table.dropColumn("category");
      636. -
      637. * table.renameColumn("cntr", "counter");
      638. -
      639. * table.setColumnType("value", "float");
      640. -
      641. * table.setColumnDefault("value", "float");
      642. -
      643. * table.addIndex(["group", "category"]);
      644. -
      645. * table.dropIndex [:group, :category]
      646. -
      647. * });
      648. -
      649. *
      650. -
      651. * //using this
      652. -
      653. * DB.alterTable("items", function(){
      654. -
      655. * this.addColumn("category", "text", {default : 'javascript'});
      656. -
      657. * this.dropColumn("category");
      658. -
      659. * this.renameColumn("cntr", "counter");
      660. -
      661. * this.setColumnType("value", "float");
      662. -
      663. * this.setColumnDefault("value", "float");
      664. -
      665. * this.addIndex(["group", "category"]);
      666. -
      667. * this.dropIndex [:group, :category]
      668. -
      669. * });
      670. -
      671. *
      672. -
      673. * //This will not work
      674. -
      675. * DB.alterTable("items", comb.hitch(someObject, function(){
      676. -
      677. * //This is called in the scope of someObject so this
      678. -
      679. * //will not work and will throw an error
      680. -
      681. * this.addColumn("category", "text", {default : 'javascript'});
      682. -
      683. * }));
      684. -
      685. *
      686. -
      687. * //This will work
      688. -
      689. * DB.alterTable("items", comb.hitch(someObject, function(table){
      690. -
      691. * //This is called in the scope of someObject so you must
      692. -
      693. * //use the table argument
      694. -
      695. * table.category("text", {default : 'javascript'});
      696. -
      697. * }));
      698. -
      699. *
      700. -
      701. *
      702. -
      703. * @param {String|patio.sql.Identifier} table to the table to perform the ALTER TABLE operations on.
      704. -
      705. * @param {Function} block the block to invoke for the ALTER TABLE operations
      706. -
      707. *
      708. -
      709. * @return {Promise} a promise that is resolved once all ALTER TABLE operations have completed.
      710. -
      711. * */
      712. -
      713. alterTable:function (name, generator, block) {
      714. -
      715. 84 if (isFunction(generator)) {
      716. -
      717. 84 block = generator;
      718. -
      719. 84 generator = new AlterTableGenerator(this, block);
      720. +
      721. __createJoinTableInsertRemoveQuery:function (model, item) {
      722. +
      723. 176 var q = {};
      724. +
      725. 176 var keys = this._getAssociationKey(model),
      726. +
      727. leftKey = keys[0],
      728. +
      729. rightKey = keys[1],
      730. +
      731. parentPk = this.parentPrimaryKey,
      732. +
      733. modelPk = this.modelPrimaryKey;
      734. +
      735. 176 if (!(leftKey && leftKey.length === parentPk.length)) {
      736. +
      737. 0 throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);
      738. }
      739. -
      740. 84 var ret = new Promise();
      741. -
      742. 84 this.__alterTableSqlList(name, generator.operations).then(hitch(this, function (res) {
      743. -
      744. 84 var sqls = array.flatten(res.map(function (r) {
      745. -
      746. 93 return r[1];
      747. -
      748. }));
      749. -
      750. 84 var l = sqls.length;
      751. -
      752. 84 var drop = hitch(this, function (i) {
      753. -
      754. 179 if (i < l) {
      755. -
      756. 95 var sql = sqls[i++];
      757. -
      758. 95 this.executeDdl(sql).then(hitch(this, drop, i), ret);
      759. -
      760. } else {
      761. -
      762. 84 this.removeCachedSchema(name);
      763. -
      764. 84 ret.callback();
      765. -
      766. }
      767. -
      768. });
      769. -
      770. 84 drop(0);
      771. -
      772. }));
      773. -
      774. 84 return ret.promise();
      775. -
      776. },
      777. -
      778. -
      779. /**
      780. -
      781. * Creates a table with the columns given in the provided block:
      782. -
      783. *
      784. -
      785. * <p>
      786. -
      787. * <b>NOTE:</b> The block is invoked in the scope of the table that is being created. The block
      788. -
      789. * is also called with the table as the first argument. Within the block you must use
      790. -
      791. * <b>this</b>(If the block has not been bound to a different scope), or the table object
      792. -
      793. * that is passed in for all create table operations. See {@link patio.SchemaGenerator} for
      794. -
      795. * available operations.
      796. -
      797. * </p>
      798. -
      799. *
      800. -
      801. *
      802. -
      803. * @example
      804. -
      805. *
      806. -
      807. * //using the table to create the table
      808. -
      809. * DB.createTable("posts", function(table){
      810. -
      811. * table.primaryKey("id");
      812. -
      813. * table.column('title", "text");
      814. -
      815. * //you may also invoke the column name as
      816. -
      817. * //function on the table
      818. -
      819. * table.content(String);
      820. -
      821. * table.index(title);
      822. -
      823. * });
      824. -
      825. *
      826. -
      827. * //using this to create the table
      828. -
      829. * DB.createTable("posts", function(){
      830. -
      831. * this.primaryKey("id");
      832. -
      833. * this.column('title", "text");
      834. -
      835. * //you may also invoke the column name as
      836. -
      837. * //function on the table
      838. -
      839. * this.content(String);
      840. -
      841. * this.index(title);
      842. -
      843. * });
      844. -
      845. *
      846. -
      847. * @param {String|patio.sql.Identifier} name the name of the table to create.
      848. -
      849. * @param {Object} [options] an optional options object
      850. -
      851. * @param {Boolean} [options.temp] set to true if this table is a TEMPORARY table.
      852. -
      853. * @param {Boolean} [options.ignoreIndexErrors] Ignore any errors when creating indexes.
      854. -
      855. * @param {Function} block the block to invoke when creating the table.
      856. -
      857. *
      858. -
      859. * @return {Promise} a promise that is resolved when the CREATE TABLE action is completed.
      860. -
      861. *
      862. -
      863. */
      864. -
      865. createTable:function (name, options, block) {
      866. -
      867. 218 if (isFunction(options)) {
      868. -
      869. 208 block = options;
      870. -
      871. 208 options = {};
      872. +
      873. 176 if (!(rightKey && rightKey.length === modelPk.length)) {
      874. +
      875. 0 throw new AssociationError("Invalid rightKey for " + this.name + " : " + rightKey);
      876. }
      877. -
      878. 218 this.removeCachedSchema(name);
      879. -
      880. 218 if (isInstanceOf(options, SchemaGenerator)) {
      881. -
      882. 0 options = {generator:options};
      883. +
      884. 176 for (var i = 0; i < leftKey.length; i++) {
      885. +
      886. 176 q[leftKey[i]] = model[parentPk[i]];
      887. }
      888. -
      889. 218 var generator = options.generator || new SchemaGenerator(this, block);
      890. -
      891. 218 return this.__createTableFromGenerator(name, generator, options)
      892. -
      893. .chain(hitch(this, "__createTableIndexesFromGenerator", name, generator, options))
      894. -
      895. .promise();
      896. +
      897. 176 for (i = 0; i < rightKey.length; i++) {
      898. +
      899. 176 q[rightKey[i]] = item[modelPk[i]];
      900. +
      901. }
      902. +
      903. 176 return q;
      904. },
      905. -
      906. /**
      907. -
      908. * Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.
      909. -
      910. * <p>
      911. -
      912. * See {@link patio.Database#createTable} for parameter types.
      913. -
      914. * </p>
      915. -
      916. *
      917. -
      918. * @example
      919. -
      920. * // DROP TABLE a
      921. -
      922. * // CREATE TABLE a (a integer)
      923. -
      924. * DB.forceCreateTable("a", function(){
      925. -
      926. * this.a("integer");
      927. -
      928. * });
      929. -
      930. *
      931. -
      932. **/
      933. -
      934. forceCreateTable:function (name, options, block) {
      935. -
      936. 18 return this.dropTable(name)
      937. -
      938. .chainBoth(hitch(this, this.createTable, name, options, block))
      939. -
      940. .promise();
      941. +
      942. _preRemove:function (next, model) {
      943. +
      944. 202 if (this.isOwner && !this.isCascading) {
      945. +
      946. 202 var q = {};
      947. +
      948. 202 this._setAssociationKeys(model, q);
      949. +
      950. 202 this.joinTable.where(q).remove().classic(next);
      951. +
      952. } else {
      953. +
      954. 0 next();
      955. +
      956. }
      957. },
      958. -
      959. /**
      960. -
      961. * Creates the table unless the table already exists.
      962. -
      963. * <p>
      964. -
      965. * See {@link patio.Database#createTable} for parameter types.
      966. -
      967. * </p>
      968. -
      969. */
      970. -
      971. createTableUnlessExists:function (name, options, block) {
      972. -
      973. 0 var ret = new Promise();
      974. -
      975. 0 this.tableExists(name).then(hitch(this, function (exists) {
      976. -
      977. 0 if (!exists) {
      978. -
      979. 0 this.createTable(name, options, block).then(ret);
      980. +
      981. addAssociation:function (item, model, reload) {
      982. +
      983. 260 reload = isBoolean(reload) ? reload : false;
      984. +
      985. 260 var ret = new Promise().callback(model);
      986. +
      987. 260 if (!isUndefinedOrNull(item)) {
      988. +
      989. 260 if (!model.isNew) {
      990. +
      991. 148 item = this._toModel(item);
      992. +
      993. 148 var loaded = this.associationLoaded(model);
      994. +
      995. 148 var recip = this.model._findAssociation(this), save = item.isNew;
      996. +
      997. 148 ret = model._checkTransaction(hitch(this, function () {
      998. +
      999. 148 var joinTable = this.joinTable, ret = new Promise();
      1000. +
      1001. 148 serial([
      1002. +
      1003. function () {
      1004. +
      1005. 148 return save ? item.save() : null;
      1006. +
      1007. },
      1008. +
      1009. function () {
      1010. +
      1011. 148 return joinTable.insert(this.__createJoinTableInsertRemoveQuery(model, item));
      1012. +
      1013. }.bind(this),
      1014. +
      1015. function () {
      1016. +
      1017. 148 if (recip) {
      1018. +
      1019. 148 recip[1].__setValue(item, [model]);
      1020. +
      1021. }
      1022. +
      1023. },
      1024. +
      1025. function () {
      1026. +
      1027. 148 if (loaded && reload) {
      1028. +
      1029. 3 return this.parent._reloadAssociationsForType(this.type, this.model, model);
      1030. +
      1031. } else {
      1032. +
      1033. 145 return model;
      1034. +
      1035. }
      1036. +
      1037. }.bind(this)
      1038. +
      1039. ]).then(hitchIgnore(ret, "callback", model), ret);
      1040. +
      1041. 148 return ret.promise();
      1042. +
      1043. }));
      1044. } else {
      1045. -
      1046. 0 ret.callback();
      1047. -
      1048. }
      1049. -
      1050. }), ret);
      1051. -
      1052. 0 return ret.promise();
      1053. -
      1054. },
      1055. -
      1056. -
      1057. /**
      1058. -
      1059. * Creates a view, replacing it if it already exists:
      1060. -
      1061. * @example
      1062. -
      1063. * DB.createOrReplaceView("cheapItems", "SELECT * FROM items WHERE price < 100");
      1064. -
      1065. * //=> CREATE OR REPLACE VIEW cheapItems AS SELECT * FROM items WHERE price < 100
      1066. -
      1067. * DB.createOrReplaceView("miscItems", DB[:items].filter({category : 'misc'}));
      1068. -
      1069. * //=> CREATE OR REPLACE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'
      1070. -
      1071. *
      1072. -
      1073. * @param {String|patio.sql.Identifier} name the name of the view to create.
      1074. -
      1075. * @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the
      1076. -
      1077. * view.
      1078. -
      1079. *
      1080. -
      1081. * @return {Promise} a promise that is resolved when the CREATE OR REPLACE VIEW action is complete.
      1082. -
      1083. **/
      1084. -
      1085. createOrReplaceView:function (name, source) {
      1086. -
      1087. 4 if (isInstanceOf(source, Dataset)) {
      1088. -
      1089. 2 source = source.sql;
      1090. -
      1091. }
      1092. -
      1093. 4 var ret = new Promise();
      1094. -
      1095. 4 this.executeDdl(format("CREATE OR REPLACE VIEW %s AS %s", this.__quoteSchemaTable(name), source)).then(hitch(this, function () {
      1096. -
      1097. 4 this.removeCachedSchema(name);
      1098. -
      1099. 4 ret.callback();
      1100. -
      1101. }), ret);
      1102. -
      1103. 4 return ret.promise();
      1104. -
      1105. },
      1106. -
      1107. -
      1108. /**
      1109. -
      1110. * Creates a view based on a dataset or an SQL string:
      1111. -
      1112. * @example
      1113. -
      1114. * DB.createView("cheapItems", "SELECT * FROM items WHERE price < 100");
      1115. -
      1116. * //=> CREATE VIEW cheapItems AS SELECT * FROM items WHERE price < 100
      1117. -
      1118. * DB.createView("miscItems", DB[:items].filter({category : 'misc'}));
      1119. -
      1120. * //=> CREATE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'
      1121. -
      1122. *
      1123. -
      1124. * @param {String|patio.sql.Identifier} name the name of the view to create.
      1125. -
      1126. * @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the
      1127. -
      1128. * view.
      1129. -
      1130. **/
      1131. -
      1132. createView:function (name, source) {
      1133. -
      1134. 4 if (isInstanceOf(source, Dataset)) {
      1135. -
      1136. 2 source = source.sql;
      1137. +
      1138. 112 item = this._toModel(item);
      1139. +
      1140. 112 var items = this.getAssociation(model);
      1141. +
      1142. 112 if (isUndefinedOrNull(items)) {
      1143. +
      1144. 39 this.__setValue(model, [item]);
      1145. +
      1146. } else {
      1147. +
      1148. 73 items.push(item);
      1149. +
      1150. }
      1151. +
      1152. }
      1153. }
      1154. -
      1155. 4 return this.executeDdl(format("CREATE VIEW %s AS %s", this.__quoteSchemaTable(name), source));
      1156. +
      1157. 260 return ret.promise();
      1158. },
      1159. -
      1160. /**
      1161. -
      1162. * Drops one or more tables corresponding to the given names.
      1163. -
      1164. *
      1165. -
      1166. * @example
      1167. -
      1168. * DB.dropTable("test");
      1169. -
      1170. * //=>'DROP TABLE test'
      1171. -
      1172. * DB.dropTable("a", "bb", "ccc");
      1173. -
      1174. * //=>'DROP TABLE a',
      1175. -
      1176. * //=>'DROP TABLE bb',
      1177. -
      1178. * //=>'DROP TABLE ccc'
      1179. -
      1180. *
      1181. -
      1182. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the tables
      1183. -
      1184. * to drop.
      1185. -
      1186. *
      1187. -
      1188. * @return {Promise} a promise that is resolved once all tables have been dropped.
      1189. -
      1190. **/
      1191. -
      1192. dropTable:function (names) {
      1193. -
      1194. 81 var ret = new Promise(), l = names.length;
      1195. -
      1196. 81 if (isArray(names)) {
      1197. -
      1198. 48 var drop = hitch(this, function (i) {
      1199. -
      1200. 104 if (i < l) {
      1201. -
      1202. 65 var name = names[i++];
      1203. -
      1204. 65 this.executeDdl(this.__dropTableSql(name)).then(hitch(this, function () {
      1205. -
      1206. 56 this.removeCachedSchema(name);
      1207. -
      1208. 56 drop(i);
      1209. +
      1210. removeItem:function (item, model, remove, reload) {
      1211. +
      1212. 56 reload = isBoolean(reload) ? reload : false;
      1213. +
      1214. 56 remove = isBoolean(remove) ? remove : false;
      1215. +
      1216. 56 var ret = new Promise().callback(model);
      1217. +
      1218. 56 if (!isUndefinedOrNull(item)) {
      1219. +
      1220. 56 if (!model.isNew) {
      1221. +
      1222. 56 if (isInstanceOf(item, this.model) && !item.isNew) {
      1223. +
      1224. 56 var loaded = this.associationLoaded(model);
      1225. +
      1226. 56 remove = remove && !item.isNew;
      1227. +
      1228. 56 ret = new Promise();
      1229. +
      1230. 56 model._checkTransaction(hitch(this, function () {
      1231. +
      1232. 56 return remove ? item.remove() : this.joinTable.where(this.__createJoinTableInsertRemoveQuery(model, item)).remove();
      1233. +
      1234. })).then(hitch(this, function () {
      1235. +
      1236. 56 if (loaded && reload) {
      1237. +
      1238. 18 return this.parent._reloadAssociationsForType(this.type, this.model, model).then(ret);
      1239. +
      1240. } else {
      1241. +
      1242. 38 ret.callback(model);
      1243. +
      1244. }
      1245. }), ret);
      1246. -
      1247. } else {
      1248. -
      1249. 39 ret.callback();
      1250. +
      1251. 56 return ret.promise();
      1252. }
      1253. -
      1254. });
      1255. -
      1256. 48 drop(0);
      1257. -
      1258. 48 return ret.promise();
      1259. -
      1260. } else {
      1261. -
      1262. 33 return this.dropTable(argsToArray(arguments).filter(function (t) {
      1263. -
      1264. 38 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
      1265. -
      1266. }));
      1267. -
      1268. +
      1269. } else {
      1270. +
      1271. 0 item = this._toModel(item);
      1272. +
      1273. 0 var items = this.getAssociation(model), index;
      1274. +
      1275. 0 if (!isUndefinedOrNull(items) && (index = items.indexOf(item)) !== -1) {
      1276. +
      1277. 0 items.splice(index, 1);
      1278. +
      1279. }
      1280. +
      1281. }
      1282. }
      1283. +
      1284. 0 return ret.promise();
      1285. },
      1286. -
      1287. /**
      1288. -
      1289. * Forcible drops one or more tables corresponding to the given names, ignoring errors.
      1290. -
      1291. *
      1292. -
      1293. * @example
      1294. -
      1295. * DB.dropTable("test");
      1296. -
      1297. * //=>'DROP TABLE test'
      1298. -
      1299. * DB.dropTable("a", "bb", "ccc");
      1300. -
      1301. * //=>'DROP TABLE a',
      1302. -
      1303. * //=>'DROP TABLE bb',
      1304. -
      1305. * //=>'DROP TABLE ccc'
      1306. -
      1307. *
      1308. -
      1309. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the tables
      1310. -
      1311. * to drop.
      1312. -
      1313. *
      1314. -
      1315. * @return {Promise} a promise that is resolved once all tables have been dropped.
      1316. -
      1317. **/
      1318. -
      1319. forceDropTable:function (names) {
      1320. -
      1321. 158 var ret = new Promise(), l = names.length;
      1322. -
      1323. 158 if (isArray(names)) {
      1324. -
      1325. 96 var drop = hitch(this, function (i) {
      1326. -
      1327. 241 if (i < l) {
      1328. -
      1329. 145 var name = names[i++];
      1330. -
      1331. 145 this.executeDdl(this.__dropTableSql(name)).both(hitch(this, function () {
      1332. -
      1333. 145 this.removeCachedSchema(name);
      1334. -
      1335. 145 drop(i);
      1336. -
      1337. }));
      1338. -
      1339. } else {
      1340. -
      1341. 96 ret.callback();
      1342. -
      1343. }
      1344. -
      1345. });
      1346. -
      1347. 96 drop(0);
      1348. +
      1349. removeAllItems:function (model, remove) {
      1350. +
      1351. 4 remove = isBoolean(remove) ? remove : false;
      1352. +
      1353. 4 var ret = new Promise();
      1354. +
      1355. 4 if (!model.isNew) {
      1356. +
      1357. 4 var q = {}, removeQ = {};
      1358. +
      1359. 4 this._setAssociationKeys(model, q);
      1360. +
      1361. 4 this._setAssociationKeys(model, removeQ, null);
      1362. +
      1363. 4 var loaded = this.associationLoaded(model);
      1364. +
      1365. 4 return model._checkTransaction(hitch(this, function () {
      1366. +
      1367. 4 var ds = this.model.dataset, ret = new Promise();
      1368. +
      1369. 4 when(remove ? this._filter(model).forEach(function (m) {
      1370. +
      1371. 6 return m.remove();
      1372. +
      1373. }) : this.joinTable.filter(q).update(removeQ)).then(function () {
      1374. +
      1375. 4 if (loaded) {
      1376. +
      1377. 2 this.parent._reloadAssociationsForType(this.type, this.model, model)
      1378. +
      1379. .then(hitchIgnore(ret, "callback", model), ret);
      1380. +
      1381. } else {
      1382. +
      1383. 2 ret.callback(model);
      1384. +
      1385. }
      1386. +
      1387. }.bind(this), ret);
      1388. +
      1389. 4 return ret.promise();
      1390. +
      1391. }));
      1392. } else {
      1393. -
      1394. 62 this.forceDropTable(argsToArray(arguments).filter(function (t) {
      1395. -
      1396. 62 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
      1397. -
      1398. })).both(ret);
      1399. +
      1400. //todo we may want to check if any of the items were previously saved items;
      1401. +
      1402. 0 this._clearAssociations(model);
      1403. +
      1404. 0 ret.callback(model);
      1405. }
      1406. -
      1407. 158 return ret.promise();
      1408. +
      1409. 0 return ret.promise();
      1410. },
      1411. -
      1412. /**
      1413. -
      1414. * Drops one or more views corresponding to the given names.
      1415. -
      1416. *
      1417. -
      1418. * @example
      1419. -
      1420. * DB.dropView("test_view");
      1421. -
      1422. * //=>'DROP VIEW test_view'
      1423. -
      1424. * DB.dropTable("test_view_1", "test_view_2", "test_view_3");
      1425. -
      1426. * //=>'DROP VIEW test_view_1',
      1427. -
      1428. * //=>'DROP VIEW test_view_2',
      1429. -
      1430. * //=>'DROP VIEW test_view_3'
      1431. -
      1432. *
      1433. -
      1434. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the views
      1435. -
      1436. * to drop.
      1437. -
      1438. *
      1439. -
      1440. * @return {Promise} a promise that is resolved once the view/s have been dropped.
      1441. -
      1442. **/
      1443. -
      1444. dropView:function (names) {
      1445. -
      1446. 8 var ret = new Promise(), l = names.length;
      1447. -
      1448. 8 if (isArray(names)) {
      1449. -
      1450. 4 var drop = hitch(this, function (i) {
      1451. -
      1452. 8 if (i < l) {
      1453. -
      1454. 4 var name = names[i++];
      1455. -
      1456. 4 this.executeDdl(format("DROP VIEW %s", this.__quoteSchemaTable(name))).then(hitch(this, function () {
      1457. -
      1458. 4 this.removeCachedSchema(name);
      1459. -
      1460. 4 drop(i);
      1461. -
      1462. }), ret);
      1463. -
      1464. } else {
      1465. -
      1466. 4 ret.callback();
      1467. -
      1468. }
      1469. -
      1470. });
      1471. -
      1472. 4 drop(0);
      1473. -
      1474. 4 return ret.promise();
      1475. -
      1476. } else {
      1477. -
      1478. 4 return this.dropView(argsToArray(arguments).filter(function (t) {
      1479. -
      1480. 4 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
      1481. -
      1482. }));
      1483. -
      1484. }
      1485. -
      1486. },
      1487. +
      1488. getters:{
      1489. -
      1490. /**
      1491. -
      1492. * Renames a table.
      1493. -
      1494. *
      1495. -
      1496. * @example
      1497. -
      1498. * comb.executeInOrder(DB, function(DB){
      1499. -
      1500. * DB.tables(); //=> ["items"]
      1501. -
      1502. * DB.renameTable("items", "old_items");
      1503. -
      1504. * //=>'ALTER TABLE items RENAME TO old_items'
      1505. -
      1506. * DB.tables; //=> ["old_items"]
      1507. -
      1508. *});
      1509. -
      1510. *
      1511. -
      1512. * @param {String|patio.sql.Identifier} name the name of the table to rename
      1513. -
      1514. * @param {String|patio.sql.Identifier} newName the new name of the table
      1515. -
      1516. * @return {Promise} a promise that is resolved once the table is renamed.
      1517. -
      1518. **/
      1519. -
      1520. renameTable:function (name, newName) {
      1521. -
      1522. 2 return this.executeDdl(this.__renameTableSql(name, newName)).chain(hitch(this, function () {
      1523. -
      1524. 2 this.removeCachedSchema(name);
      1525. -
      1526. })).promise();
      1527. +
      1528. select:function () {
      1529. +
      1530. 326 return this.__select;
      1531. +
      1532. },
      1533. -
      1534. },
      1535. +
      1536. defaultLeftKey:function () {
      1537. +
      1538. 1740 return this.__opts.leftKey || this.parent.tableName + "Id";
      1539. +
      1540. },
      1541. -
      1542. /**
      1543. -
      1544. * @private
      1545. -
      1546. * The SQL to execute to modify the DDL for the given table name. op
      1547. -
      1548. * should be one of the operations returned by the AlterTableGenerator.
      1549. -
      1550. * */
      1551. -
      1552. __alterTableSql:function (table, op) {
      1553. -
      1554. 83 var ret = new Promise();
      1555. -
      1556. 83 var quotedName = op.name ? this.__quoteIdentifier(op.name) : null;
      1557. -
      1558. 83 var alterTableOp = null;
      1559. -
      1560. 83 switch (op.op) {
      1561. -
      1562. case "addColumn":
      1563. -
      1564. 13 alterTableOp = format("ADD COLUMN %s", this.__columnDefinitionSql(op));
      1565. -
      1566. 13 break;
      1567. -
      1568. case "dropColumn":
      1569. -
      1570. 4 alterTableOp = format("DROP COLUMN %s", quotedName);
      1571. -
      1572. 4 break;
      1573. -
      1574. case "renameColumn":
      1575. -
      1576. 52 alterTableOp = format("RENAME COLUMN %s TO %s", quotedName, this.__quoteIdentifier(op.newName));
      1577. -
      1578. 52 break;
      1579. -
      1580. case "setColumnType":
      1581. -
      1582. 3 alterTableOp = format("ALTER COLUMN %s TYPE %s", quotedName, this.typeLiteral(op));
      1583. -
      1584. 3 break;
      1585. -
      1586. case "setColumnDefault":
      1587. -
      1588. 2 alterTableOp = format("ALTER COLUMN %s SET DEFAULT %s", quotedName, this.literal(op["default"]));
      1589. -
      1590. 2 break;
      1591. -
      1592. case "setColumnNull":
      1593. -
      1594. 0 alterTableOp = format("ALTER COLUMN %s %s NOT NULL", quotedName, op["null"] ? "DROP" : "SET");
      1595. -
      1596. 0 break;
      1597. -
      1598. case "addIndex":
      1599. -
      1600. 5 return ret.callback(this.__indexDefinitionSql(table, op)).promise();
      1601. -
      1602. case "dropIndex":
      1603. -
      1604. 2 return ret.callback(this.__dropIndexSql(table, op)).promise();
      1605. -
      1606. case "addConstraint":
      1607. -
      1608. 1 alterTableOp = format("ADD %s", this.__constraintDefinitionSql(op));
      1609. -
      1610. 1 break;
      1611. -
      1612. case "dropConstraint":
      1613. -
      1614. 0 alterTableOp = format("DROP CONSTRAINT %s", quotedName);
      1615. -
      1616. 0 break;
      1617. -
      1618. default :
      1619. -
      1620. 1 throw new DatabaseError("Invalid altertable operator");
      1621. +
      1622. defaultRightKey:function () {
      1623. +
      1624. 1740 return this.__opts.rightKey || this.model.tableName + "Id";
      1625. +
      1626. },
      1627. +
      1628. +
      1629. parentPrimaryKey:function () {
      1630. +
      1631. 1036 return this.__opts.leftPrimaryKey || this.parent.primaryKey;
      1632. +
      1633. },
      1634. +
      1635. +
      1636. modelPrimaryKey:function () {
      1637. +
      1638. 502 return this.__opts.rightPrimaryKey || this.model.primaryKey;
      1639. +
      1640. },
      1641. +
      1642. +
      1643. joinTableName:function () {
      1644. +
      1645. 344 if (!this._joinTable) {
      1646. +
      1647. 18 var options = this.__opts;
      1648. +
      1649. 18 var joinTable = options.joinTable;
      1650. +
      1651. 18 if (isUndefined(joinTable)) {
      1652. +
      1653. 18 var defaultJoinTable = this.defaultJoinTable;
      1654. +
      1655. 18 if (isUndefined(defaultJoinTable)) {
      1656. +
      1657. 0 throw new Error("Unable to determine jointable for " + this.name);
      1658. +
      1659. } else {
      1660. +
      1661. 18 this._joinTable = defaultJoinTable;
      1662. +
      1663. }
      1664. +
      1665. } else {
      1666. +
      1667. 0 this._joinTable = joinTable;
      1668. +
      1669. }
      1670. +
      1671. }
      1672. +
      1673. 344 return this._joinTable;
      1674. +
      1675. },
      1676. +
      1677. +
      1678. //returns our join table model
      1679. +
      1680. joinTable:function () {
      1681. +
      1682. 380 if (!this.__joinTableDataset) {
      1683. +
      1684. 18 var ds = this.__joinTableDataset = this.model.dataset.db.from(this.joinTableName), model = this.model, options = this.__opts;
      1685. +
      1686. 18 var identifierInputMethod = isUndefined(options.identifierInputMethod) ? model.identifierInputMethod : options.identifierInputMethod,
      1687. +
      1688. identifierOutputMethod = isUndefined(options.identifierOutputMethod) ? model.identifierOutputMethod : options.identifierOutputMethod;
      1689. +
      1690. 18 if (identifierInputMethod) {
      1691. +
      1692. 14 ds.identifierInputMethod = identifierInputMethod;
      1693. +
      1694. }
      1695. +
      1696. 18 if (identifierOutputMethod) {
      1697. +
      1698. 14 ds.identifierOutputMethod = identifierOutputMethod;
      1699. +
      1700. }
      1701. +
      1702. }
      1703. +
      1704. 380 return this.__joinTableDataset;
      1705. +
      1706. },
      1707. +
      1708. +
      1709. defaultJoinTable:function () {
      1710. +
      1711. 18 var ret;
      1712. +
      1713. 18 var recip = this.model._findAssociation(this);
      1714. +
      1715. 18 if (recip && recip.length) {
      1716. +
      1717. 18 var names = [pluralize(this._model), pluralize(recip[1]._model)].sort();
      1718. +
      1719. 18 names[1] = names[1].charAt(0).toUpperCase() + names[1].substr(1);
      1720. +
      1721. 18 ret = names.join("");
      1722. +
      1723. }
      1724. +
      1725. 18 return ret;
      1726. }
      1727. -
      1728. 75 return ret.callback(format("ALTER TABLE %s %s", this.__quoteSchemaTable(table), alterTableOp)).promise();
      1729. -
      1730. },
      1731. +
      1732. }
      1733. +
      1734. }
      1735. +
      1736. });
      1737. +
      +
    + +
    + + + + + +
    +
    database/schema.js
    +
    +
    + Coverage89.45 + SLOC1230 + LOC275 + Missed29 +
    +
    +
    1. 1"use strict";
    2. +
    3. 1var comb = require("comb"),
    4. +
    5. isFunction = comb.isFunction,
    6. +
    7. argsToArray = comb.argsToArray,
    8. +
    9. array = comb.array,
    10. +
    11. isArray = comb.isArray,
    12. +
    13. isString = comb.isString,
    14. +
    15. isUndefined = comb.isUndefined,
    16. +
    17. isNumber = comb.isNumber,
    18. +
    19. toArray = comb.array.toArray,
    20. +
    21. hitch = comb.hitch,
    22. +
    23. format = comb.string.format,
    24. +
    25. Dataset = require("../dataset"),
    26. +
    27. Promise = comb.Promise,
    28. +
    29. PromiseList = comb.PromiseList,
    30. +
    31. errors = require("../errors"),
    32. +
    33. DatabaseError = errors.DatabaseError,
    34. +
    35. generators = require("./schemaGenerators"),
    36. +
    37. SchemaGenerator = generators.SchemaGenerator,
    38. +
    39. AlterTableGenerator = generators.AlterTableGenerator,
    40. +
    41. sql = require("../sql").sql,
    42. +
    43. Time = sql.Time,
    44. +
    45. TimeStamp = sql.TimeStamp,
    46. +
    47. DateTime = sql.DateTime,
    48. +
    49. Year = sql.Year,
    50. +
    51. Float = sql.Float,
    52. +
    53. Decimal = sql.Decimal,
    54. +
    55. isInstanceOf = comb.isInstanceOf,
    56. +
    57. Identifier = sql.Identifier,
    58. +
    59. QualifiedIdentifier = sql.QualifiedIdentifier,
    60. +
    61. define = comb.define;
    62. -
    63. /**
    64. -
    65. * @private
    66. -
    67. * Array of SQL DDL modification statements for the given table,
    68. -
    69. * corresponding to the DDL changes specified by the operations.
    70. -
    71. * */
    72. -
    73. __alterTableSqlList:function (table, operations) {
    74. -
    75. 84 return new PromiseList(operations.map(hitch(this, "__alterTableSql", table)));
    76. +
    77. +
    78. 1define(null, {
    79. +
    80. instance:{
    81. +
    82. /**@lends patio.Database.prototype*/
    83. +
    84. +
    85. /**@ignore*/
    86. +
    87. constructor:function () {
    88. +
    89. 123 this._super(arguments);
    90. +
    91. 123 this.schemas = {};
    92. },
    93. /**
    94. -
    95. * @private
    96. -
    97. * SQL DDL fragment containing the column creation SQL for the given column.
    98. +
    99. * Adds a column to the specified table. This method expects a column name,
    100. +
    101. * a datatype and optionally a hash with additional constraints and options:
    102. *
    103. -
    104. * @param column
    105. -
    106. */
    107. -
    108. __columnDefinitionSql:function (column) {
    109. -
    110. 644 var sql = [format("%s %s", this.__quoteIdentifier(column.name), this.typeLiteral(column))];
    111. -
    112. 644 column.unique && sql.push(this._static.UNIQUE);
    113. -
    114. 644 (column.allowNull === false || column["null"] === false) && sql.push(this._static.NOT_NULL);
    115. -
    116. 644 (column.allowNull === true || column["null"] === true) && sql.push(this._static.NULL);
    117. -
    118. 644 !isUndefined(column["default"]) && sql.push(format(" DEFAULT %s", this.literal(column["default"])));
    119. -
    120. 644 column.primaryKey && sql.push(this._static.PRIMARY_KEY);
    121. -
    122. 644 column.autoIncrement && sql.push(" " + this.autoIncrementSql);
    123. -
    124. 644 column.table && sql.push(this.__columnReferencesColumnConstraintSql(column));
    125. -
    126. 644 return sql.join("");
    127. +
    128. * <p>
    129. +
    130. * This method is a shortcut to {@link patio.Database#alterTable} with an
    131. +
    132. * addColumn call.
    133. +
    134. * </p>
    135. +
    136. *
    137. +
    138. *
    139. +
    140. * @example
    141. +
    142. * //Outside of a table
    143. +
    144. * //ALTER TABLE test ADD COLUMN name text UNIQUE'
    145. +
    146. * DB.addColumn("test", "name", "text", {unique : true});
    147. +
    148. *
    149. +
    150. * @param {String} table the table to add the column to.
    151. +
    152. * @param {String} column the name of the column to add.
    153. +
    154. * @param type datatype of the column
    155. +
    156. * @param {Object} [opts] additional options that can be used when adding a column.
    157. +
    158. * @param {Boolean} [opts.primaryKey] set to true if this column is a primary key.
    159. +
    160. * @param {Boolean} [opts.allowNull] whether or not this column should allow null.
    161. +
    162. * @param {Boolean} [opts.unique] set to true to add a UNIQUE constraint to a column,
    163. +
    164. *
    165. +
    166. * @return {Promise} a promise that is resolved when the ADD COLUMN action is complete.
    167. +
    168. **/
    169. +
    170. addColumn:function (table, column, type, opts) {
    171. +
    172. 9 var args = argsToArray(arguments).slice(1);
    173. +
    174. 9 return this.alterTable(table, function () {
    175. +
    176. 9 this.addColumn.apply(this, args);
    177. +
    178. });
    179. },
    180. /**
    181. -
    182. * @private
    183. -
    184. * SQL DDL fragment containing the column creation
    185. -
    186. * SQL for all given columns, used inside a CREATE TABLE block.
    187. -
    188. */
    189. -
    190. __columnListSql:function (generator) {
    191. -
    192. 218 return generator.columns.map(hitch(this, "__columnDefinitionSql")).concat(generator.constraints.map(hitch(this, "__constraintDefinitionSql"))).join(this._static.COMMA_SEPARATOR);
    193. +
    194. * Adds an index to a table for the given columns
    195. +
    196. *
    197. +
    198. * <p>
    199. +
    200. * This method is a shortcut to {@link patio.Database#alterTable} with an
    201. +
    202. * addIndex call.
    203. +
    204. * </p>
    205. +
    206. * @example
    207. +
    208. * DB.addIndex("test", "name", {unique : true});
    209. +
    210. * //=> 'CREATE UNIQUE INDEX test_name_index ON test (name)'
    211. +
    212. * DB.addIndex("test", ["one", "two"]);
    213. +
    214. * //=> ''CREATE INDEX test_one_two_index ON test (one, two)''
    215. +
    216. *
    217. +
    218. * @param {String} table the table to add the index to.
    219. +
    220. * @param {String|String[]} columns the name of the column/s to create an index for.
    221. +
    222. * @param {Object} [options] additional options that can be used when adding an index.
    223. +
    224. * @param {Boolean} [options.unique] set to true if this this index should have a UNIQUE constraint.
    225. +
    226. * @param {Boolean} [options.ignoreErrors] set to true to ignore errors.
    227. +
    228. *
    229. +
    230. * @return {Promise} a promise that is resolved when the CREATE INDEX action is complete.
    231. +
    232. * */
    233. +
    234. addIndex:function (table, columns, options) {
    235. +
    236. 4 options = options || {};
    237. +
    238. 4 var ignoreErrors = options.ignoreErrors === true;
    239. +
    240. 4 var ret = new Promise();
    241. +
    242. 4 this.alterTable(table,function () {
    243. +
    244. 4 this.addIndex(columns, options);
    245. +
    246. }).then(ret, function (err) {
    247. +
    248. 0 if (!ignoreErrors) {
    249. +
    250. 0 ret.errback(err);
    251. +
    252. } else {
    253. +
    254. 0 ret.callback();
    255. +
    256. }
    257. +
    258. });
    259. +
    260. 4 return ret.promise();
    261. },
    262. /**
    263. -
    264. * @private
    265. -
    266. *SQL DDL fragment for column foreign key references (column constraints)
    267. -
    268. */
    269. -
    270. __columnReferencesColumnConstraintSql:function (column) {
    271. -
    272. 30 return this.__columnReferencesSql(column);
    273. +
    274. * Removes a column from the specified table.
    275. +
    276. * <p>
    277. +
    278. * This method is a shortcut to {@link patio.Database#alterTable} with an
    279. +
    280. * dropColumn call.
    281. +
    282. * </p>
    283. +
    284. *
    285. +
    286. * @example
    287. +
    288. * DB.dropColumn("items", "category");
    289. +
    290. * //=> 'ALTER TABLE items DROP COLUMN category',
    291. +
    292. *
    293. +
    294. * @param {String|patio.sql.Identifier} table the table to alter.
    295. +
    296. * @param {String|patio.sql.Identifier} column the column to drop.
    297. +
    298. *
    299. +
    300. * @return {Promise} a promise that is resolved once the DROP COLUMN action is complete.
    301. +
    302. * */
    303. +
    304. dropColumn:function (table, column) {
    305. +
    306. 3 column = argsToArray(arguments).slice(1);
    307. +
    308. 3 return this.alterTable(table, function () {
    309. +
    310. 3 this.dropColumn.apply(this, column);
    311. +
    312. });
    313. },
    314. /**
    315. -
    316. * @private
    317. -
    318. * SQL DDL fragment for column foreign key references
    319. -
    320. */
    321. -
    322. __columnReferencesSql:function (column) {
    323. -
    324. 38 var sql = format(" REFERENCES %s", this.__quoteSchemaTable(column.table));
    325. -
    326. 38 column.key && (sql += format("(%s)", array.toArray(column.key).map(this.__quoteIdentifier, this).join(this._static.COMMA_SEPARATOR)));
    327. -
    328. 38 column.onDelete && (sql += format(" ON DELETE %s", this.__onDeleteClause(column.onDelete)));
    329. -
    330. 38 column.onUpdate && (sql += format(" ON UPDATE %s", this.__onUpdateClause(column.onUpdate)));
    331. -
    332. 38 column.deferrable && (sql += " DEFERRABLE INITIALLY DEFERRED");
    333. -
    334. 38 return sql;
    335. +
    336. * Removes an index for the given table and column/s.
    337. +
    338. *
    339. +
    340. * <p>
    341. +
    342. * This method is a shortcut to {@link patio.Database#alterTable} with an
    343. +
    344. * dropIndex call.
    345. +
    346. * </p>
    347. +
    348. *
    349. +
    350. * @example
    351. +
    352. * DB.dropIndex("posts", "title");
    353. +
    354. * //=>'DROP INDEX posts_title_index
    355. +
    356. * DB.dropIndex("posts", ["author", "title"]);
    357. +
    358. * //'DROP INDEX posts_author_title_index'
    359. +
    360. *
    361. +
    362. * @param {String|patio.sql.Identifier} table the table to alter.
    363. +
    364. * @param {String|patio.sql.Identifier} column the name of the column/s the index was created from.
    365. +
    366. *
    367. +
    368. * @return {Promise} a promise that is resolved once the DROP INDEX action is complete.
    369. +
    370. * */
    371. +
    372. dropIndex:function (table, columns, options) {
    373. +
    374. 1 var args = argsToArray(arguments).slice(1);
    375. +
    376. 1 return this.alterTable(table, function () {
    377. +
    378. 1 this.dropIndex.apply(this, args);
    379. +
    380. });
    381. },
    382. /**
    383. -
    384. * @private
    385. -
    386. * SQL DDL fragment for table foreign key references (table constraints)
    387. +
    388. * Renames a column in the specified table.
    389. +
    390. *
    391. +
    392. * <p>
    393. +
    394. * This method is a shortcut to {@link patio.Database#alterTable} with an
    395. +
    396. * renameColumn call.
    397. +
    398. * </p>
    399. +
    400. *
    401. +
    402. * @example
    403. +
    404. * DB.renameColumn("items", "cntr", "counter");
    405. +
    406. * //=> ALTER TABLE items RENAME COLUMN cntr TO counter
    407. +
    408. *
    409. +
    410. * @param {String|patio.sql.Identifier} table the table to alter.
    411. +
    412. * @param {String|patio.sql.Identifier} column the name of the column to rename.
    413. +
    414. * @param {String|patio.sql.Identifier} newColumn the new name of the column.
    415. +
    416. *
    417. +
    418. * @return {Promise} a promise that is resolved once the RENAME COLUMN action is complete.
    419. * */
    420. -
    421. __columnReferencesTableConstraintSql:function (constraint) {
    422. -
    423. 6 return format("FOREIGN KEY %s%s", this.literal(constraint.columns.map(function (c) {
    424. -
    425. 6 return isString(c) ? sql.stringToIdentifier(c) : c;
    426. -
    427. })), this.__columnReferencesSql(constraint));
    428. +
    429. renameColumn:function (table, column, newColumn) {
    430. +
    431. 4 var args = argsToArray(arguments).slice(1);
    432. +
    433. 4 return this.alterTable(table, function () {
    434. +
    435. 4 this.renameColumn.apply(this, args);
    436. +
    437. });
    438. },
    439. -
    440. /**
    441. -
    442. * @private
    443. -
    444. * SQL DDL fragment specifying a constraint on a table.
    445. -
    446. */
    447. -
    448. __constraintDefinitionSql:function (constraint) {
    449. -
    450. 6 var ret = [constraint.name ? format("CONSTRAINT %s ", this.__quoteIdentifier(constraint.name)) : ""];
    451. -
    452. 6 switch (constraint.type) {
    453. -
    454. case "check":
    455. -
    456. 0 var check = constraint.check;
    457. -
    458. 0 ret.push(format("CHECK %s", this.__filterExpr(isArray(check) && check.length === 1 ? check[0] : check)));
    459. -
    460. 0 break;
    461. -
    462. case "primaryKey":
    463. -
    464. 0 ret.push(format("PRIMARY KEY %s", this.literal(constraint.columns.map(function (c) {
    465. -
    466. 0 return isString(c) ? sql.stringToIdentifier(c) : c;
    467. -
    468. }))));
    469. -
    470. 0 break;
    471. -
    472. case "foreignKey":
    473. -
    474. 6 ret.push(this.__columnReferencesTableConstraintSql(constraint));
    475. -
    476. 6 break;
    477. -
    478. case "unique":
    479. -
    480. 0 ret.push(format("UNIQUE %s", this.literal(constraint.columns.map(function (c) {
    481. -
    482. 0 return isString(c) ? sql.stringToIdentifier(c) : c;
    483. -
    484. }))));
    485. -
    486. 0 break;
    487. -
    488. default:
    489. -
    490. 0 throw new DatabaseError(format("Invalid constriant type %s, should be 'check', 'primaryKey', foreignKey', or 'unique'", constraint.type));
    491. -
    492. }
    493. -
    494. 6 return ret.join("");
    495. -
    496. },
    497. /**
    498. -
    499. * @private
    500. -
    501. * Execute the create table statements using the generator.
    502. +
    503. *Sets the default value for the given column in the given table:
    504. +
    505. *
    506. +
    507. * <p>
    508. +
    509. * This method is a shortcut to {@link patio.Database#alterTable} with an
    510. +
    511. * setColumnDefault call.
    512. +
    513. * </p>
    514. +
    515. *
    516. +
    517. * @example
    518. +
    519. * DB.setColumnDefault("items", "category", "misc");
    520. +
    521. * //=> ALTER TABLE items ALTER COLUMN category SET DEFAULT 'misc'
    522. +
    523. *
    524. +
    525. * @param {String|patio.sql.Identifier} table the table to alter.
    526. +
    527. * @param {String|patio.sql.Identifier} column the name of the column to set the DEFAULT on.
    528. +
    529. * @param def the new default value of the column.
    530. +
    531. *
    532. +
    533. * @return {Promise} a promise that is resolved once the SET DEFAULT action is complete.
    534. * */
    535. -
    536. __createTableFromGenerator:function (name, generator, options) {
    537. -
    538. 218 return this.executeDdl(this.__createTableSql(name, generator, options));
    539. +
    540. setColumnDefault:function (table, column, def) {
    541. +
    542. 1 var args = argsToArray(arguments).slice(1);
    543. +
    544. 1 return this.alterTable(table, function () {
    545. +
    546. 1 this.setColumnDefault.apply(this, args);
    547. +
    548. });
    549. },
    550. -
    551. /**
    552. -
    553. * @private
    554. -
    555. * Execute the create index statements using the generator.
    556. -
    557. * */
    558. -
    559. __createTableIndexesFromGenerator:function (name, generator, options) {
    560. -
    561. 218 var e = options.ignoreIndexErrors;
    562. -
    563. 218 var ret = new Promise();
    564. -
    565. 218 var promises = generator.indexes.map(function (index) {
    566. -
    567. 18 var ps = this.__indexSqlList(name, [index]).map(this.executeDdl, this);
    568. -
    569. 18 return new PromiseList(ps);
    570. -
    571. }, this);
    572. -
    573. 218 if (promises.length) {
    574. -
    575. 16 new PromiseList(promises).then(ret, hitch(ret, e ? "callback" : "errback"));
    576. -
    577. } else {
    578. -
    579. 202 ret.callback();
    580. -
    581. }
    582. -
    583. 218 return ret.promise();
    584. -
    585. },
    586. /**
    587. -
    588. * @private
    589. -
    590. * DDL statement for creating a table with the given name, columns, and options
    591. +
    592. * Set the data type for the given column in the given table:
    593. +
    594. * <p>
    595. +
    596. * This method is a shortcut to {@link patio.Database#alterTable} with an
    597. +
    598. * setColumnType call.
    599. +
    600. * </p>
    601. +
    602. *
    603. +
    604. * @example
    605. +
    606. * DB.setColumnType("items", "category", String);
    607. +
    608. * //=> ALTER TABLE items ALTER COLUMN category TYPE varchar(255)
    609. +
    610. *
    611. +
    612. * @param {String|patio.sql.Identifier} table the table to alter.
    613. +
    614. * @param {String|patio.sql.Identifier} column the name of the column to set the TYPE on.
    615. +
    616. * @param type the datatype of the column.
    617. +
    618. *
    619. +
    620. * @return {Promise} a promise that is resolved once the SET TYPE action is complete.
    621. * */
    622. -
    623. __createTableSql:function (name, generator, options) {
    624. -
    625. 218 return format("CREATE %sTABLE %s (%s)", options.temp ? this.temporaryTableSql : "", this.__quoteSchemaTable(name), this.__columnListSql(generator));
    626. +
    627. setColumnType:function (table, column, type) {
    628. +
    629. 3 var args = argsToArray(arguments).slice(1);
    630. +
    631. 3 return this.alterTable(table, function () {
    632. +
    633. 3 this.setColumnType.apply(this, args);
    634. +
    635. });
    636. },
    637. +
    638. /**
    639. -
    640. * @private
    641. -
    642. * Default index name for the table and columns, may be too long
    643. -
    644. * for certain databases.
    645. -
    646. */
    647. -
    648. __defaultIndexName:function (tableName, columns) {
    649. -
    650. 25 var parts = this.__schemaAndTable(tableName);
    651. -
    652. 25 var schema = parts[0], table = parts[1];
    653. -
    654. 25 var index = [];
    655. -
    656. 25 if (schema && schema !== this.defaultSchema) {
    657. -
    658. 0 index.push(schema);
    659. +
    660. * Alters the given table with the specified block.
    661. +
    662. * <p>
    663. +
    664. * <b>NOTE:</b> The block is invoked in the scope of the table that is being altered. The block
    665. +
    666. * is also called with the table as the first argument. Within the block you must use
    667. +
    668. * <b>this</b>(If the block has not been bound to a different scope), or the table object
    669. +
    670. * that is passed in for all alter table operations. See {@link patio.AlterTableGenerator} for
    671. +
    672. * avaiable operations.
    673. +
    674. * </p>
    675. +
    676. *
    677. +
    678. * <p>
    679. +
    680. * <b>Note</b> that addColumn accepts all the options available for column
    681. +
    682. * definitions using createTable, and addIndex accepts all the options
    683. +
    684. * available for index definition.
    685. +
    686. * </p>
    687. +
    688. *
    689. +
    690. * @example
    691. +
    692. * //using the table object
    693. +
    694. * DB.alterTable("items", function(table){
    695. +
    696. * //you must use the passed in table object.
    697. +
    698. * table.addColumn("category", "text", {default : 'javascript'});
    699. +
    700. * table.dropColumn("category");
    701. +
    702. * table.renameColumn("cntr", "counter");
    703. +
    704. * table.setColumnType("value", "float");
    705. +
    706. * table.setColumnDefault("value", "float");
    707. +
    708. * table.addIndex(["group", "category"]);
    709. +
    710. * table.dropIndex [:group, :category]
    711. +
    712. * });
    713. +
    714. *
    715. +
    716. * //using this
    717. +
    718. * DB.alterTable("items", function(){
    719. +
    720. * this.addColumn("category", "text", {default : 'javascript'});
    721. +
    722. * this.dropColumn("category");
    723. +
    724. * this.renameColumn("cntr", "counter");
    725. +
    726. * this.setColumnType("value", "float");
    727. +
    728. * this.setColumnDefault("value", "float");
    729. +
    730. * this.addIndex(["group", "category"]);
    731. +
    732. * this.dropIndex [:group, :category]
    733. +
    734. * });
    735. +
    736. *
    737. +
    738. * //This will not work
    739. +
    740. * DB.alterTable("items", comb.hitch(someObject, function(){
    741. +
    742. * //This is called in the scope of someObject so this
    743. +
    744. * //will not work and will throw an error
    745. +
    746. * this.addColumn("category", "text", {default : 'javascript'});
    747. +
    748. * }));
    749. +
    750. *
    751. +
    752. * //This will work
    753. +
    754. * DB.alterTable("items", comb.hitch(someObject, function(table){
    755. +
    756. * //This is called in the scope of someObject so you must
    757. +
    758. * //use the table argument
    759. +
    760. * table.category("text", {default : 'javascript'});
    761. +
    762. * }));
    763. +
    764. *
    765. +
    766. *
    767. +
    768. * @param {String|patio.sql.Identifier} table to the table to perform the ALTER TABLE operations on.
    769. +
    770. * @param {Function} block the block to invoke for the ALTER TABLE operations
    771. +
    772. *
    773. +
    774. * @return {Promise} a promise that is resolved once all ALTER TABLE operations have completed.
    775. +
    776. * */
    777. +
    778. alterTable:function (name, generator, block) {
    779. +
    780. 84 if (isFunction(generator)) {
    781. +
    782. 84 block = generator;
    783. +
    784. 84 generator = new AlterTableGenerator(this, block);
    785. }
    786. -
    787. 25 index.push(table);
    788. -
    789. 25 index = index.concat(columns.map(function (c) {
    790. -
    791. 28 return isString(c) ? c : this.literal(c).replace(/\W/g, "");
    792. -
    793. }, this));
    794. -
    795. 25 index.push("index");
    796. -
    797. 25 return index.join(this._static.UNDERSCORE);
    798. -
    799. +
    800. 84 var ret = new Promise();
    801. +
    802. 84 this.__alterTableSqlList(name, generator.operations).then(hitch(this, function (res) {
    803. +
    804. 84 var sqls = array.flatten(res.map(function (r) {
    805. +
    806. 93 return r[1];
    807. +
    808. }));
    809. +
    810. 84 var l = sqls.length;
    811. +
    812. 84 var drop = hitch(this, function (i) {
    813. +
    814. 179 if (i < l) {
    815. +
    816. 95 var sql = sqls[i++];
    817. +
    818. 95 this.executeDdl(sql).then(hitch(this, drop, i), ret);
    819. +
    820. } else {
    821. +
    822. 84 this.removeCachedSchema(name);
    823. +
    824. 84 ret.callback();
    825. +
    826. }
    827. +
    828. });
    829. +
    830. 84 drop(0);
    831. +
    832. }));
    833. +
    834. 84 return ret.promise();
    835. },
    836. /**
    837. -
    838. * @private
    839. -
    840. * The SQL to drop an index for the table.
    841. -
    842. * */
    843. -
    844. __dropIndexSql:function (table, op) {
    845. -
    846. 2 return format("DROP INDEX %s", this.__quoteIdentifier(op.name || this.__defaultIndexName(table, op.columns)));
    847. -
    848. },
    849. -
    850. -
    851. /**
    852. -
    853. * @private
    854. +
    855. * Creates a table with the columns given in the provided block:
    856. +
    857. *
    858. +
    859. * <p>
    860. +
    861. * <b>NOTE:</b> The block is invoked in the scope of the table that is being created. The block
    862. +
    863. * is also called with the table as the first argument. Within the block you must use
    864. +
    865. * <b>this</b>(If the block has not been bound to a different scope), or the table object
    866. +
    867. * that is passed in for all create table operations. See {@link patio.SchemaGenerator} for
    868. +
    869. * available operations.
    870. +
    871. * </p>
    872. +
    873. *
    874. +
    875. *
    876. +
    877. * @example
    878. +
    879. *
    880. +
    881. * //using the table to create the table
    882. +
    883. * DB.createTable("posts", function(table){
    884. +
    885. * table.primaryKey("id");
    886. +
    887. * table.column('title", "text");
    888. +
    889. * //you may also invoke the column name as
    890. +
    891. * //function on the table
    892. +
    893. * table.content(String);
    894. +
    895. * table.index(title);
    896. +
    897. * });
    898. +
    899. *
    900. +
    901. * //using this to create the table
    902. +
    903. * DB.createTable("posts", function(){
    904. +
    905. * this.primaryKey("id");
    906. +
    907. * this.column('title", "text");
    908. +
    909. * //you may also invoke the column name as
    910. +
    911. * //function on the table
    912. +
    913. * this.content(String);
    914. +
    915. * this.index(title);
    916. +
    917. * });
    918. +
    919. *
    920. +
    921. * @param {String|patio.sql.Identifier} name the name of the table to create.
    922. +
    923. * @param {Object} [options] an optional options object
    924. +
    925. * @param {Boolean} [options.temp] set to true if this table is a TEMPORARY table.
    926. +
    927. * @param {Boolean} [options.ignoreIndexErrors] Ignore any errors when creating indexes.
    928. +
    929. * @param {Function} block the block to invoke when creating the table.
    930. +
    931. *
    932. +
    933. * @return {Promise} a promise that is resolved when the CREATE TABLE action is completed.
    934. *
    935. -
    936. * SQL DDL statement to drop the table with the given name.
    937. -
    938. **/
    939. -
    940. __dropTableSql:function (name) {
    941. -
    942. 210 return format("DROP TABLE %s", this.__quoteSchemaTable(name));
    943. -
    944. },
    945. -
    946. -
    947. /**
    948. -
    949. * @private
    950. -
    951. * Proxy the filterExpr call to the dataset, used for creating constraints.
    952. -
    953. * */
    954. -
    955. __filterExpr:function (args, block) {
    956. -
    957. 2 var ds = this.__schemaUtiltyDataset;
    958. -
    959. 2 return ds.literal(ds._filterExpr.apply(ds, arguments));
    960. -
    961. },
    962. -
    963. -
    964. -
    965. /**
    966. -
    967. * @private
    968. -
    969. * SQL DDL statement for creating an index for the table with the given name
    970. -
    971. * and index specifications.
    972. */
    973. -
    974. __indexDefinitionSql:function (tableName, index) {
    975. -
    976. 7 var indexName = index.name || this.__defaultIndexName(tableName, index.columns);
    977. -
    978. 7 if (index.type) {
    979. -
    980. 0 throw new DatabaseError("Index types are not supported for this database");
    981. -
    982. 7 } else if (index.where) {
    983. -
    984. 0 throw new DatabaseError("Partial indexes are not supported for this database");
    985. -
    986. } else {
    987. -
    988. 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) {
    989. -
    990. 8 return isString(c) ? new Identifier(c) : c;
    991. -
    992. })));
    993. +
    994. createTable:function (name, options, block) {
    995. +
    996. 218 if (isFunction(options)) {
    997. +
    998. 208 block = options;
    999. +
    1000. 208 options = {};
    1001. +
    1002. }
    1003. +
    1004. 218 this.removeCachedSchema(name);
    1005. +
    1006. 218 if (isInstanceOf(options, SchemaGenerator)) {
    1007. +
    1008. 0 options = {generator:options};
    1009. }
    1010. +
    1011. 218 var generator = options.generator || new SchemaGenerator(this, block);
    1012. +
    1013. 218 return this.__createTableFromGenerator(name, generator, options)
    1014. +
    1015. .chain(hitch(this, "__createTableIndexesFromGenerator", name, generator, options))
    1016. +
    1017. .promise();
    1018. },
    1019. /**
    1020. -
    1021. * Array of SQL DDL statements, one for each index specification,
    1022. -
    1023. * for the given table.
    1024. -
    1025. */
    1026. -
    1027. __indexSqlList:function (tableName, indexes) {
    1028. -
    1029. 18 return indexes.map(hitch(this, this.__indexDefinitionSql, tableName));
    1030. +
    1031. * Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.
    1032. +
    1033. * <p>
    1034. +
    1035. * See {@link patio.Database#createTable} for parameter types.
    1036. +
    1037. * </p>
    1038. +
    1039. *
    1040. +
    1041. * @example
    1042. +
    1043. * // DROP TABLE a
    1044. +
    1045. * // CREATE TABLE a (a integer)
    1046. +
    1047. * DB.forceCreateTable("a", function(){
    1048. +
    1049. * this.a("integer");
    1050. +
    1051. * });
    1052. +
    1053. *
    1054. +
    1055. **/
    1056. +
    1057. forceCreateTable:function (name, options, block) {
    1058. +
    1059. 18 return this.dropTable(name)
    1060. +
    1061. .chainBoth(hitch(this, this.createTable, name, options, block))
    1062. +
    1063. .promise();
    1064. },
    1065. -
    1066. /**
    1067. -
    1068. * @private
    1069. -
    1070. * SQL DDL ON DELETE fragment to use, based on the given action.
    1071. -
    1072. *The following actions are recognized:
    1073. -
    1074. * <ul>
    1075. -
    1076. * <li>cascade - Delete rows referencing this row.</li>
    1077. -
    1078. * <li>noAction (default) - Raise an error if other rows reference this
    1079. -
    1080. * row, allow deferring of the integrity check.
    1081. -
    1082. * </li>
    1083. -
    1084. * <li>restrict - Raise an error if other rows reference this row,
    1085. -
    1086. * but do not allow deferring the integrity check.</li>
    1087. -
    1088. * <li> setDefault - Set columns referencing this row to their default value.</li>
    1089. -
    1090. * <li>setNull - Set columns referencing this row to NULL.</li>
    1091. -
    1092. * </ul>
    1093. -
    1094. */
    1095. -
    1096. __onDeleteClause:function (action) {
    1097. -
    1098. 23 return this._static[action.toUpperCase()] || this._static.NO_ACTION;
    1099. -
    1100. },
    1101. /**
    1102. -
    1103. * @private
    1104. -
    1105. * SQL DDL ON UPDATE fragment to use, based on the given action.
    1106. -
    1107. *The following actions are recognized:
    1108. -
    1109. * <ul>
    1110. -
    1111. * <li>cascade - Delete rows referencing this row.</li>
    1112. -
    1113. * <li>noAction (default) - Raise an error if other rows reference this
    1114. -
    1115. * row, allow deferring of the integrity check.
    1116. -
    1117. * </li>
    1118. -
    1119. * <li>restrict - Raise an error if other rows reference this row,
    1120. -
    1121. * but do not allow deferring the integrity check.</li>
    1122. -
    1123. * <li> setDefault - Set columns referencing this row to their default value.</li>
    1124. -
    1125. * <li>setNull - Set columns referencing this row to NULL.</li>
    1126. -
    1127. * </ul>
    1128. +
    1129. * Creates the table unless the table already exists.
    1130. +
    1131. * <p>
    1132. +
    1133. * See {@link patio.Database#createTable} for parameter types.
    1134. +
    1135. * </p>
    1136. */
    1137. -
    1138. __onUpdateClause:function (action) {
    1139. -
    1140. 1 return this._static[action.toUpperCase()] || this._static.NO_ACTION;
    1141. +
    1142. createTableUnlessExists:function (name, options, block) {
    1143. +
    1144. 0 var ret = new Promise();
    1145. +
    1146. 0 this.tableExists(name).then(hitch(this, function (exists) {
    1147. +
    1148. 0 if (!exists) {
    1149. +
    1150. 0 this.createTable(name, options, block).then(ret);
    1151. +
    1152. } else {
    1153. +
    1154. 0 ret.callback();
    1155. +
    1156. }
    1157. +
    1158. }), ret);
    1159. +
    1160. 0 return ret.promise();
    1161. },
    1162. /**
    1163. -
    1164. * @private
    1165. -
    1166. * Proxy the quoteSchemaTable method to the dataset
    1167. -
    1168. * */
    1169. -
    1170. __quoteSchemaTable:function (table) {
    1171. -
    1172. 2212 return this.__schemaUtiltyDataset.quoteSchemaTable(table);
    1173. +
    1174. * Creates a view, replacing it if it already exists:
    1175. +
    1176. * @example
    1177. +
    1178. * DB.createOrReplaceView("cheapItems", "SELECT * FROM items WHERE price < 100");
    1179. +
    1180. * //=> CREATE OR REPLACE VIEW cheapItems AS SELECT * FROM items WHERE price < 100
    1181. +
    1182. * DB.createOrReplaceView("miscItems", DB[:items].filter({category : 'misc'}));
    1183. +
    1184. * //=> CREATE OR REPLACE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'
    1185. +
    1186. *
    1187. +
    1188. * @param {String|patio.sql.Identifier} name the name of the view to create.
    1189. +
    1190. * @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the
    1191. +
    1192. * view.
    1193. +
    1194. *
    1195. +
    1196. * @return {Promise} a promise that is resolved when the CREATE OR REPLACE VIEW action is complete.
    1197. +
    1198. **/
    1199. +
    1200. createOrReplaceView:function (name, source) {
    1201. +
    1202. 4 if (isInstanceOf(source, Dataset)) {
    1203. +
    1204. 2 source = source.sql;
    1205. +
    1206. }
    1207. +
    1208. 4 var ret = new Promise();
    1209. +
    1210. 4 this.executeDdl(format("CREATE OR REPLACE VIEW %s AS %s", this.__quoteSchemaTable(name), source)).then(hitch(this, function () {
    1211. +
    1212. 4 this.removeCachedSchema(name);
    1213. +
    1214. 4 ret.callback();
    1215. +
    1216. }), ret);
    1217. +
    1218. 4 return ret.promise();
    1219. },
    1220. /**
    1221. -
    1222. * @private
    1223. -
    1224. * Proxy the quoteIdentifier method to the dataset, used for quoting tables and columns.
    1225. -
    1226. * */
    1227. -
    1228. __quoteIdentifier:function (v) {
    1229. -
    1230. 846 return this.__schemaUtiltyDataset.quoteIdentifier(v);
    1231. +
    1232. * Creates a view based on a dataset or an SQL string:
    1233. +
    1234. * @example
    1235. +
    1236. * DB.createView("cheapItems", "SELECT * FROM items WHERE price < 100");
    1237. +
    1238. * //=> CREATE VIEW cheapItems AS SELECT * FROM items WHERE price < 100
    1239. +
    1240. * DB.createView("miscItems", DB[:items].filter({category : 'misc'}));
    1241. +
    1242. * //=> CREATE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'
    1243. +
    1244. *
    1245. +
    1246. * @param {String|patio.sql.Identifier} name the name of the view to create.
    1247. +
    1248. * @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the
    1249. +
    1250. * view.
    1251. +
    1252. **/
    1253. +
    1254. createView:function (name, source) {
    1255. +
    1256. 4 if (isInstanceOf(source, Dataset)) {
    1257. +
    1258. 2 source = source.sql;
    1259. +
    1260. }
    1261. +
    1262. 4 return this.executeDdl(format("CREATE VIEW %s AS %s", this.__quoteSchemaTable(name), source));
    1263. },
    1264. /**
    1265. -
    1266. * @private
    1267. -
    1268. * SQL DDL statement for renaming a table.
    1269. -
    1270. * */
    1271. -
    1272. __renameTableSql:function (name, newName) {
    1273. -
    1274. 1 return format("ALTER TABLE %s RENAME TO %s", this.__quoteSchemaTable(name), this.__quoteSchemaTable(newName));
    1275. -
    1276. },
    1277. +
    1278. * Drops one or more tables corresponding to the given names.
    1279. +
    1280. *
    1281. +
    1282. * @example
    1283. +
    1284. * DB.dropTable("test");
    1285. +
    1286. * //=>'DROP TABLE test'
    1287. +
    1288. * DB.dropTable("a", "bb", "ccc");
    1289. +
    1290. * //=>'DROP TABLE a',
    1291. +
    1292. * //=>'DROP TABLE bb',
    1293. +
    1294. * //=>'DROP TABLE ccc'
    1295. +
    1296. *
    1297. +
    1298. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the tables
    1299. +
    1300. * to drop.
    1301. +
    1302. *
    1303. +
    1304. * @return {Promise} a promise that is resolved once all tables have been dropped.
    1305. +
    1306. **/
    1307. +
    1308. dropTable:function (names) {
    1309. +
    1310. 81 var ret = new Promise(), l = names.length;
    1311. +
    1312. 81 if (isArray(names)) {
    1313. +
    1314. 48 var drop = hitch(this, function (i) {
    1315. +
    1316. 104 if (i < l) {
    1317. +
    1318. 65 var name = names[i++];
    1319. +
    1320. 65 this.executeDdl(this.__dropTableSql(name)).then(hitch(this, function () {
    1321. +
    1322. 56 this.removeCachedSchema(name);
    1323. +
    1324. 56 drop(i);
    1325. +
    1326. }), ret);
    1327. +
    1328. } else {
    1329. +
    1330. 39 ret.callback();
    1331. +
    1332. }
    1333. +
    1334. });
    1335. +
    1336. 48 drop(0);
    1337. +
    1338. 48 return ret.promise();
    1339. +
    1340. } else {
    1341. +
    1342. 33 return this.dropTable(argsToArray(arguments).filter(function (t) {
    1343. +
    1344. 38 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
    1345. +
    1346. }));
    1347. -
    1348. /**
    1349. -
    1350. * @private
    1351. -
    1352. * Remove the cached schemaUtilityDataset, because the identifier
    1353. -
    1354. * quoting has changed.
    1355. -
    1356. */
    1357. -
    1358. __resetSchemaUtilityDataset:function () {
    1359. -
    1360. 0 this.__schemaUtiltyDs = null;
    1361. +
    1362. }
    1363. },
    1364. /**
    1365. -
    1366. * @private
    1367. -
    1368. * Split the schema information from the table
    1369. -
    1370. * */
    1371. -
    1372. __schemaAndTable:function (tableName) {
    1373. -
    1374. 244 return this.__schemaUtiltyDataset.schemaAndTable(tableName);
    1375. +
    1376. * Forcible drops one or more tables corresponding to the given names, ignoring errors.
    1377. +
    1378. *
    1379. +
    1380. * @example
    1381. +
    1382. * DB.dropTable("test");
    1383. +
    1384. * //=>'DROP TABLE test'
    1385. +
    1386. * DB.dropTable("a", "bb", "ccc");
    1387. +
    1388. * //=>'DROP TABLE a',
    1389. +
    1390. * //=>'DROP TABLE bb',
    1391. +
    1392. * //=>'DROP TABLE ccc'
    1393. +
    1394. *
    1395. +
    1396. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the tables
    1397. +
    1398. * to drop.
    1399. +
    1400. *
    1401. +
    1402. * @return {Promise} a promise that is resolved once all tables have been dropped.
    1403. +
    1404. **/
    1405. +
    1406. forceDropTable:function (names) {
    1407. +
    1408. 158 var ret = new Promise(), l = names.length;
    1409. +
    1410. 158 if (isArray(names)) {
    1411. +
    1412. 96 var drop = hitch(this, function (i) {
    1413. +
    1414. 241 if (i < l) {
    1415. +
    1416. 145 var name = names[i++];
    1417. +
    1418. 145 this.executeDdl(this.__dropTableSql(name)).both(hitch(this, function () {
    1419. +
    1420. 145 this.removeCachedSchema(name);
    1421. +
    1422. 145 drop(i);
    1423. +
    1424. }));
    1425. +
    1426. } else {
    1427. +
    1428. 96 ret.callback();
    1429. +
    1430. }
    1431. +
    1432. });
    1433. +
    1434. 96 drop(0);
    1435. +
    1436. } else {
    1437. +
    1438. 62 this.forceDropTable(argsToArray(arguments).filter(function (t) {
    1439. +
    1440. 62 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
    1441. +
    1442. })).both(ret);
    1443. +
    1444. }
    1445. +
    1446. 158 return ret.promise();
    1447. },
    1448. /**
    1449. -
    1450. * @private
    1451. -
    1452. * Return true if the given column schema represents an autoincrementing primary key.
    1453. +
    1454. * Drops one or more views corresponding to the given names.
    1455. *
    1456. -
    1457. */
    1458. -
    1459. _schemaAutoincrementingPrimaryKey:function (schema) {
    1460. -
    1461. 0 return !!schema.primaryKey;
    1462. +
    1463. * @example
    1464. +
    1465. * DB.dropView("test_view");
    1466. +
    1467. * //=>'DROP VIEW test_view'
    1468. +
    1469. * DB.dropTable("test_view_1", "test_view_2", "test_view_3");
    1470. +
    1471. * //=>'DROP VIEW test_view_1',
    1472. +
    1473. * //=>'DROP VIEW test_view_2',
    1474. +
    1475. * //=>'DROP VIEW test_view_3'
    1476. +
    1477. *
    1478. +
    1479. * @param {String|String[]|essom.sql.Identifier|essom.sql.Identifier[]} names the names of the views
    1480. +
    1481. * to drop.
    1482. +
    1483. *
    1484. +
    1485. * @return {Promise} a promise that is resolved once the view/s have been dropped.
    1486. +
    1487. **/
    1488. +
    1489. dropView:function (names) {
    1490. +
    1491. 8 var ret = new Promise(), l = names.length;
    1492. +
    1493. 8 if (isArray(names)) {
    1494. +
    1495. 4 var drop = hitch(this, function (i) {
    1496. +
    1497. 8 if (i < l) {
    1498. +
    1499. 4 var name = names[i++];
    1500. +
    1501. 4 this.executeDdl(format("DROP VIEW %s", this.__quoteSchemaTable(name))).then(hitch(this, function () {
    1502. +
    1503. 4 this.removeCachedSchema(name);
    1504. +
    1505. 4 drop(i);
    1506. +
    1507. }), ret);
    1508. +
    1509. } else {
    1510. +
    1511. 4 ret.callback();
    1512. +
    1513. }
    1514. +
    1515. });
    1516. +
    1517. 4 drop(0);
    1518. +
    1519. 4 return ret.promise();
    1520. +
    1521. } else {
    1522. +
    1523. 4 return this.dropView(argsToArray(arguments).filter(function (t) {
    1524. +
    1525. 4 return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);
    1526. +
    1527. }));
    1528. +
    1529. +
    1530. }
    1531. },
    1532. /**
    1533. -
    1534. * @private
    1535. -
    1536. * SQL fragment specifying the type of a given column.
    1537. -
    1538. * */
    1539. -
    1540. typeLiteral:function (column) {
    1541. -
    1542. 690 return this.__typeLiteralGeneric(column);
    1543. +
    1544. * Renames a table.
    1545. +
    1546. *
    1547. +
    1548. * @example
    1549. +
    1550. * comb.executeInOrder(DB, function(DB){
    1551. +
    1552. * DB.tables(); //=> ["items"]
    1553. +
    1554. * DB.renameTable("items", "old_items");
    1555. +
    1556. * //=>'ALTER TABLE items RENAME TO old_items'
    1557. +
    1558. * DB.tables; //=> ["old_items"]
    1559. +
    1560. *});
    1561. +
    1562. *
    1563. +
    1564. * @param {String|patio.sql.Identifier} name the name of the table to rename
    1565. +
    1566. * @param {String|patio.sql.Identifier} newName the new name of the table
    1567. +
    1568. * @return {Promise} a promise that is resolved once the table is renamed.
    1569. +
    1570. **/
    1571. +
    1572. renameTable:function (name, newName) {
    1573. +
    1574. 2 return this.executeDdl(this.__renameTableSql(name, newName)).chain(hitch(this, function () {
    1575. +
    1576. 2 this.removeCachedSchema(name);
    1577. +
    1578. })).promise();
    1579. +
    1580. },
    1581. /**
    1582. * @private
    1583. -
    1584. * SQL fragment specifying the full type of a column,
    1585. -
    1586. * consider the type with possible modifiers.
    1587. -
    1588. */
    1589. -
    1590. __typeLiteralGeneric:function (column) {
    1591. -
    1592. 690 var type = column.type;
    1593. -
    1594. 690 var meth = "__typeLiteralGeneric";
    1595. -
    1596. 690 var isStr = isString(type);
    1597. -
    1598. 690 var proper = isStr ? type.charAt(0).toUpperCase() + type.substr(1) : null;
    1599. -
    1600. 690 if (type === String || (isStr && type.match(/string/i))) {
    1601. -
    1602. 198 meth += "String";
    1603. -
    1604. 492 } else if ((isStr && type.match(/number/i)) || type === Number) {
    1605. -
    1606. 12 meth += "Numeric";
    1607. -
    1608. 480 } else if ((isStr && type.match(/datetime/i)) || type === DateTime) {
    1609. -
    1610. 8 meth += "DateTime";
    1611. -
    1612. 472 } else if ((isStr && type.match(/date/i)) || type === Date) {
    1613. -
    1614. 11 meth += "Date";
    1615. -
    1616. 461 } else if ((isStr && type.match(/year/i)) || type === Year) {
    1617. -
    1618. 2 meth += "Year";
    1619. -
    1620. 459 } else if ((isStr && type.match(/timestamp/i))|| type === TimeStamp) {
    1621. -
    1622. 4 meth += "Timestamp";
    1623. -
    1624. 455 } else if ((isStr && type.match(/time/i)) || type === Time) {
    1625. -
    1626. 2 meth += "Time";
    1627. -
    1628. 453 } else if ((isStr && type.match(/decimal/i)) || type === Decimal) {
    1629. -
    1630. 2 meth += "Decimal";
    1631. -
    1632. 451 } else if ((isStr && type.match(/float/i)) || type === Float) {
    1633. -
    1634. 15 meth += "Float";
    1635. -
    1636. 436 } else if ((isStr && type.match(/boolean/i)) || type === Boolean) {
    1637. -
    1638. 5 meth += "Boolean";
    1639. -
    1640. 431 } else if ((isStr && type.match(/buffer/i)) || type === Buffer) {
    1641. -
    1642. 29 meth += "Blob";
    1643. -
    1644. 402 } else if (isStr && isFunction(this[meth + proper])) {
    1645. -
    1646. 138 meth += proper;
    1647. -
    1648. } else {
    1649. -
    1650. 264 return this.__typeLiteralSpecific(column);
    1651. +
    1652. * The SQL to execute to modify the DDL for the given table name. op
    1653. +
    1654. * should be one of the operations returned by the AlterTableGenerator.
    1655. +
    1656. * */
    1657. +
    1658. __alterTableSql:function (table, op) {
    1659. +
    1660. 83 var ret = new Promise();
    1661. +
    1662. 83 var quotedName = op.name ? this.__quoteIdentifier(op.name) : null;
    1663. +
    1664. 83 var alterTableOp = null;
    1665. +
    1666. 83 switch (op.op) {
    1667. +
    1668. case "addColumn":
    1669. +
    1670. 13 alterTableOp = format("ADD COLUMN %s", this.__columnDefinitionSql(op));
    1671. +
    1672. 13 break;
    1673. +
    1674. case "dropColumn":
    1675. +
    1676. 4 alterTableOp = format("DROP COLUMN %s", quotedName);
    1677. +
    1678. 4 break;
    1679. +
    1680. case "renameColumn":
    1681. +
    1682. 52 alterTableOp = format("RENAME COLUMN %s TO %s", quotedName, this.__quoteIdentifier(op.newName));
    1683. +
    1684. 52 break;
    1685. +
    1686. case "setColumnType":
    1687. +
    1688. 3 alterTableOp = format("ALTER COLUMN %s TYPE %s", quotedName, this.typeLiteral(op));
    1689. +
    1690. 3 break;
    1691. +
    1692. case "setColumnDefault":
    1693. +
    1694. 2 alterTableOp = format("ALTER COLUMN %s SET DEFAULT %s", quotedName, this.literal(op["default"]));
    1695. +
    1696. 2 break;
    1697. +
    1698. case "setColumnNull":
    1699. +
    1700. 0 alterTableOp = format("ALTER COLUMN %s %s NOT NULL", quotedName, op["null"] ? "DROP" : "SET");
    1701. +
    1702. 0 break;
    1703. +
    1704. case "addIndex":
    1705. +
    1706. 5 return ret.callback(this.__indexDefinitionSql(table, op)).promise();
    1707. +
    1708. case "dropIndex":
    1709. +
    1710. 2 return ret.callback(this.__dropIndexSql(table, op)).promise();
    1711. +
    1712. case "addConstraint":
    1713. +
    1714. 1 alterTableOp = format("ADD %s", this.__constraintDefinitionSql(op));
    1715. +
    1716. 1 break;
    1717. +
    1718. case "dropConstraint":
    1719. +
    1720. 0 alterTableOp = format("DROP CONSTRAINT %s", quotedName);
    1721. +
    1722. 0 break;
    1723. +
    1724. default :
    1725. +
    1726. 1 throw new DatabaseError("Invalid altertable operator");
    1727. }
    1728. -
    1729. 426 return this[meth](column);
    1730. +
    1731. 75 return ret.callback(format("ALTER TABLE %s %s", this.__quoteSchemaTable(table), alterTableOp)).promise();
    1732. },
    1733. /**
    1734. * @private
    1735. -
    1736. * patio uses the date type by default for Dates.
    1737. -
    1738. * <ul>
    1739. -
    1740. * <li>if onlyTime is present then time is used</li>
    1741. -
    1742. * <li>if timeStamp is present then timestamp is used,</li>
    1743. -
    1744. * <li>if dateTime is present then datetime is used</li>
    1745. -
    1746. * <li>if yearOnly is present then year is used</li>
    1747. -
    1748. * <li>else date is used</li>
    1749. -
    1750. * </ul>
    1751. -
    1752. */
    1753. -
    1754. __typeLiteralGenericDate:function (column) {
    1755. -
    1756. 11 var type = column.type, ret = "date";
    1757. -
    1758. 11 if (column.onlyTime) {
    1759. -
    1760. 2 ret = "time";
    1761. -
    1762. 9 } else if (column.timeStamp) {
    1763. -
    1764. 2 ret = "timestamp";
    1765. -
    1766. 7 } else if (column.dateTime) {
    1767. -
    1768. 2 ret = "datetime";
    1769. -
    1770. 5 } else if (column.yearOnly) {
    1771. -
    1772. 2 ret = "year";
    1773. -
    1774. }
    1775. -
    1776. 11 return ret;
    1777. +
    1778. * Array of SQL DDL modification statements for the given table,
    1779. +
    1780. * corresponding to the DDL changes specified by the operations.
    1781. +
    1782. * */
    1783. +
    1784. __alterTableSqlList:function (table, operations) {
    1785. +
    1786. 84 return new PromiseList(operations.map(hitch(this, "__alterTableSql", table)));
    1787. },
    1788. /**
    1789. * @private
    1790. -
    1791. * * patio uses the blob type by default for Buffers.
    1792. +
    1793. * SQL DDL fragment containing the column creation SQL for the given column.
    1794. +
    1795. *
    1796. +
    1797. * @param column
    1798. */
    1799. -
    1800. __typeLiteralGenericBlob:function (column) {
    1801. -
    1802. 25 return "blob";
    1803. +
    1804. __columnDefinitionSql:function (column) {
    1805. +
    1806. 644 var sql = [format("%s %s", this.__quoteIdentifier(column.name), this.typeLiteral(column))];
    1807. +
    1808. 644 column.unique && sql.push(this._static.UNIQUE);
    1809. +
    1810. 644 (column.allowNull === false || column["null"] === false) && sql.push(this._static.NOT_NULL);
    1811. +
    1812. 644 (column.allowNull === true || column["null"] === true) && sql.push(this._static.NULL);
    1813. +
    1814. 644 !isUndefined(column["default"]) && sql.push(format(" DEFAULT %s", this.literal(column["default"])));
    1815. +
    1816. 644 column.primaryKey && sql.push(this._static.PRIMARY_KEY);
    1817. +
    1818. 644 column.autoIncrement && sql.push(" " + this.autoIncrementSql);
    1819. +
    1820. 644 column.table && sql.push(this.__columnReferencesColumnConstraintSql(column));
    1821. +
    1822. 644 return sql.join("");
    1823. },
    1824. /**
    1825. * @private
    1826. -
    1827. * * patio uses the year type by default for {@link patio.sql.DateTime}.
    1828. +
    1829. * SQL DDL fragment containing the column creation
    1830. +
    1831. * SQL for all given columns, used inside a CREATE TABLE block.
    1832. */
    1833. -
    1834. __typeLiteralGenericDateTime:function (column) {
    1835. -
    1836. 2 return "datetime";
    1837. +
    1838. __columnListSql:function (generator) {
    1839. +
    1840. 218 return generator.columns.map(hitch(this, "__columnDefinitionSql")).concat(generator.constraints.map(hitch(this, "__constraintDefinitionSql"))).join(this._static.COMMA_SEPARATOR);
    1841. },
    1842. /**
    1843. * @private
    1844. -
    1845. * patio uses the timestamp type by default for {@link patio.sql.TimeStamp}.
    1846. +
    1847. *SQL DDL fragment for column foreign key references (column constraints)
    1848. */
    1849. -
    1850. __typeLiteralGenericTimestamp:function (column) {
    1851. -
    1852. 4 return "timestamp";
    1853. +
    1854. __columnReferencesColumnConstraintSql:function (column) {
    1855. +
    1856. 30 return this.__columnReferencesSql(column);
    1857. },
    1858. /**
    1859. * @private
    1860. -
    1861. * patio uses the time type by default for {@link patio.sql.Time}.
    1862. +
    1863. * SQL DDL fragment for column foreign key references
    1864. */
    1865. -
    1866. __typeLiteralGenericTime:function (column) {
    1867. -
    1868. 2 return "time";
    1869. +
    1870. __columnReferencesSql:function (column) {
    1871. +
    1872. 38 var sql = format(" REFERENCES %s", this.__quoteSchemaTable(column.table));
    1873. +
    1874. 38 column.key && (sql += format("(%s)", array.toArray(column.key).map(this.__quoteIdentifier, this).join(this._static.COMMA_SEPARATOR)));
    1875. +
    1876. 38 column.onDelete && (sql += format(" ON DELETE %s", this.__onDeleteClause(column.onDelete)));
    1877. +
    1878. 38 column.onUpdate && (sql += format(" ON UPDATE %s", this.__onUpdateClause(column.onUpdate)));
    1879. +
    1880. 38 column.deferrable && (sql += " DEFERRABLE INITIALLY DEFERRED");
    1881. +
    1882. 38 return sql;
    1883. },
    1884. /**
    1885. * @private
    1886. -
    1887. * patio uses the year type by default for {@link patio.sql.Year}.
    1888. -
    1889. */
    1890. -
    1891. __typeLiteralGenericYear:function (column) {
    1892. -
    1893. 2 return "year";
    1894. +
    1895. * SQL DDL fragment for table foreign key references (table constraints)
    1896. +
    1897. * */
    1898. +
    1899. __columnReferencesTableConstraintSql:function (constraint) {
    1900. +
    1901. 6 return format("FOREIGN KEY %s%s", this.literal(constraint.columns.map(function (c) {
    1902. +
    1903. 6 return isString(c) ? sql.stringToIdentifier(c) : c;
    1904. +
    1905. })), this.__columnReferencesSql(constraint));
    1906. },
    1907. /**
    1908. * @private
    1909. -
    1910. * patio uses the boolean type by default for Boolean class
    1911. -
    1912. * */
    1913. -
    1914. __typeLiteralGenericBoolean:function (column) {
    1915. -
    1916. 3 return "boolean";
    1917. +
    1918. * SQL DDL fragment specifying a constraint on a table.
    1919. +
    1920. */
    1921. +
    1922. __constraintDefinitionSql:function (constraint) {
    1923. +
    1924. 6 var ret = [constraint.name ? format("CONSTRAINT %s ", this.__quoteIdentifier(constraint.name)) : ""];
    1925. +
    1926. 6 switch (constraint.type) {
    1927. +
    1928. case "check":
    1929. +
    1930. 0 var check = constraint.check;
    1931. +
    1932. 0 ret.push(format("CHECK %s", this.__filterExpr(isArray(check) && check.length === 1 ? check[0] : check)));
    1933. +
    1934. 0 break;
    1935. +
    1936. case "primaryKey":
    1937. +
    1938. 0 ret.push(format("PRIMARY KEY %s", this.literal(constraint.columns.map(function (c) {
    1939. +
    1940. 0 return isString(c) ? sql.stringToIdentifier(c) : c;
    1941. +
    1942. }))));
    1943. +
    1944. 0 break;
    1945. +
    1946. case "foreignKey":
    1947. +
    1948. 6 ret.push(this.__columnReferencesTableConstraintSql(constraint));
    1949. +
    1950. 6 break;
    1951. +
    1952. case "unique":
    1953. +
    1954. 0 ret.push(format("UNIQUE %s", this.literal(constraint.columns.map(function (c) {
    1955. +
    1956. 0 return isString(c) ? sql.stringToIdentifier(c) : c;
    1957. +
    1958. }))));
    1959. +
    1960. 0 break;
    1961. +
    1962. default:
    1963. +
    1964. 0 throw new DatabaseError(format("Invalid constriant type %s, should be 'check', 'primaryKey', foreignKey', or 'unique'", constraint.type));
    1965. +
    1966. }
    1967. +
    1968. 6 return ret.join("");
    1969. },
    1970. /**
    1971. * @private
    1972. -
    1973. * patio uses the numeric type by default for NumericTypes
    1974. -
    1975. * If a size is given, it is used, otherwise, it will default to whatever
    1976. -
    1977. * the database default is for an unsized value.
    1978. -
    1979. * <ul>
    1980. -
    1981. * <li> if isInt is present the int is used</li>
    1982. -
    1983. * <li> if isDouble is present then double precision is used</li>
    1984. -
    1985. * </ul>
    1986. -
    1987. */
    1988. -
    1989. __typeLiteralGenericNumeric:function (column) {
    1990. -
    1991. 10 return column.size ? format("numeric(%s)", array.toArray(column.size).join(', ')) : column.isInt ? "integer" : column.isDouble ? "double precision" : "numeric";
    1992. +
    1993. * Execute the create table statements using the generator.
    1994. +
    1995. * */
    1996. +
    1997. __createTableFromGenerator:function (name, generator, options) {
    1998. +
    1999. 218 return this.executeDdl(this.__createTableSql(name, generator, options));
    2000. },
    2001. -
    2002. /**
    2003. * @private
    2004. -
    2005. */
    2006. -
    2007. __typeLiteralGenericFloat:function (column) {
    2008. -
    2009. 15 return "double precision";
    2010. +
    2011. * Execute the create index statements using the generator.
    2012. +
    2013. * */
    2014. +
    2015. __createTableIndexesFromGenerator:function (name, generator, options) {
    2016. +
    2017. 218 var e = options.ignoreIndexErrors;
    2018. +
    2019. 218 var ret = new Promise();
    2020. +
    2021. 218 var promises = generator.indexes.map(function (index) {
    2022. +
    2023. 18 var ps = this.__indexSqlList(name, [index]).map(this.executeDdl, this);
    2024. +
    2025. 18 return new PromiseList(ps);
    2026. +
    2027. }, this);
    2028. +
    2029. 218 if (promises.length) {
    2030. +
    2031. 16 new PromiseList(promises).then(ret, hitch(ret, e ? "callback" : "errback"));
    2032. +
    2033. } else {
    2034. +
    2035. 202 ret.callback();
    2036. +
    2037. }
    2038. +
    2039. 218 return ret.promise();
    2040. },
    2041. /**
    2042. * @private
    2043. -
    2044. */
    2045. -
    2046. __typeLiteralGenericDecimal:function (column) {
    2047. -
    2048. 2 return "double precision";
    2049. +
    2050. * DDL statement for creating a table with the given name, columns, and options
    2051. +
    2052. * */
    2053. +
    2054. __createTableSql:function (name, generator, options) {
    2055. +
    2056. 218 return format("CREATE %sTABLE %s (%s)", options.temp ? this.temporaryTableSql : "", this.__quoteSchemaTable(name), this.__columnListSql(generator));
    2057. },
    2058. /**
    2059. * @private
    2060. -
    2061. * patio uses the varchar type by default for Strings. If a
    2062. -
    2063. * size isn't present, patio assumes a size of 255. If the
    2064. -
    2065. * fixed option is used, patio uses the char type. If the
    2066. -
    2067. * text option is used, patio uses the :text type.
    2068. +
    2069. * Default index name for the table and columns, may be too long
    2070. +
    2071. * for certain databases.
    2072. */
    2073. -
    2074. __typeLiteralGenericString:function (column) {
    2075. -
    2076. 41 return column.text ? "text" : format("%s(%s)", column.fixed ? "char" : "varchar", column.size || 255);
    2077. +
    2078. __defaultIndexName:function (tableName, columns) {
    2079. +
    2080. 25 var parts = this.__schemaAndTable(tableName);
    2081. +
    2082. 25 var schema = parts[0], table = parts[1];
    2083. +
    2084. 25 var index = [];
    2085. +
    2086. 25 if (schema && schema !== this.defaultSchema) {
    2087. +
    2088. 0 index.push(schema);
    2089. +
    2090. }
    2091. +
    2092. 25 index.push(table);
    2093. +
    2094. 25 index = index.concat(columns.map(function (c) {
    2095. +
    2096. 28 return isString(c) ? c : this.literal(c).replace(/\W/g, "");
    2097. +
    2098. }, this));
    2099. +
    2100. 25 index.push("index");
    2101. +
    2102. 25 return index.join(this._static.UNDERSCORE);
    2103. +
    2104. },
    2105. /**
    2106. * @private
    2107. -
    2108. * SQL fragment for the given type of a column if the column is not one of the
    2109. -
    2110. * generic types specified with a native javascript type class.
    2111. -
    2112. */
    2113. -
    2114. __typeLiteralSpecific:function (column) {
    2115. -
    2116. 336 var type = column.type;
    2117. -
    2118. 336 type = type === "double" ? "double precision" : type;
    2119. -
    2120. 336 if (type === "varchar") {
    2121. -
    2122. 7 column.size = isNumber(column.size) ? column.size : 255;
    2123. -
    2124. }
    2125. -
    2126. 336 var elements = column.size || column.elements;
    2127. -
    2128. 336 return format("%s%s%s", type, elements ? this.literal(toArray(elements)) : "", column.unsigned ? " UNSIGNED" : "");
    2129. +
    2130. * The SQL to drop an index for the table.
    2131. +
    2132. * */
    2133. +
    2134. __dropIndexSql:function (table, op) {
    2135. +
    2136. 2 return format("DROP INDEX %s", this.__quoteIdentifier(op.name || this.__defaultIndexName(table, op.columns)));
    2137. },
    2138. -
    2139. /**@ignore*/
    2140. -
    2141. getters:{
    2142. -
    2143. /**@lends patio.Database.prototype*/
    2144. -
    2145. /**
    2146. -
    2147. * @private
    2148. -
    2149. * The SQL string specify the autoincrement property, generally used by
    2150. -
    2151. * primary keys.
    2152. -
    2153. *
    2154. -
    2155. * @field
    2156. -
    2157. * */
    2158. -
    2159. autoIncrementSql:function () {
    2160. -
    2161. 10 return this._static.AUTOINCREMENT;
    2162. -
    2163. },
    2164. -
    2165. -
    2166. /**
    2167. -
    2168. * @private
    2169. -
    2170. * @field
    2171. -
    2172. * */
    2173. -
    2174. temporaryTableSql:function () {
    2175. -
    2176. 3 return this._static.TEMPORARY;
    2177. -
    2178. },
    2179. -
    2180. -
    2181. /**
    2182. -
    2183. * @private
    2184. -
    2185. * @field
    2186. -
    2187. * */
    2188. -
    2189. __schemaUtiltyDataset:function () {
    2190. -
    2191. 3304 this.__schemaUtiltyDs = this.__schemaUtiltyDs || this.dataset;
    2192. -
    2193. 3304 return this.__schemaUtiltyDs;
    2194. -
    2195. }
    2196. -
    2197. }
    2198. +
    2199. /**
    2200. +
    2201. * @private
    2202. +
    2203. *
    2204. +
    2205. * SQL DDL statement to drop the table with the given name.
    2206. +
    2207. **/
    2208. +
    2209. __dropTableSql:function (name) {
    2210. +
    2211. 210 return format("DROP TABLE %s", this.__quoteSchemaTable(name));
    2212. +
    2213. },
    2214. -
    2215. },
    2216. +
    2217. /**
    2218. +
    2219. * @private
    2220. +
    2221. * Proxy the filterExpr call to the dataset, used for creating constraints.
    2222. +
    2223. * */
    2224. +
    2225. __filterExpr:function (args, block) {
    2226. +
    2227. 2 var ds = this.__schemaUtiltyDataset;
    2228. +
    2229. 2 return ds.literal(ds._filterExpr.apply(ds, arguments));
    2230. +
    2231. },
    2232. -
    2233. "static":{
    2234. -
    2235. /**@lends patio.Database*/
    2236. /**
    2237. -
    2238. *Default AUTO INCREMENT SQL
    2239. +
    2240. * @private
    2241. +
    2242. * SQL DDL statement for creating an index for the table with the given name
    2243. +
    2244. * and index specifications.
    2245. */
    2246. -
    2247. AUTOINCREMENT:'AUTOINCREMENT',
    2248. +
    2249. __indexDefinitionSql:function (tableName, index) {
    2250. +
    2251. 7 var indexName = index.name || this.__defaultIndexName(tableName, index.columns);
    2252. +
    2253. 7 if (index.type) {
    2254. +
    2255. 0 throw new DatabaseError("Index types are not supported for this database");
    2256. +
    2257. 7 } else if (index.where) {
    2258. +
    2259. 0 throw new DatabaseError("Partial indexes are not supported for this database");
    2260. +
    2261. } else {
    2262. +
    2263. 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) {
    2264. +
    2265. 8 return isString(c) ? new Identifier(c) : c;
    2266. +
    2267. })));
    2268. +
    2269. }
    2270. +
    2271. },
    2272. /**
    2273. -
    2274. *Default CASCACDE SQL
    2275. +
    2276. * Array of SQL DDL statements, one for each index specification,
    2277. +
    2278. * for the given table.
    2279. */
    2280. -
    2281. CASCADE:'CASCADE',
    2282. +
    2283. __indexSqlList:function (tableName, indexes) {
    2284. +
    2285. 18 return indexes.map(hitch(this, this.__indexDefinitionSql, tableName));
    2286. +
    2287. },
    2288. /**
    2289. -
    2290. *Default comma
    2291. +
    2292. * @private
    2293. +
    2294. * SQL DDL ON DELETE fragment to use, based on the given action.
    2295. +
    2296. *The following actions are recognized:
    2297. +
    2298. * <ul>
    2299. +
    2300. * <li>cascade - Delete rows referencing this row.</li>
    2301. +
    2302. * <li>noAction (default) - Raise an error if other rows reference this
    2303. +
    2304. * row, allow deferring of the integrity check.
    2305. +
    2306. * </li>
    2307. +
    2308. * <li>restrict - Raise an error if other rows reference this row,
    2309. +
    2310. * but do not allow deferring the integrity check.</li>
    2311. +
    2312. * <li> setDefault - Set columns referencing this row to their default value.</li>
    2313. +
    2314. * <li>setNull - Set columns referencing this row to NULL.</li>
    2315. +
    2316. * </ul>
    2317. */
    2318. -
    2319. COMMA_SEPARATOR:', ',
    2320. +
    2321. __onDeleteClause:function (action) {
    2322. +
    2323. 23 return this._static[action.toUpperCase()] || this._static.NO_ACTION;
    2324. +
    2325. },
    2326. /**
    2327. -
    2328. *Default NO ACTION SQL
    2329. +
    2330. * @private
    2331. +
    2332. * SQL DDL ON UPDATE fragment to use, based on the given action.
    2333. +
    2334. *The following actions are recognized:
    2335. +
    2336. * <ul>
    2337. +
    2338. * <li>cascade - Delete rows referencing this row.</li>
    2339. +
    2340. * <li>noAction (default) - Raise an error if other rows reference this
    2341. +
    2342. * row, allow deferring of the integrity check.
    2343. +
    2344. * </li>
    2345. +
    2346. * <li>restrict - Raise an error if other rows reference this row,
    2347. +
    2348. * but do not allow deferring the integrity check.</li>
    2349. +
    2350. * <li> setDefault - Set columns referencing this row to their default value.</li>
    2351. +
    2352. * <li>setNull - Set columns referencing this row to NULL.</li>
    2353. +
    2354. * </ul>
    2355. */
    2356. -
    2357. NO_ACTION:'NO ACTION',
    2358. +
    2359. __onUpdateClause:function (action) {
    2360. +
    2361. 1 return this._static[action.toUpperCase()] || this._static.NO_ACTION;
    2362. +
    2363. },
    2364. /**
    2365. -
    2366. *Default NOT NULL SQL
    2367. -
    2368. */
    2369. -
    2370. NOT_NULL:' NOT NULL',
    2371. +
    2372. * @private
    2373. +
    2374. * Proxy the quoteSchemaTable method to the dataset
    2375. +
    2376. * */
    2377. +
    2378. __quoteSchemaTable:function (table) {
    2379. +
    2380. 2212 return this.__schemaUtiltyDataset.quoteSchemaTable(table);
    2381. +
    2382. },
    2383. /**
    2384. -
    2385. * Default NULL SQL
    2386. -
    2387. */
    2388. -
    2389. NULL:' NULL',
    2390. +
    2391. * @private
    2392. +
    2393. * Proxy the quoteIdentifier method to the dataset, used for quoting tables and columns.
    2394. +
    2395. * */
    2396. +
    2397. __quoteIdentifier:function (v) {
    2398. +
    2399. 846 return this.__schemaUtiltyDataset.quoteIdentifier(v);
    2400. +
    2401. },
    2402. /**
    2403. -
    2404. *Default PRIMARY KEY SQL
    2405. -
    2406. */
    2407. -
    2408. PRIMARY_KEY:' PRIMARY KEY',
    2409. +
    2410. * @private
    2411. +
    2412. * SQL DDL statement for renaming a table.
    2413. +
    2414. * */
    2415. +
    2416. __renameTableSql:function (name, newName) {
    2417. +
    2418. 1 return format("ALTER TABLE %s RENAME TO %s", this.__quoteSchemaTable(name), this.__quoteSchemaTable(newName));
    2419. +
    2420. },
    2421. /**
    2422. -
    2423. *Default RESTRICT SQL
    2424. +
    2425. * @private
    2426. +
    2427. * Remove the cached schemaUtilityDataset, because the identifier
    2428. +
    2429. * quoting has changed.
    2430. */
    2431. -
    2432. RESTRICT:'RESTRICT',
    2433. +
    2434. __resetSchemaUtilityDataset:function () {
    2435. +
    2436. 0 this.__schemaUtiltyDs = null;
    2437. +
    2438. },
    2439. /**
    2440. -
    2441. *Default SET DEFAULT SQL
    2442. -
    2443. */
    2444. -
    2445. SET_DEFAULT:'SET DEFAULT',
    2446. -
    2447. +
    2448. * @private
    2449. +
    2450. * Split the schema information from the table
    2451. +
    2452. * */
    2453. +
    2454. __schemaAndTable:function (tableName) {
    2455. +
    2456. 244 return this.__schemaUtiltyDataset.schemaAndTable(tableName);
    2457. +
    2458. },
    2459. +
    2460. /**
    2461. -
    2462. *Default SET NULL SQL
    2463. +
    2464. * @private
    2465. +
    2466. * Return true if the given column schema represents an autoincrementing primary key.
    2467. +
    2468. *
    2469. */
    2470. -
    2471. SET_NULL:'SET NULL',
    2472. +
    2473. _schemaAutoincrementingPrimaryKey:function (schema) {
    2474. +
    2475. 0 return !!schema.primaryKey;
    2476. +
    2477. },
    2478. /**
    2479. -
    2480. *Default TEMPORARY SQL
    2481. -
    2482. */
    2483. -
    2484. TEMPORARY:'TEMPORARY ',
    2485. +
    2486. * @private
    2487. +
    2488. * SQL fragment specifying the type of a given column.
    2489. +
    2490. * */
    2491. +
    2492. typeLiteral:function (column) {
    2493. +
    2494. 690 return this.__typeLiteralGeneric(column);
    2495. +
    2496. },
    2497. /**
    2498. -
    2499. *Default UNDERSCORE SQL, used in index creation.
    2500. +
    2501. * @private
    2502. +
    2503. * SQL fragment specifying the full type of a column,
    2504. +
    2505. * consider the type with possible modifiers.
    2506. */
    2507. -
    2508. UNDERSCORE:'_',
    2509. +
    2510. __typeLiteralGeneric:function (column) {
    2511. +
    2512. 690 var type = column.type;
    2513. +
    2514. 690 var meth = "__typeLiteralGeneric";
    2515. +
    2516. 690 var isStr = isString(type);
    2517. +
    2518. 690 var proper = isStr ? type.charAt(0).toUpperCase() + type.substr(1) : null;
    2519. +
    2520. 690 if (type === String || (isStr && type.match(/string/i))) {
    2521. +
    2522. 198 meth += "String";
    2523. +
    2524. 492 } else if ((isStr && type.match(/number/i)) || type === Number) {
    2525. +
    2526. 12 meth += "Numeric";
    2527. +
    2528. 480 } else if ((isStr && type.match(/datetime/i)) || type === DateTime) {
    2529. +
    2530. 8 meth += "DateTime";
    2531. +
    2532. 472 } else if ((isStr && type.match(/date/i)) || type === Date) {
    2533. +
    2534. 11 meth += "Date";
    2535. +
    2536. 461 } else if ((isStr && type.match(/year/i)) || type === Year) {
    2537. +
    2538. 2 meth += "Year";
    2539. +
    2540. 459 } else if ((isStr && type.match(/timestamp/i))|| type === TimeStamp) {
    2541. +
    2542. 4 meth += "Timestamp";
    2543. +
    2544. 455 } else if ((isStr && type.match(/time/i)) || type === Time) {
    2545. +
    2546. 2 meth += "Time";
    2547. +
    2548. 453 } else if ((isStr && type.match(/decimal/i)) || type === Decimal) {
    2549. +
    2550. 2 meth += "Decimal";
    2551. +
    2552. 451 } else if ((isStr && type.match(/float/i)) || type === Float) {
    2553. +
    2554. 15 meth += "Float";
    2555. +
    2556. 436 } else if ((isStr && type.match(/boolean/i)) || type === Boolean) {
    2557. +
    2558. 5 meth += "Boolean";
    2559. +
    2560. 431 } else if ((isStr && type.match(/buffer/i)) || type === Buffer) {
    2561. +
    2562. 29 meth += "Blob";
    2563. +
    2564. 402 } else if (isStr && isFunction(this[meth + proper])) {
    2565. +
    2566. 138 meth += proper;
    2567. +
    2568. } else {
    2569. +
    2570. 264 return this.__typeLiteralSpecific(column);
    2571. +
    2572. }
    2573. +
    2574. 426 return this[meth](column);
    2575. +
    2576. },
    2577. /**
    2578. -
    2579. *Default UNIQUE SQL
    2580. +
    2581. * @private
    2582. +
    2583. * patio uses the date type by default for Dates.
    2584. +
    2585. * <ul>
    2586. +
    2587. * <li>if onlyTime is present then time is used</li>
    2588. +
    2589. * <li>if timeStamp is present then timestamp is used,</li>
    2590. +
    2591. * <li>if dateTime is present then datetime is used</li>
    2592. +
    2593. * <li>if yearOnly is present then year is used</li>
    2594. +
    2595. * <li>else date is used</li>
    2596. +
    2597. * </ul>
    2598. */
    2599. -
    2600. UNIQUE:' UNIQUE',
    2601. +
    2602. __typeLiteralGenericDate:function (column) {
    2603. +
    2604. 11 var type = column.type, ret = "date";
    2605. +
    2606. 11 if (column.onlyTime) {
    2607. +
    2608. 2 ret = "time";
    2609. +
    2610. 9 } else if (column.timeStamp) {
    2611. +
    2612. 2 ret = "timestamp";
    2613. +
    2614. 7 } else if (column.dateTime) {
    2615. +
    2616. 2 ret = "datetime";
    2617. +
    2618. 5 } else if (column.yearOnly) {
    2619. +
    2620. 2 ret = "year";
    2621. +
    2622. }
    2623. +
    2624. 11 return ret;
    2625. +
    2626. },
    2627. /**
    2628. -
    2629. * Default UNSIGNED SQL
    2630. +
    2631. * @private
    2632. +
    2633. * * patio uses the blob type by default for Buffers.
    2634. */
    2635. -
    2636. UNSIGNED:' UNSIGNED'
    2637. -
    2638. -
    2639. }
    2640. -
    2641. }).as(module);
    2642. -
    -
    - -
    - - - - - -
    -
    errors.js
    -
    -
    - Coverage89.47 - SLOC81 - LOC19 - Missed2 -
    -
    -
    1. 1var patio = exports;
    2. +
    3. __typeLiteralGenericBlob:function (column) {
    4. +
    5. 25 return "blob";
    6. +
    7. },
    8. +
    9. /**
    10. +
    11. * @private
    12. +
    13. * * patio uses the year type by default for {@link patio.sql.DateTime}.
    14. +
    15. */
    16. +
    17. __typeLiteralGenericDateTime:function (column) {
    18. +
    19. 2 return "datetime";
    20. +
    21. },
    22. -
    23. /**
    24. -
    25. * @class Thrown if a function is not impltemened
    26. -
    27. *
    28. -
    29. * @param {String} message the message to show.
    30. -
    31. */
    32. -
    33. 1patio.NotImplemented = function(message) {
    34. -
    35. 4 return new Error("Not Implemented : " + message);
    36. -
    37. };
    38. +
    39. /**
    40. +
    41. * @private
    42. +
    43. * patio uses the timestamp type by default for {@link patio.sql.TimeStamp}.
    44. +
    45. */
    46. +
    47. __typeLiteralGenericTimestamp:function (column) {
    48. +
    49. 4 return "timestamp";
    50. +
    51. },
    52. -
    53. /**
    54. -
    55. * @class Thrown if there is an Expression Error.
    56. -
    57. *
    58. -
    59. * @param {String} message the message to show.
    60. -
    61. */
    62. -
    63. 1patio.ExpressionError = function(message) {
    64. -
    65. 0 return new Error("Expression Error :" + message);
    66. -
    67. };
    68. +
    69. /**
    70. +
    71. * @private
    72. +
    73. * patio uses the time type by default for {@link patio.sql.Time}.
    74. +
    75. */
    76. +
    77. __typeLiteralGenericTime:function (column) {
    78. +
    79. 2 return "time";
    80. +
    81. },
    82. -
    83. /**
    84. -
    85. * @class Thrown if there is a Query Error.
    86. -
    87. *
    88. -
    89. * @param {String} message the message to show.
    90. -
    91. */
    92. -
    93. 1patio.QueryError = function(message) {
    94. -
    95. 121 return new Error("QueryError : " + message);
    96. -
    97. };
    98. +
    99. /**
    100. +
    101. * @private
    102. +
    103. * patio uses the year type by default for {@link patio.sql.Year}.
    104. +
    105. */
    106. +
    107. __typeLiteralGenericYear:function (column) {
    108. +
    109. 2 return "year";
    110. +
    111. },
    112. -
    113. /**
    114. -
    115. * @class Thrown if there is a Dataset Error.
    116. -
    117. *
    118. -
    119. * @param {String} message the message to show.
    120. -
    121. */
    122. -
    123. 1patio.DatasetError = function(message) {
    124. -
    125. 7 return new Error("DatasetError : " + message);
    126. -
    127. };
    128. +
    129. /**
    130. +
    131. * @private
    132. +
    133. * patio uses the boolean type by default for Boolean class
    134. +
    135. * */
    136. +
    137. __typeLiteralGenericBoolean:function (column) {
    138. +
    139. 3 return "boolean";
    140. +
    141. },
    142. -
    143. /**
    144. -
    145. * @class Thrown if there is a Database Error.
    146. -
    147. *
    148. -
    149. * @param {String} message the message to show.
    150. -
    151. */
    152. -
    153. 1patio.DatabaseError = function(message) {
    154. -
    155. 5 return new Error("Database error : " + message);
    156. -
    157. };
    158. +
    159. /**
    160. +
    161. * @private
    162. +
    163. * patio uses the numeric type by default for NumericTypes
    164. +
    165. * If a size is given, it is used, otherwise, it will default to whatever
    166. +
    167. * the database default is for an unsized value.
    168. +
    169. * <ul>
    170. +
    171. * <li> if isInt is present the int is used</li>
    172. +
    173. * <li> if isDouble is present then double precision is used</li>
    174. +
    175. * </ul>
    176. +
    177. */
    178. +
    179. __typeLiteralGenericNumeric:function (column) {
    180. +
    181. 10 return column.size ? format("numeric(%s)", array.toArray(column.size).join(', ')) : column.isInt ? "integer" : column.isDouble ? "double precision" : "numeric";
    182. +
    183. },
    184. -
    185. /**
    186. -
    187. * @class Thrown if there is a unexpected Error.
    188. -
    189. *
    190. -
    191. * @param {String} message the message to show.
    192. -
    193. */
    194. -
    195. 1patio.PatioError = function(message) {
    196. -
    197. 31 return new Error("Patio error : " + message);
    198. -
    199. };
    200. -
    201. /**
    202. -
    203. * @class Thrown if there is a error thrown within a model.
    204. -
    205. * @param {String} message the message to show.
    206. -
    207. */
    208. -
    209. 1patio.ModelError = function(message) {
    210. -
    211. 2 return new Error("Model error : " + message);
    212. -
    213. };
    214. +
    215. /**
    216. +
    217. * @private
    218. +
    219. */
    220. +
    221. __typeLiteralGenericFloat:function (column) {
    222. +
    223. 15 return "double precision";
    224. +
    225. },
    226. -
    227. /**
    228. -
    229. * @class Thrown if there is an error when loading/creating/deleteing an association.
    230. -
    231. * @param {String} message the message to show.
    232. -
    233. */
    234. -
    235. 1patio.AssociationError = function(message) {
    236. -
    237. 0 return new Error("Association error : " + message);
    238. -
    239. };
    240. +
    241. /**
    242. +
    243. * @private
    244. +
    245. */
    246. +
    247. __typeLiteralGenericDecimal:function (column) {
    248. +
    249. 2 return "double precision";
    250. +
    251. },
    252. +
    253. /**
    254. +
    255. * @private
    256. +
    257. * patio uses the varchar type by default for Strings. If a
    258. +
    259. * size isn't present, patio assumes a size of 255. If the
    260. +
    261. * fixed option is used, patio uses the char type. If the
    262. +
    263. * text option is used, patio uses the :text type.
    264. +
    265. */
    266. +
    267. __typeLiteralGenericString:function (column) {
    268. +
    269. 41 return column.text ? "text" : format("%s(%s)", column.fixed ? "char" : "varchar", column.size || 255);
    270. +
    271. },
    272. -
    273. /**
    274. -
    275. * Thrown if there is an error when performing a migration.
    276. -
    277. * @param {String} message the message to show.
    278. -
    279. */
    280. -
    281. 1patio.MigrationError = function(message) {
    282. -
    283. 2 return new Error("Migration error : " + message);
    284. -
    285. };
    +
  • /**
  • +
  • * @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) {
  • +
  • 336 var type = column.type;
  • +
  • 336 type = type === "double" ? "double precision" : type;
  • +
  • 336 if (type === "varchar") {
  • +
  • 7 column.size = isNumber(column.size) ? column.size : 255;
  • +
  • }
  • +
  • 336 var elements = column.size || column.elements;
  • +
  • 336 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 () {
  • +
  • 3304 this.__schemaUtiltyDs = this.__schemaUtiltyDs || this.dataset;
  • +
  • 3304 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);
  • +
  • +
    + +
    + + + + + +
    +
    errors.js
    +
    +
    + Coverage89.47 + SLOC81 + LOC19 + Missed2 +
    +
    +
    1. 1var patio = exports;
    2. +
    3. +
    4. +
    5. /**
    6. +
    7. * @class Thrown if a function is not impltemened
    8. +
    9. *
    10. +
    11. * @param {String} message the message to show.
    12. +
    13. */
    14. +
    15. 1patio.NotImplemented = function(message) {
    16. +
    17. 4 return new Error("Not Implemented : " + message);
    18. +
    19. };
    20. +
    21. +
    22. /**
    23. +
    24. * @class Thrown if there is an Expression Error.
    25. +
    26. *
    27. +
    28. * @param {String} message the message to show.
    29. +
    30. */
    31. +
    32. 1patio.ExpressionError = function(message) {
    33. +
    34. 0 return new Error("Expression Error :" + message);
    35. +
    36. };
    37. +
    38. +
    39. /**
    40. +
    41. * @class Thrown if there is a Query Error.
    42. +
    43. *
    44. +
    45. * @param {String} message the message to show.
    46. +
    47. */
    48. +
    49. 1patio.QueryError = function(message) {
    50. +
    51. 121 return new Error("QueryError : " + message);
    52. +
    53. };
    54. +
    55. +
    56. /**
    57. +
    58. * @class Thrown if there is a Dataset Error.
    59. +
    60. *
    61. +
    62. * @param {String} message the message to show.
    63. +
    64. */
    65. +
    66. 1patio.DatasetError = function(message) {
    67. +
    68. 7 return new Error("DatasetError : " + message);
    69. +
    70. };
    71. +
    72. +
    73. /**
    74. +
    75. * @class Thrown if there is a Database Error.
    76. +
    77. *
    78. +
    79. * @param {String} message the message to show.
    80. +
    81. */
    82. +
    83. 1patio.DatabaseError = function(message) {
    84. +
    85. 5 return new Error("Database error : " + message);
    86. +
    87. };
    88. +
    89. +
    90. /**
    91. +
    92. * @class Thrown if there is a unexpected Error.
    93. +
    94. *
    95. +
    96. * @param {String} message the message to show.
    97. +
    98. */
    99. +
    100. 1patio.PatioError = function(message) {
    101. +
    102. 31 return new Error("Patio error : " + message);
    103. +
    104. };
    105. +
    106. +
    107. /**
    108. +
    109. * @class Thrown if there is a error thrown within a model.
    110. +
    111. * @param {String} message the message to show.
    112. +
    113. */
    114. +
    115. 1patio.ModelError = function(message) {
    116. +
    117. 2 return new Error("Model error : " + message);
    118. +
    119. };
    120. +
    121. +
    122. /**
    123. +
    124. * @class Thrown if there is an error when loading/creating/deleteing an association.
    125. +
    126. * @param {String} message the message to show.
    127. +
    128. */
    129. +
    130. 1patio.AssociationError = function(message) {
    131. +
    132. 0 return new Error("Association error : " + message);
    133. +
    134. };
    135. +
    136. +
    137. +
    138. /**
    139. +
    140. * Thrown if there is an error when performing a migration.
    141. +
    142. * @param {String} message the message to show.
    143. +
    144. */
    145. +
    146. 1patio.MigrationError = function(message) {
    147. +
    148. 2 return new Error("Migration error : " + message);
    149. +
    150. };
    @@ -9069,21 +9542,21 @@
  • 1var virtualRow = function (name) {
  • -
  • 874 var WILDCARD = new LiteralString('*');
  • -
  • 874 var QUESTION_MARK = new LiteralString('?');
  • -
  • 874 var COMMA_SEPARATOR = new LiteralString(', ');
  • -
  • 874 var DOUBLE_UNDERSCORE = '__';
  • -
  • -
  • 874 var parts = name.split(DOUBLE_UNDERSCORE);
  • -
  • 874 var table = parts[0], column = parts[1];
  • -
  • 874 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • -
  • 874 var prox = methodMissing(ident, function (m) {
  • +
  • 875 var WILDCARD = new LiteralString('*');
  • +
  • 875 var QUESTION_MARK = new LiteralString('?');
  • +
  • 875 var COMMA_SEPARATOR = new LiteralString(', ');
  • +
  • 875 var DOUBLE_UNDERSCORE = '__';
  • +
  • +
  • 875 var parts = name.split(DOUBLE_UNDERSCORE);
  • +
  • 875 var table = parts[0], column = parts[1];
  • +
  • 875 var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);
  • +
  • 875 var prox = methodMissing(ident, function (m) {
  • 4 return function () {
  • 3 var args = argsToArray(arguments);
  • 3 return SQLFunction.fromArgs([m, name].concat(args));
  • }
  • }, column ? QualifiedIdentifier : Identifier);
  • -
  • 874 var ret = createFunctionWrapper(prox, function (m) {
  • +
  • 875 var ret = createFunctionWrapper(prox, function (m) {
  • 548 var args = argsToArray(arguments);
  • 548 if (args.length) {
  • 542 return SQLFunction.fromArgs([name].concat(args));
  • @@ -9093,8 +9566,8 @@
  • }, function () {
  • 0 return SQLFunction.fromArgs(arguments);
  • });
  • -
  • 874 ret.__proto__ = ident;
  • -
  • 874 return ret;
  • +
  • 875 ret.__proto__ = ident;
  • +
  • 875 return ret;
  • };
  • 1var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds",
  • @@ -9546,7 +10019,7 @@
  • });
  • 1exports.sql = methodMissing(sql, function (name) {
  • -
  • 874 return virtualRow(name);
  • +
  • 875 return virtualRow(name);
  • });
  • 1var OPERTATOR_INVERSIONS = {
  • @@ -10198,7 +10671,7 @@
  • * @return {patio.sql.ColumnAll}
  • */
  • all:function () {
  • -
  • 329 return new ColumnAll(this);
  • +
  • 3 return new ColumnAll(this);
  • }
  • @@ -10356,13 +10829,13 @@
  • * @return {patio.sql.Expression} an expression.
  • */
  • fromArgs:function (args) {
  • -
  • 2215 var ret;
  • -
  • 2215 try {
  • -
  • 2215 ret = new this();
  • +
  • 2216 var ret;
  • +
  • 2216 try {
  • +
  • 2216 ret = new this();
  • } catch (ignore) {
  • }
  • -
  • 2215 this.apply(ret, args);
  • -
  • 2215 return ret;
  • +
  • 2216 this.apply(ret, args);
  • +
  • 2216 return ret;
  • },
  • /**
  • @@ -10554,7 +11027,7 @@
  • * @property table the table this all column expression represents.
  • */
  • constructor:function (table) {
  • -
  • 346 this.table = table;
  • +
  • 20 this.table = table;
  • },
  • /**
  • @@ -10566,9 +11039,9 @@
  • * @return String the SQL columnAll expression fragment.
  • */
  • toString:function (ds) {
  • -
  • 345 !Dataset && (Dataset = require("./dataset"));
  • -
  • 345 ds = ds || new Dataset();
  • -
  • 345 return ds.columnAllSql(this);
  • +
  • 19 !Dataset && (Dataset = require("./dataset"));
  • +
  • 19 ds = ds || new Dataset();
  • +
  • 19 return ds.columnAllSql(this);
  • }
  • }
  • }).as(sql, "ColumnAll");
  • @@ -11165,7 +11638,7 @@
  • * @property {String} value <b>READ ONLY</b> the column or table this identifier represents.
  • */
  • constructor:function (value) {
  • -
  • 16412 this.__value = value;
  • +
  • 16414 this.__value = value;
  • },
  • /**
  • @@ -11185,7 +11658,7 @@
  • /**@ignore*/
  • getters:{
  • value:function () {
  • -
  • 25480 return this.__value;
  • +
  • 25154 return this.__value;
  • }
  • }
  • }
  • @@ -11742,7 +12215,7 @@
  • 1var addStringMethod = function (op) {
  • 24 return function () {
  • -
  • 3968 return this.__str[op].apply(this.__str, arguments);
  • +
  • 4294 return this.__str[op].apply(this.__str, arguments);
  • }
  • };
  • @@ -11765,7 +12238,7 @@
  • * @param {String} str the literal string.
  • */
  • constructor:function (str) {
  • -
  • 3092 this.__str = str;
  • +
  • 3095 this.__str = str;
  • }
  • }
  • }).as(sql, "LiteralString");
  • @@ -12275,601 +12748,144 @@
  • } else {
  • 0 ret.callback(this.__appliedMigrations);
  • }
  • -
  • 18 return ret.promise();
  • -
  • },
  • -
  • -
  • __getMirationFiles:function () {
  • -
  • 18 var ret = new Promise();
  • -
  • 18 var upMigrations = [], downMigrations = [], target = this.target;
  • -
  • 18 if (!this.__migrationFiles) {
  • -
  • 18 when(
  • -
  • this.getFileNames(),
  • -
  • this.__getAppliedMigrations()
  • -
  • ).then(hitch(this, function (res) {
  • -
  • 18 var files = res[0], appliedMigrations = res[1];
  • -
  • 18 var l = files.length, inserts = [];
  • -
  • 18 if (l > 0) {
  • -
  • 18 try {
  • -
  • 18 for (var i = 0; i < l; i++) {
  • -
  • 92 var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
  • -
  • 92 if (!isUndefined(target)) {
  • -
  • 48 var version = this.getMigrationVersionFromFile(f);
  • -
  • 48 if (version > target || (version === 0 && target === version)) {
  • -
  • 35 if (appliedMigrations.indexOf(fLowerCase) != -1) {
  • -
  • 26 downMigrations.push([f, require(file), "down"]);
  • -
  • }
  • -
  • 13 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
  • -
  • 9 upMigrations.push([f, require(file), "up"]);
  • -
  • }
  • -
  • 44 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
  • -
  • 35 upMigrations.push([f, require(file), "up"]);
  • -
  • }
  • -
  • }
  • -
  • } catch (e) {
  • -
  • 0 return ret.errback(e)
  • -
  • }
  • -
  • 18 this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
  • -
  • 18 ret.callback(this.__migrationFiles);
  • -
  • } else {
  • -
  • 0 return ret.callback();
  • -
  • }
  • -
  • }), ret);
  • -
  • } else {
  • -
  • 0 ret.callback(this.__migrationFiles);
  • -
  • }
  • -
  • 18 return ret.promise();
  • -
  • },
  • -
  • -
  • -
  • // Returns the dataset for the schema_migrations table. If no such table
  • -
  • // exists, it is automatically created.
  • -
  • _getSchemaDataset:function () {
  • -
  • 36 var ret = new Promise();
  • -
  • 36 if (!this.__schemaDataset) {
  • -
  • 18 var ds = this.db.from(this.table);
  • -
  • 18 this.__createTable().then(hitch(this, function () {
  • -
  • 18 this.__schemaDataset = ds;
  • -
  • 18 ret.callback(ds);
  • -
  • }), ret);
  • -
  • } else {
  • -
  • 18 ret.callback(this.__schemaDataset);
  • -
  • }
  • -
  • 36 return ret.promise();
  • -
  • },
  • -
  • -
  • __convertSchemaInfo:function () {
  • -
  • 1 var ret = new Promise(), c = this.column;
  • -
  • 1 var ds = this.db.from(this.table);
  • -
  • 1 this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
  • -
  • 1 this.getFileNames().then(hitch(this, function (files) {
  • -
  • 1 var l = files.length, inserts = [];
  • -
  • 1 if (l > 0) {
  • -
  • 1 for (var i = 0; i < l; i++) {
  • -
  • 7 var f = path.basename(files[i]);
  • -
  • 7 if (this.getMigrationVersionFromFile(f) <= version) {
  • -
  • 5 var insert = {};
  • -
  • 5 insert[c] = f;
  • -
  • 5 inserts.push(ds.insert(insert));
  • -
  • }
  • -
  • }
  • -
  • }
  • -
  • 1 if (inserts.length) {
  • -
  • 1 when.apply(comb, inserts).then(ret);
  • -
  • } else {
  • -
  • 0 ret.callback();
  • -
  • }
  • -
  • }), ret);
  • -
  • }), ret);
  • -
  • 1 return ret.promise();
  • -
  • -
  • },
  • -
  • -
  • __createTable:function () {
  • -
  • 18 var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
  • -
  • 18 var ds = this.db.from(table);
  • -
  • 18 var ret = new Promise();
  • -
  • 18 when(
  • -
  • db.tableExists(table),
  • -
  • db.tableExists(intMigrationTable)
  • -
  • ).then(hitch(this, function (res) {
  • -
  • 18 var exists = res[0], intMigratorExists = res[1];
  • -
  • 18 if (!exists) {
  • -
  • 10 db.createTable(table,
  • -
  • function () {
  • -
  • 10 this.column(c, String, {primaryKey:true});
  • -
  • }).addErrback(ret);
  • -
  • 10 if (intMigratorExists) {
  • -
  • 1 db.from(intMigrationTable).all().then(hitch(this, function (versions) {
  • -
  • 1 var version;
  • -
  • 1 if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
  • -
  • 1 this.__convertSchemaInfo().then(ret);
  • -
  • } else {
  • -
  • 0 ret.callback();
  • -
  • }
  • -
  • }));
  • -
  • } else {
  • -
  • 9 ret.callback();
  • -
  • }
  • -
  • } else {
  • -
  • 8 ds.columns.then(hitch(this, function (columns) {
  • -
  • 8 if (columns.indexOf(c) === -1) {
  • -
  • 0 ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
  • -
  • } else {
  • -
  • 8 ret.callback();
  • -
  • }
  • -
  • }), ret);
  • -
  • }
  • -
  • }), ret);
  • -
  • 18 return ret.promise();
  • -
  • }
  • -
  • },
  • -
  • -
  • static:{
  • -
  • DEFAULT_SCHEMA_COLUMN:"filename",
  • -
  • DEFAULT_SCHEMA_TABLE:"schema_migrations"
  • -
  • }
  • -
  • }).as(exports, "TimestampMigrator");
  • -
  • -
  • 1exports.run = function () {
  • -
  • 31 return Migrator.run.apply(Migrator, arguments);
  • -
  • };
  • -
    - -
    - - - - - -
    -
    dataset/index.js
    -
    -
    - Coverage92.54 - SLOC439 - LOC67 - Missed5 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. hitch = comb.hitch,
    4. -
    5. logging = comb.logging,
    6. -
    7. Logger = logging.Logger,
    8. -
    9. errors = require("../errors"),
    10. -
    11. QueryError = errors.QueryError,
    12. -
    13. DatasetError = errors.DatasetError,
    14. -
    15. Promise = comb.Promise,
    16. -
    17. PromiseList = comb.PromiseList,
    18. -
    19. isUndefined = comb.isUndefined,
    20. -
    21. isUndefinedOrNull = comb.isUndefinedOrNull,
    22. -
    23. isString = comb.isString,
    24. -
    25. isInstanceOf = comb.isInstanceOf,
    26. -
    27. isString = comb.isString,
    28. -
    29. isFunction = comb.isFunction,
    30. -
    31. isNull = comb.isNull,
    32. -
    33. merge = comb.merge,
    34. -
    35. define = comb.define,
    36. -
    37. graph = require("./graph"),
    38. -
    39. actions = require("./actions"),
    40. -
    41. features = require("./features"),
    42. -
    43. query = require("./query"),
    44. -
    45. sql = require("./sql"),
    46. -
    47. SQL = require("../sql").sql,
    48. -
    49. AliasedExpression = SQL.AliasedExpression,
    50. -
    51. Identifier = SQL.Identifier,
    52. -
    53. QualifiedIdentifier = SQL.QualifiedIdentifier;
    54. -
    55. -
    56. -
    57. 1var LOGGER = comb.logger("patio.Dataset");
    58. -
    59. 1define([actions, graph, features, query, sql], {
    60. -
    61. instance:{
    62. -
    63. -
    64. /**@lends patio.Dataset.prototype*/
    65. -
    66. -
    67. /**
    68. -
    69. * Class that is used for querying/retirving datasets from a database.
    70. -
    71. *
    72. -
    73. * <p> Dynamically genertated methods include
    74. -
    75. * <ul>
    76. -
    77. * <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
    78. -
    79. * {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
    80. -
    81. * to {@link patio.Dataset#joinTable}, so to invoke include all arguments that
    82. -
    83. * {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.
    84. -
    85. * <ul>
    86. -
    87. * <li>Conditioned join types that accept conditions.
    88. -
    89. * <ul>
    90. -
    91. * <li>inner - INNER JOIN</li>
    92. -
    93. * <li>fullOuter - FULL OUTER</li>
    94. -
    95. * <li>rightOuter - RIGHT OUTER JOIN</li>
    96. -
    97. * <li>leftOuter - LEFT OUTER JOIN</li>
    98. -
    99. * <li>full - FULL JOIN</li>
    100. -
    101. * <li>right - RIGHT JOIN</li>
    102. -
    103. * <li>left - LEFT JOIN</li>
    104. -
    105. * </ul>
    106. -
    107. * </li>
    108. -
    109. * <li>Unconditioned join types that do not accept join conditions
    110. -
    111. * <ul>
    112. -
    113. * <li>natural - NATURAL JOIN</li>
    114. -
    115. * <li>naturalLeft - NATURAL LEFT JOIN</li>
    116. -
    117. * <li>naturalRight - NATURAL RIGHT JOIN</li>
    118. -
    119. * <li>naturalFull - NATURA FULLL JOIN</li>
    120. -
    121. * <li>cross - CROSS JOIN</li>
    122. -
    123. * </ul>
    124. -
    125. * </li>
    126. -
    127. * </ul>
    128. -
    129. * </li>
    130. -
    131. * </li>
    132. -
    133. * </ul>
    134. -
    135. *
    136. -
    137. * <p>
    138. -
    139. * <h4>Features:</h4>
    140. -
    141. * <p>
    142. -
    143. * Features that a particular {@link patio.Dataset} supports are shown in the example below.
    144. -
    145. * If you wish to implement an adapter please override these values depending on the database that
    146. -
    147. * you are developing the adapter for.
    148. -
    149. * </p>
    150. -
    151. * <pre class="code">
    152. -
    153. * var ds = DB.from("test");
    154. -
    155. *
    156. -
    157. * //The default values returned
    158. -
    159. *
    160. -
    161. * //Whether this dataset quotes identifiers.
    162. -
    163. * //Whether this dataset quotes identifiers.
    164. -
    165. * ds.quoteIdentifiers //=>true
    166. -
    167. *
    168. -
    169. * //Whether this dataset will provide accurate number of rows matched for
    170. -
    171. * //delete and update statements. Accurate in this case is the number of
    172. -
    173. * //rows matched by the dataset's filter.
    174. -
    175. * ds.providesAccurateRowsMatched; //=>true
    176. -
    177. *
    178. -
    179. * //Times Whether the dataset requires SQL standard datetimes (false by default,
    180. -
    181. * // as most allow strings with ISO 8601 format).
    182. -
    183. * ds.requiresSqlStandardDate; //=>false
    184. -
    185. *
    186. -
    187. * //Whether the dataset supports common table expressions (the WITH clause).
    188. -
    189. * ds.supportsCte; //=>true
    190. -
    191. *
    192. -
    193. * //Whether the dataset supports the DISTINCT ON clause, false by default.
    194. -
    195. * ds.supportsDistinctOn; //=>false
    196. -
    197. *
    198. -
    199. * //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
    200. -
    201. * ds.supportsIntersectExcept; //=>true
    202. -
    203. *
    204. -
    205. * //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
    206. -
    207. * ds.supportsIntersectExceptAll; //=>true
    208. -
    209. *
    210. -
    211. * //Whether the dataset supports the IS TRUE syntax.
    212. -
    213. * ds.supportsIsTrue; //=>true
    214. -
    215. *
    216. -
    217. * //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
    218. -
    219. * ds.supportsJoinUsing; //=>true
    220. -
    221. *
    222. -
    223. * //Whether modifying joined datasets is supported.
    224. -
    225. * ds.supportsModifyingJoin; //=>false
    226. -
    227. *
    228. -
    229. * //Whether the IN/NOT IN operators support multiple columns when an
    230. -
    231. * ds.supportsMultipleColumnIn; //=>true
    232. -
    233. *
    234. -
    235. * //Whether the dataset supports timezones in literal timestamps
    236. -
    237. * ds.supportsTimestampTimezone; //=>false
    238. -
    239. *
    240. -
    241. * //Whether the dataset supports fractional seconds in literal timestamps
    242. -
    243. * ds.supportsTimestampUsecs; //=>true
    244. -
    245. *
    246. -
    247. * //Whether the dataset supports window functions.
    248. -
    249. * ds.supportsWindowFunctions; //=>false
    250. -
    251. * </pre>
    252. -
    253. * <p>
    254. -
    255. * <p>
    256. -
    257. * <h4>Actions</h4>
    258. -
    259. * <p>
    260. -
    261. * Each dataset does not actually send any query to the database until an action method has
    262. -
    263. * been called upon it(with the exception of {@link patio.Dataset#graph} because columns
    264. -
    265. * from the other table might need retrived in order to set up the graph). Each action
    266. -
    267. * returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
    268. -
    269. * that you account for errors otherwise it can be difficult to track down issues.
    270. -
    271. * The list of action methods is:
    272. -
    273. * <ul>
    274. -
    275. * <li>{@link patio.Dataset#all}</li>
    276. -
    277. * <li>{@link patio.Dataset#one}</li>
    278. -
    279. * <li>{@link patio.Dataset#avg}</li>
    280. -
    281. * <li>{@link patio.Dataset#count}</li>
    282. -
    283. * <li>{@link patio.Dataset#columns}</li>
    284. -
    285. * <li>{@link patio.Dataset#remove}</li>
    286. -
    287. * <li>{@link patio.Dataset#forEach}</li>
    288. -
    289. * <li>{@link patio.Dataset#empty}</li>
    290. -
    291. * <li>{@link patio.Dataset#first}</li>
    292. -
    293. * <li>{@link patio.Dataset#get}</li>
    294. -
    295. * <li>{@link patio.Dataset#import}</li>
    296. -
    297. * <li>{@link patio.Dataset#insert}</li>
    298. -
    299. * <li>{@link patio.Dataset#save}</li>
    300. -
    301. * <li>{@link patio.Dataset#insertMultiple}</li>
    302. -
    303. * <li>{@link patio.Dataset#saveMultiple}</li>
    304. -
    305. * <li>{@link patio.Dataset#interval}</li>
    306. -
    307. * <li>{@link patio.Dataset#last}</li>
    308. -
    309. * <li>{@link patio.Dataset#map}</li>
    310. -
    311. * <li>{@link patio.Dataset#max}</li>
    312. -
    313. * <li>{@link patio.Dataset#min}</li>
    314. -
    315. * <li>{@link patio.Dataset#multiInsert}</li>
    316. -
    317. * <li>{@link patio.Dataset#range}</li>
    318. -
    319. * <li>{@link patio.Dataset#selectHash}</li>
    320. -
    321. * <li>{@link patio.Dataset#selectMap}</li>
    322. -
    323. * <li>{@link patio.Dataset#selectOrderMap}</li>
    324. -
    325. * <li>{@link patio.Dataset#set}</li>
    326. -
    327. * <li>{@link patio.Dataset#singleRecord}</li>
    328. -
    329. * <li>{@link patio.Dataset#singleValue}</li>
    330. -
    331. * <li>{@link patio.Dataset#sum}</li>
    332. -
    333. * <li>{@link patio.Dataset#toCsv}</li>
    334. -
    335. * <li>{@link patio.Dataset#toHash}</li>
    336. -
    337. * <li>{@link patio.Dataset#truncate}</li>
    338. -
    339. * <li>{@link patio.Dataset#update}</li>
    340. -
    341. * </ul>
    342. -
    343. *
    344. -
    345. * </p>
    346. -
    347. * </p>
    348. -
    349. *
    350. -
    351. * @constructs
    352. -
    353. *
    354. -
    355. *
    356. -
    357. * @param {patio.Database} db the database this dataset should use when querying for data.
    358. -
    359. * @param {Object} opts options to set on this dataset instance
    360. -
    361. *
    362. -
    363. * @property {Function} rowCb callback to be invoked for each row returned from the database.
    364. -
    365. * the return value will be used as the result of query. The rowCb can also return a promise,
    366. -
    367. * The resolved value of the promise will be used as result.
    368. -
    369. *
    370. -
    371. * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
    372. -
    373. * This value will be defaulted to whatever the identifierInputMethod
    374. -
    375. * is on the database used in initialization.
    376. -
    377. *
    378. -
    379. * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
    380. -
    381. * This value will be defaulted to whatever the identifierOutputMethod
    382. -
    383. * is on the database used in initialization.
    384. -
    385. * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
    386. -
    387. * throws a {patio.DatasetError} tf the dataset doesn't have a table.
    388. -
    389. * <pre class="code">
    390. -
    391. * DB.from("table").firstSourceAlias;
    392. -
    393. * //=> "table"
    394. -
    395. *
    396. -
    397. * DB.from("table___t").firstSourceAlias;
    398. -
    399. * //=> "t"
    400. -
    401. * </pre>
    402. -
    403. *
    404. -
    405. * @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't
    406. -
    407. * have a table, raises a {@link patio.erros.DatasetError}.
    408. -
    409. *<pre class="code">
    410. -
    411. *
    412. -
    413. * DB.from("table").firstSourceTable;
    414. -
    415. * //=> "table"
    416. -
    417. *
    418. -
    419. * DB.from("table___t").firstSourceTable;
    420. -
    421. * //=> "t"
    422. -
    423. * </pre>
    424. -
    425. * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
    426. -
    427. * <pre class="code">
    428. -
    429. * DB.from("items").isSimpleSelectAll; //=> true
    430. -
    431. * DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
    432. -
    433. * </pre>
    434. -
    435. * @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.
    436. -
    437. * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
    438. -
    439. * delete and update statements. Accurate in this case is the number of
    440. -
    441. * rows matched by the dataset's filter.
    442. -
    443. * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
    444. -
    445. * as most allow strings with ISO 8601 format).
    446. -
    447. * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
    448. -
    449. * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
    450. -
    451. * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
    452. -
    453. * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
    454. -
    455. * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
    456. -
    457. * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
    458. -
    459. * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
    460. -
    461. * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
    462. -
    463. * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
    464. -
    465. * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
    466. -
    467. * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
    468. -
    469. */
    470. -
    471. constructor:function (db, opts) {
    472. -
    473. 29542 this._super(arguments);
    474. -
    475. 29542 this.db = db;
    476. -
    477. 29542 this.__opts = {};
    478. -
    479. 29542 this.__rowCb = null;
    480. -
    481. 29542 if (db) {
    482. -
    483. 15365 this.__quoteIdentifiers = db.quoteIdentifiers;
    484. -
    485. 15365 this.__identifierInputMethod = db.identifierInputMethod;
    486. -
    487. 15365 this.__identifierOutputMethod = db.identifierOutputMethod;
    488. -
    489. }
    490. +
    491. 18 return ret.promise();
    492. },
    493. -
    494. -
    495. /**
    496. -
    497. * Returns a new clone of the dataset with with the given options merged into the current datasets options.
    498. -
    499. * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
    500. -
    501. * columns are deleted. This method should generally not be called
    502. -
    503. * directly by user code.
    504. -
    505. *
    506. -
    507. * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
    508. -
    509. * @return [patio.Dataset] a cloned dataset with the merged options
    510. -
    511. **/
    512. -
    513. mergeOptions:function (opts) {
    514. -
    515. 15274 opts = isUndefined(opts) ? {} : opts;
    516. -
    517. 15274 var ds = new this._static(this.db, {});
    518. -
    519. 15274 ds.rowCb = this.rowCb;
    520. -
    521. 15274 this._static.FEATURES.forEach(function (f) {
    522. -
    523. 213836 ds[f] = this[f];
    524. -
    525. }, this);
    526. -
    527. 15274 ds.__opts = merge({}, this.__opts, opts);
    528. -
    529. 15274 ds.identifierInputMethod = this.identifierInputMethod;
    530. -
    531. 15274 ds.identifierOutputMethod = this.identifierOutputMethod;
    532. -
    533. 15274 var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
    534. -
    535. 15274 if (Object.keys(opts).some(function (o) {
    536. -
    537. 13862 return columnChangeOpts.indexOf(o) != -1;
    538. -
    539. })) {
    540. -
    541. 2782 ds.__opts.columns = null;
    542. +
    543. __getMirationFiles:function () {
    544. +
    545. 18 var ret = new Promise();
    546. +
    547. 18 var upMigrations = [], downMigrations = [], target = this.target;
    548. +
    549. 18 if (!this.__migrationFiles) {
    550. +
    551. 18 when(
    552. +
    553. this.getFileNames(),
    554. +
    555. this.__getAppliedMigrations()
    556. +
    557. ).then(hitch(this, function (res) {
    558. +
    559. 18 var files = res[0], appliedMigrations = res[1];
    560. +
    561. 18 var l = files.length, inserts = [];
    562. +
    563. 18 if (l > 0) {
    564. +
    565. 18 try {
    566. +
    567. 18 for (var i = 0; i < l; i++) {
    568. +
    569. 92 var file = files[i], f = path.basename(file), fLowerCase = f.toLowerCase();
    570. +
    571. 92 if (!isUndefined(target)) {
    572. +
    573. 48 var version = this.getMigrationVersionFromFile(f);
    574. +
    575. 48 if (version > target || (version === 0 && target === version)) {
    576. +
    577. 35 if (appliedMigrations.indexOf(fLowerCase) != -1) {
    578. +
    579. 26 downMigrations.push([f, require(file), "down"]);
    580. +
    581. }
    582. +
    583. 13 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
    584. +
    585. 9 upMigrations.push([f, require(file), "up"]);
    586. +
    587. }
    588. +
    589. 44 } else if (appliedMigrations.indexOf(fLowerCase) == -1) {
    590. +
    591. 35 upMigrations.push([f, require(file), "up"]);
    592. +
    593. }
    594. +
    595. }
    596. +
    597. } catch (e) {
    598. +
    599. 0 return ret.errback(e)
    600. +
    601. }
    602. +
    603. 18 this.__migrationFiles = upMigrations.concat(downMigrations.reverse())
    604. +
    605. 18 ret.callback(this.__migrationFiles);
    606. +
    607. } else {
    608. +
    609. 0 return ret.callback();
    610. +
    611. }
    612. +
    613. }), ret);
    614. +
    615. } else {
    616. +
    617. 0 ret.callback(this.__migrationFiles);
    618. }
    619. -
    620. 15274 return ds;
    621. +
    622. 18 return ret.promise();
    623. },
    624. -
    625. /**
    626. -
    627. * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
    628. -
    629. * or {@link patio.sql.AliasedExpression}, depending on the format:
    630. -
    631. *
    632. -
    633. * <ul>
    634. -
    635. * <li>For columns : table__column___alias.</li>
    636. -
    637. * <li>For tables : schema__table___alias.</li>
    638. -
    639. * </ul>
    640. -
    641. * each portion of the identifier is optional. See example below
    642. -
    643. *
    644. -
    645. * @example
    646. -
    647. *
    648. -
    649. * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
    650. -
    651. * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
    652. -
    653. * ds.stringToIdentifier("table__column___alias");
    654. -
    655. * //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
    656. -
    657. *
    658. -
    659. * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
    660. -
    661. * or {@link patio.sql.AliasedExpression}.
    662. -
    663. *
    664. -
    665. * @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
    666. -
    667. */
    668. -
    669. stringToIdentifier:function (name) {
    670. -
    671. 15093 if (isString(name)) {
    672. -
    673. 10138 var parts = this._splitString(name);
    674. -
    675. 10138 var schema = parts[0], table = parts[1], alias = parts[2];
    676. -
    677. 10138 return (schema && table && alias
    678. -
    679. ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
    680. -
    681. : (schema && table
    682. -
    683. ? new QualifiedIdentifier(schema, table)
    684. -
    685. : (table && alias
    686. -
    687. ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
    688. +
    689. // Returns the dataset for the schema_migrations table. If no such table
    690. +
    691. // exists, it is automatically created.
    692. +
    693. _getSchemaDataset:function () {
    694. +
    695. 36 var ret = new Promise();
    696. +
    697. 36 if (!this.__schemaDataset) {
    698. +
    699. 18 var ds = this.db.from(this.table);
    700. +
    701. 18 this.__createTable().then(hitch(this, function () {
    702. +
    703. 18 this.__schemaDataset = ds;
    704. +
    705. 18 ret.callback(ds);
    706. +
    707. }), ret);
    708. } else {
    709. -
    710. 4955 return name;
    711. -
    712. }
    713. -
    714. },
    715. -
    716. -
    717. /**
    718. -
    719. * Can either be a string or null.
    720. -
    721. *
    722. -
    723. *
    724. -
    725. * @example
    726. -
    727. * //columns
    728. -
    729. * table__column___alias //=> table.column as alias
    730. -
    731. * table__column //=> table.column
    732. -
    733. * //tables
    734. -
    735. * schema__table___alias //=> schema.table as alias
    736. -
    737. * schema__table //=> schema.table
    738. -
    739. *
    740. -
    741. * //name and alias
    742. -
    743. * columnOrTable___alias //=> columnOrTable as alias
    744. -
    745. *
    746. -
    747. *
    748. -
    749. *
    750. -
    751. * @return {String[]} an array with the elements being:
    752. -
    753. * <ul>
    754. -
    755. * <li>For columns :[table, column, alias].</li>
    756. -
    757. * <li>For tables : [schema, table, alias].</li>
    758. -
    759. * </ul>
    760. -
    761. */
    762. -
    763. _splitString:function (s) {
    764. -
    765. 19662 var ret, m;
    766. -
    767. 19662 if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
    768. -
    769. 189 ret = m.slice(1);
    770. -
    771. }
    772. -
    773. 19473 else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
    774. -
    775. 28 ret = [null, m[1], m[2]];
    776. -
    777. }
    778. -
    779. 19445 else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
    780. -
    781. 2079 ret = [m[1], m[2], null];
    782. -
    783. }
    784. -
    785. else {
    786. -
    787. 17366 ret = [null, s, null];
    788. +
    789. 18 ret.callback(this.__schemaDataset);
    790. }
    791. -
    792. 19662 return ret;
    793. +
    794. 36 return ret.promise();
    795. },
    796. -
    797. /**
    798. -
    799. * @ignore
    800. -
    801. **/
    802. -
    803. getters:{
    804. -
    805. -
    806. rowCb:function () {
    807. -
    808. 23378 return this.__rowCb;
    809. -
    810. },
    811. -
    812. -
    813. identifierInputMethod:function () {
    814. -
    815. 15274 return this.__identifierInputMethod;
    816. -
    817. },
    818. -
    819. -
    820. identifierOutputMethod:function () {
    821. -
    822. 15274 return this.__identifierOutputMethod;
    823. -
    824. },
    825. -
    826. -
    827. firstSourceAlias:function () {
    828. -
    829. 905 var source = this.__opts.from;
    830. -
    831. 905 if (isUndefinedOrNull(source) || !source.length) {
    832. -
    833. 2 throw new DatasetError("No source specified for the query");
    834. -
    835. }
    836. -
    837. 903 source = source[0];
    838. -
    839. 903 if (isInstanceOf(source, AliasedExpression)) {
    840. -
    841. 20 return source.alias;
    842. -
    843. 883 } else if (isString(source)) {
    844. -
    845. 0 var parts = this._splitString(source);
    846. -
    847. 0 var alias = parts[2];
    848. -
    849. 0 return alias ? alias : source;
    850. -
    851. } else {
    852. -
    853. 883 return source;
    854. -
    855. }
    856. -
    857. },
    858. +
    859. __convertSchemaInfo:function () {
    860. +
    861. 1 var ret = new Promise(), c = this.column;
    862. +
    863. 1 var ds = this.db.from(this.table);
    864. +
    865. 1 this.db.from(IntegerMigrator.DEFAULT_SCHEMA_TABLE).get(IntegerMigrator.DEFAULT_SCHEMA_COLUMN).then(hitch(this, function (version) {
    866. +
    867. 1 this.getFileNames().then(hitch(this, function (files) {
    868. +
    869. 1 var l = files.length, inserts = [];
    870. +
    871. 1 if (l > 0) {
    872. +
    873. 1 for (var i = 0; i < l; i++) {
    874. +
    875. 7 var f = path.basename(files[i]);
    876. +
    877. 7 if (this.getMigrationVersionFromFile(f) <= version) {
    878. +
    879. 5 var insert = {};
    880. +
    881. 5 insert[c] = f;
    882. +
    883. 5 inserts.push(ds.insert(insert));
    884. +
    885. }
    886. +
    887. }
    888. +
    889. }
    890. +
    891. 1 if (inserts.length) {
    892. +
    893. 1 when.apply(comb, inserts).then(ret);
    894. +
    895. } else {
    896. +
    897. 0 ret.callback();
    898. +
    899. }
    900. +
    901. }), ret);
    902. +
    903. }), ret);
    904. +
    905. 1 return ret.promise();
    906. -
    907. firstSourceTable:function () {
    908. -
    909. 15 var source = this.__opts.from;
    910. -
    911. 15 if (isUndefinedOrNull(source) || !source.length) {
    912. -
    913. 1 throw new QueryError("No source specified for the query");
    914. -
    915. }
    916. -
    917. 14 var source = source[0];
    918. -
    919. 14 if (isInstanceOf(source, AliasedExpression)) {
    920. -
    921. 3 return source.expression;
    922. -
    923. 11 } else if (isString(source)) {
    924. -
    925. 0 var parts = this._splitString(source);
    926. -
    927. 0 return source;
    928. -
    929. } else {
    930. -
    931. 11 return source;
    932. -
    933. }
    934. -
    935. }
    936. },
    937. -
    938. /**
    939. -
    940. * @ignore
    941. -
    942. **/
    943. -
    944. setters:{
    945. -
    946. /**@lends patio.Dataset.prototype*/
    947. -
    948. -
    949. identifierInputMethod:function (meth) {
    950. -
    951. 15364 this.__identifierInputMethod = meth;
    952. -
    953. },
    954. -
    955. -
    956. identifierOutputMethod:function (meth) {
    957. -
    958. 15364 this.__identifierOutputMethod = meth;
    959. -
    960. },
    961. -
    962. -
    963. rowCb:function (cb) {
    964. -
    965. 18890 if (isFunction(cb) || isNull(cb)) {
    966. -
    967. 18885 this.__rowCb = cb;
    968. +
    969. __createTable:function () {
    970. +
    971. 18 var c = this.column, table = this.table, db = this.db, intMigrationTable = IntegerMigrator.DEFAULT_SCHEMA_TABLE;
    972. +
    973. 18 var ds = this.db.from(table);
    974. +
    975. 18 var ret = new Promise();
    976. +
    977. 18 when(
    978. +
    979. db.tableExists(table),
    980. +
    981. db.tableExists(intMigrationTable)
    982. +
    983. ).then(hitch(this, function (res) {
    984. +
    985. 18 var exists = res[0], intMigratorExists = res[1];
    986. +
    987. 18 if (!exists) {
    988. +
    989. 10 db.createTable(table,
    990. +
    991. function () {
    992. +
    993. 10 this.column(c, String, {primaryKey:true});
    994. +
    995. }).addErrback(ret);
    996. +
    997. 10 if (intMigratorExists) {
    998. +
    999. 1 db.from(intMigrationTable).all().then(hitch(this, function (versions) {
    1000. +
    1001. 1 var version;
    1002. +
    1003. 1 if (versions.length === 1 && (version = versions[0]) && isNumber(version[Object.keys(version)[0]])) {
    1004. +
    1005. 1 this.__convertSchemaInfo().then(ret);
    1006. +
    1007. } else {
    1008. +
    1009. 0 ret.callback();
    1010. +
    1011. }
    1012. +
    1013. }));
    1014. +
    1015. } else {
    1016. +
    1017. 9 ret.callback();
    1018. +
    1019. }
    1020. } else {
    1021. -
    1022. 5 throw new DatasetError("rowCb mus be a function");
    1023. +
    1024. 8 ds.columns.then(hitch(this, function (columns) {
    1025. +
    1026. 8 if (columns.indexOf(c) === -1) {
    1027. +
    1028. 0 ret.errback(new MigrationError(format("Migration table %s does not contain column %s", table, c)));
    1029. +
    1030. } else {
    1031. +
    1032. 8 ret.callback();
    1033. +
    1034. }
    1035. +
    1036. }), ret);
    1037. }
    1038. -
    1039. }
    1040. +
    1041. }), ret);
    1042. +
    1043. 18 return ret.promise();
    1044. }
    1045. },
    1046. static:{
    1047. -
    1048. COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
    1049. -
    1050. COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
    1051. -
    1052. COLUMN_REF_RE3:/^(\w+)__(\w+)$/
    1053. +
    1054. DEFAULT_SCHEMA_COLUMN:"filename",
    1055. +
    1056. DEFAULT_SCHEMA_TABLE:"schema_migrations"
    1057. }
    1058. -
    1059. }).as(module);
    1060. +
    1061. }).as(exports, "TimestampMigrator");
    1062. -
    +
  • 1exports.run = function () {
  • +
  • 31 return Migrator.run.apply(Migrator, arguments);
  • +
  • };
  • @@ -13876,7 +13892,7 @@
    Coverage92.86 - SLOC1683 + SLOC1684 LOC504 Missed36
    @@ -13884,44 +13900,45 @@
    1. 1var comb = require("comb"),
    2. -
    3. define = comb.define,
    4. -
    5. array = comb.array,
    6. -
    7. intersect = array.intersect,
    8. -
    9. compact = array.compact,
    10. -
    11. string = comb.string,
    12. -
    13. format = string.format,
    14. -
    15. argsToArray = comb.argsToArray,
    16. -
    17. isInstanceOf = comb.isInstanceOf,
    18. -
    19. isArray = comb.isArray,
    20. -
    21. isNumber = comb.isNumber,
    22. -
    23. isDate = comb.isDate,
    24. -
    25. isNull = comb.isNull,
    26. -
    27. isBoolean = comb.isBoolean,
    28. -
    29. isFunction = comb.isFunction,
    30. -
    31. isUndefined = comb.isUndefined,
    32. -
    33. isObject = comb.isObject,
    34. -
    35. isHash = comb.isHash,
    36. -
    37. merge = comb.merge,
    38. -
    39. isUndefinedOrNull = comb.isUndefinedOrNull,
    40. -
    41. isString = comb.isString,
    42. -
    43. sql = require("../sql").sql,
    44. -
    45. Expression = sql.Expression,
    46. -
    47. ComplexExpression = sql.ComplexExpression,
    48. -
    49. AliasedExpression = sql.AliasedExpression,
    50. -
    51. Identifier = sql.Identifier,
    52. -
    53. QualifiedIdentifier = sql.QualifiedIdentifier,
    54. -
    55. OrderedExpression = sql.OrderedExpression,
    56. -
    57. CaseExpression = sql.CaseExpression,
    58. -
    59. SubScript = sql.SubScript,
    60. -
    61. NumericExpression = sql.NumericExpression,
    62. -
    63. ColumnAll = sql.ColumnAll,
    64. -
    65. Cast = sql.Cast,
    66. -
    67. StringExpression = sql.StringExpression,
    68. -
    69. BooleanExpression = sql.BooleanExpression,
    70. -
    71. SQLFunction = sql.SQLFunction,
    72. -
    73. LiteralString = sql.LiteralString,
    74. -
    75. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    76. -
    77. QueryError = require("../errors").QueryError, patio;
    78. +
    79. define = comb.define,
    80. +
    81. array = comb.array,
    82. +
    83. intersect = array.intersect,
    84. +
    85. compact = array.compact,
    86. +
    87. string = comb.string,
    88. +
    89. format = string.format,
    90. +
    91. argsToArray = comb.argsToArray,
    92. +
    93. isInstanceOf = comb.isInstanceOf,
    94. +
    95. isArray = comb.isArray,
    96. +
    97. isNumber = comb.isNumber,
    98. +
    99. isDate = comb.isDate,
    100. +
    101. isNull = comb.isNull,
    102. +
    103. isBoolean = comb.isBoolean,
    104. +
    105. isFunction = comb.isFunction,
    106. +
    107. isUndefined = comb.isUndefined,
    108. +
    109. isObject = comb.isObject,
    110. +
    111. isHash = comb.isHash,
    112. +
    113. isEmpty = comb.isEmpty,
    114. +
    115. merge = comb.merge,
    116. +
    117. isUndefinedOrNull = comb.isUndefinedOrNull,
    118. +
    119. isString = comb.isString,
    120. +
    121. sql = require("../sql").sql,
    122. +
    123. Expression = sql.Expression,
    124. +
    125. ComplexExpression = sql.ComplexExpression,
    126. +
    127. AliasedExpression = sql.AliasedExpression,
    128. +
    129. Identifier = sql.Identifier,
    130. +
    131. QualifiedIdentifier = sql.QualifiedIdentifier,
    132. +
    133. OrderedExpression = sql.OrderedExpression,
    134. +
    135. CaseExpression = sql.CaseExpression,
    136. +
    137. SubScript = sql.SubScript,
    138. +
    139. NumericExpression = sql.NumericExpression,
    140. +
    141. ColumnAll = sql.ColumnAll,
    142. +
    143. Cast = sql.Cast,
    144. +
    145. StringExpression = sql.StringExpression,
    146. +
    147. BooleanExpression = sql.BooleanExpression,
    148. +
    149. SQLFunction = sql.SQLFunction,
    150. +
    151. LiteralString = sql.LiteralString,
    152. +
    153. PlaceHolderLiteralString = sql.PlaceHolderLiteralString,
    154. +
    155. QueryError = require("../errors").QueryError, patio;
    156. 1var Dataset;
    157. @@ -13985,9 +14002,9 @@
    158. constructor:function () {
    159. //We initialize these here because otherwise
    160. //the will be blank because of recursive dependencies.
    161. -
    162. 29542 !patio && (patio = require("../index"));
    163. -
    164. 29542 !Dataset && (Dataset = patio.Dataset);
    165. -
    166. 29542 this._super(arguments);
    167. +
    168. 29216 !patio && (patio = require("../index"));
    169. +
    170. 29216 !Dataset && (Dataset = patio.Dataset);
    171. +
    172. 29216 this._super(arguments);
    173. },
    174. /**
    175. @@ -14272,15 +14289,15 @@
    176. * @return {String} a literal representation of the value.
    177. */
    178. literal:function (v) {
    179. -
    180. 44748 if (isInstanceOf(v, LiteralString)) {
    181. +
    182. 44422 if (isInstanceOf(v, LiteralString)) {
    183. 204 return "" + v;
    184. -
    185. 44544 } else if (isString(v)) {
    186. +
    187. 44218 } else if (isString(v)) {
    188. 6110 return this._literalString(v);
    189. -
    190. 38434 } else if (isNumber(v)) {
    191. +
    192. 38108 } else if (isNumber(v)) {
    193. 6997 return this._literalNumber(v);
    194. }
    195. -
    196. 31437 else if (isInstanceOf(v, Expression)) {
    197. -
    198. 28903 return this._literalExpression(v);
    199. +
    200. 31111 else if (isInstanceOf(v, Expression)) {
    201. +
    202. 28577 return this._literalExpression(v);
    203. }
    204. 2534 else if (isInstanceOf(v, Dataset)) {
    205. 104 return this._literalDataset(v);
    206. @@ -14332,7 +14349,7 @@
    207. 59 return new QualifiedIdentifier(table, e);
    208. 217 } else if (isInstanceOf(e, OrderedExpression)) {
    209. 2 return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending,
    210. -
    211. {nulls:e.nulls});
    212. +
    213. {nulls:e.nulls});
    214. 215 } else if (isInstanceOf(e, AliasedExpression)) {
    215. 72 return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias);
    216. 143 } else if (isInstanceOf(e, CaseExpression)) {
    217. @@ -14500,9 +14517,9 @@
    218. 1440 var columns = this.__opts.columns, ret = "";
    219. 1440 if (columns && columns.length) {
    220. 1387 ret = " (" + columns.map(
    221. -
    222. function (c) {
    223. -
    224. 6691 return c.toString(this);
    225. -
    226. }, this).join(this._static.COMMA_SEPARATOR) + ")";
    227. +
    228. function (c) {
    229. +
    230. 6691 return c.toString(this);
    231. +
    232. }, this).join(this._static.COMMA_SEPARATOR) + ")";
    233. }
    234. 1440 return ret;
    235. },
    236. @@ -14592,7 +14609,7 @@
    237. * SQL fragment for specifying all columns in a given table
    238. **/
    239. columnAllSql:function (ca) {
    240. -
    241. 345 return string.format("%s.*", this.quoteSchemaTable(ca.table));
    242. +
    243. 19 return string.format("%s.*", this.quoteSchemaTable(ca.table));
    244. },
    245. /**
    246. @@ -14614,8 +14631,8 @@
    247. 0 return this.complexExpressionSql("EQ", args);
    248. } else {
    249. 0 return this.complexExpressionSql("OR",
    250. -
    251. [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
    252. -
    253. null)]);
    254. +
    255. [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0],
    256. +
    257. null)]);
    258. }
    259. 6454 } else if (["IN", "NOTIN"].indexOf(op) != -1) {
    260. @@ -14646,8 +14663,8 @@
    261. //literal so that if values is an array of two element arrays, it
    262. //will be treated as a value list instead of a condition specifier.
    263. 4 return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
    264. -
    265. ComplexExpression.IN_OPERATORS[op],
    266. -
    267. valArray ? this._arraySql(vals) : this.literal(vals));
    268. +
    269. ComplexExpression.IN_OPERATORS[op],
    270. +
    271. valArray ? this._arraySql(vals) : this.literal(vals));
    272. }
    273. }
    274. else {
    275. @@ -14661,13 +14678,13 @@
    276. }
    277. } else {
    278. 10 return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols),
    279. -
    280. ComplexExpression.IN_OPERATORS[op], this.literal(vals));
    281. +
    282. ComplexExpression.IN_OPERATORS[op], this.literal(vals));
    283. }
    284. }
    285. 6437 } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) {
    286. 5427 var l = args[0];
    287. 5427 return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp,
    288. -
    289. this.literal(args[1]));
    290. +
    291. this.literal(args[1]));
    292. 1010 } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) {
    293. 976 return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " "));
    294. 34 } else if (op == "NOT") {
    295. @@ -14710,7 +14727,7 @@
    296. }
    297. 925 var tref = this.__tableRef(table);
    298. 925 return string.format(" %s %s", this._joinTypeSql(jc.joinType),
    299. -
    300. tableAlias ? this.__asSql(tref, tableAlias) : tref);
    301. +
    302. tableAlias ? this.__asSql(tref, tableAlias) : tref);
    303. },
    304. /**
    305. @@ -14783,11 +14800,11 @@
    306. */
    307. qualifiedIdentifierSql:function (qcr) {
    308. 4532 return [qcr.table, qcr.column].map(
    309. -
    310. function (x) {
    311. -
    312. 9064 return [QualifiedIdentifier, Identifier, String].some(function (c) {
    313. -
    314. 25671 return x instanceof c
    315. -
    316. }) ? this.literal(x) : this.quoteIdentifier(x)
    317. -
    318. }, this).join('.');
    319. +
    320. function (x) {
    321. +
    322. 9064 return [QualifiedIdentifier, Identifier, String].some(function (c) {
    323. +
    324. 25671 return x instanceof c
    325. +
    326. }) ? this.literal(x) : this.quoteIdentifier(x)
    327. +
    328. }, this).join('.');
    329. },
    330. /**
    331. @@ -14798,18 +14815,18 @@
    332. * quote the name with {@link patio.dataset._Sql#_quotedIdentifier}.
    333. */
    334. quoteIdentifier:function (name) {
    335. -
    336. 34093 if (isInstanceOf(name, LiteralString)) {
    337. +
    338. 33767 if (isInstanceOf(name, LiteralString)) {
    339. 260 return name;
    340. } else {
    341. -
    342. 33833 if (isInstanceOf(name, Identifier)) {
    343. +
    344. 33507 if (isInstanceOf(name, Identifier)) {
    345. 22237 name = name.value;
    346. }
    347. -
    348. 33833 name = this.inputIdentifier(name);
    349. -
    350. 33833 if (this.quoteIdentifiers) {
    351. -
    352. 26623 name = this._quotedIdentifier(name)
    353. +
    354. 33507 name = this.inputIdentifier(name);
    355. +
    356. 33507 if (this.quoteIdentifiers) {
    357. +
    358. 26311 name = this._quotedIdentifier(name)
    359. }
    360. }
    361. -
    362. 33833 return name;
    363. +
    364. 33507 return name;
    365. },
    366. /**
    367. @@ -14819,15 +14836,15 @@
    368. * identifierOutputMethod.
    369. */
    370. inputIdentifier:function (v) {
    371. -
    372. 34019 var i = this.__identifierInputMethod;
    373. -
    374. 34019 v = v.toString(this);
    375. -
    376. 34019 return !isUndefinedOrNull(i) ?
    377. -
    378. isFunction(v[i]) ?
    379. -
    380. v[i]() :
    381. -
    382. isFunction(comb[i]) ?
    383. -
    384. comb[i](v)
    385. -
    386. : v
    387. -
    388. : v;
    389. +
    390. 33693 var i = this.__identifierInputMethod;
    391. +
    392. 33693 v = v.toString(this);
    393. +
    394. 33693 return !isUndefinedOrNull(i) ?
    395. +
    396. isFunction(v[i]) ?
    397. +
    398. v[i]() :
    399. +
    400. isFunction(comb[i]) ?
    401. +
    402. comb[i](v)
    403. +
    404. : v
    405. +
    406. : v;
    407. },
    408. /**
    409. @@ -14837,15 +14854,15 @@
    410. * identifierOutputMethod.
    411. */
    412. outputIdentifier:function (v) {
    413. -
    414. 19523 (v == '' && (v = 'untitled'));
    415. -
    416. 19523 var i = this.__identifierOutputMethod;
    417. -
    418. 19523 return !isUndefinedOrNull(i) ?
    419. -
    420. isFunction(v[i]) ?
    421. -
    422. v[i]() :
    423. -
    424. isFunction(comb[i]) ?
    425. -
    426. comb[i](v)
    427. -
    428. : v
    429. -
    430. : v;
    431. +
    432. 19885 (v == '' && (v = 'untitled'));
    433. +
    434. 19885 var i = this.__identifierOutputMethod;
    435. +
    436. 19885 return !isUndefinedOrNull(i) ?
    437. +
    438. isFunction(v[i]) ?
    439. +
    440. v[i]() :
    441. +
    442. isFunction(comb[i]) ?
    443. +
    444. comb[i](v)
    445. +
    446. : v
    447. +
    448. : v;
    449. },
    450. /**
    451. @@ -14855,10 +14872,10 @@
    452. * quoted (if quoting identifiers)
    453. */
    454. quoteSchemaTable:function (table) {
    455. -
    456. 2557 var parts = this.schemaAndTable(table);
    457. -
    458. 2557 var schema = parts[0];
    459. -
    460. 2557 table = parts[1];
    461. -
    462. 2557 return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));
    463. +
    464. 2231 var parts = this.schemaAndTable(table);
    465. +
    466. 2231 var schema = parts[0];
    467. +
    468. 2231 table = parts[1];
    469. +
    470. 2231 return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table));
    471. },
    472. @@ -14867,15 +14884,15 @@
    473. * Split the schema information from the table
    474. */
    475. schemaAndTable:function (tableName) {
    476. -
    477. 2801 var sch = this.db ? this.db.defaultSchema || null : null;
    478. -
    479. 2801 if (isString(tableName)) {
    480. +
    481. 2475 var sch = this.db ? this.db.defaultSchema || null : null;
    482. +
    483. 2475 if (isString(tableName)) {
    484. 1008 var parts = this._splitString(tableName);
    485. 1008 var s = parts[0], table = parts[1];
    486. 1008 return [s || sch, table];
    487. -
    488. 1793 } else if (isInstanceOf(tableName, QualifiedIdentifier)) {
    489. +
    490. 1467 } else if (isInstanceOf(tableName, QualifiedIdentifier)) {
    491. 3 return [tableName.table, tableName.column]
    492. -
    493. 1790 } else if (isInstanceOf(tableName, Identifier)) {
    494. -
    495. 1790 return [null, tableName.value];
    496. +
    497. 1464 } else if (isInstanceOf(tableName, Identifier)) {
    498. +
    499. 1464 return [null, tableName.value];
    500. } else {
    501. 0 throw new QueryError("table should be a QualifiedIdentifier, Identifier, or String");
    502. }
    503. @@ -14924,7 +14941,7 @@
    504. * expressions.
    505. */
    506. __expressionList:function (columns) {
    507. -
    508. 4610 return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);
    509. +
    510. 4284 return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR);
    511. },
    512. //Format the timestamp based on the default_timestamp_format, with a couple
    513. @@ -14941,9 +14958,9 @@
    514. */
    515. _joinTypeSql:function (joinType) {
    516. 922 return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g,
    517. -
    518. function (m) {
    519. -
    520. 1127 return m.toUpperCase() + " ";
    521. -
    522. }
    523. +
    524. function (m) {
    525. +
    526. 1127 return m.toUpperCase() + " ";
    527. +
    528. }
    529. ).trimRight() + " JOIN";
    530. },
    531. @@ -15046,7 +15063,7 @@
    532. * @return SQL fragment for SQL::Expression, result depends on the specific type of expression.
    533. * */
    534. _literalExpression:function (v) {
    535. -
    536. 28913 return v.toString(this);
    537. +
    538. 28587 return v.toString(this);
    539. },
    540. /**
    541. @@ -15069,10 +15086,10 @@
    542. 6059 var table = parts[0], column = parts[1], alias = parts[2];
    543. 6059 if (!alias) {
    544. 6059 return column && table ? this._literalExpression(QualifiedIdentifier.fromArgs([table, column])) : "'"
    545. -
    546. + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
    547. +
    548. + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"
    549. } else {
    550. 0 return this.literal(new AliasedExpression(column
    551. -
    552. && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
    553. +
    554. && table ? QualifiedIdentifier.fromArgs([table, column]) : new Identifier(column), alias));
    555. }
    556. },
    557. @@ -15365,9 +15382,9 @@
    558. 0 throw new QueryError("No source specified for the query");
    559. }
    560. 6657 return " " + source.map(
    561. -
    562. function (s) {
    563. -
    564. 6971 return this.__tableRef(s);
    565. -
    566. }, this).join(this._static.COMMA_SEPARATOR);
    567. +
    568. function (s) {
    569. +
    570. 6971 return this.__tableRef(s);
    571. +
    572. }, this).join(this._static.COMMA_SEPARATOR);
    573. },
    574. /**
    575. @@ -15712,7 +15729,7 @@
    576. * @type String
    577. */
    578. identifierInputMethod:function () {
    579. -
    580. 15364 return this.__identifierInputMethod;
    581. +
    582. 15038 return this.__identifierInputMethod;
    583. },
    584. /**
    585. @@ -15724,7 +15741,7 @@
    586. * @type String
    587. */
    588. identifierOutputMethod:function () {
    589. -
    590. 15364 return this.__identifierOutputMethod;
    591. +
    592. 15038 return this.__identifierOutputMethod;
    593. },
    594. /**
    595. @@ -15737,7 +15754,7 @@
    596. * @default true
    597. */
    598. quoteIdentifiers:function () {
    599. -
    600. 15330 return this.__quoteIdentifiers;
    601. +
    602. 15004 return this.__quoteIdentifiers;
    603. }
    604. },
    605. @@ -15880,10 +15897,10 @@
    606. /**@ignore*/
    607. constructor:function () {
    608. -
    609. 29542 if (!Dataset) {
    610. +
    611. 29216 if (!Dataset) {
    612. 1 Dataset = require("../index").Dataset;
    613. }
    614. -
    615. 29542 this._super(arguments);
    616. +
    617. 29216 this._super(arguments);
    618. },
    619. @@ -15913,7 +15930,7 @@
    620. 2734 var a = [];
    621. 2734 var ret = new Promise().classic(cb);
    622. 2734 this.forEach(hitch(this, function (r) {
    623. -
    624. 2934 a.push(r);
    625. +
    626. 2947 a.push(r);
    627. })).then(hitch(this, function () {
    628. 2731 this.postLoad(a);
    629. 2731 if (block) {
    630. @@ -17159,10 +17176,10 @@
    631. *
    632. */
    633. constructor:function () {
    634. -
    635. 3111 if (comb.isUndefinedOrNull(this.__associations)) {
    636. -
    637. 3002 this.__associations = {};
    638. +
    639. 3107 if (comb.isUndefinedOrNull(this.__associations)) {
    640. +
    641. 3000 this.__associations = {};
    642. }
    643. -
    644. 3111 this._super(arguments);
    645. +
    646. 3107 this._super(arguments);
    647. },
    648. reload:function () {
    649. @@ -18089,240 +18106,29 @@
    650. * });
    651. * }
    652. *
    653. -
    654. *
    655. -
    656. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    657. -
    658. *
    659. -
    660. * @throws {patio.ModelError} if name is not a function or string.
    661. -
    662. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    663. -
    664. */
    665. -
    666. validate:function (name) {
    667. -
    668. 41 this.__initValidation();
    669. -
    670. 41 var ret;
    671. -
    672. 41 if (isFunction(name)) {
    673. -
    674. 1 name.apply(this, [this.__getValidator.bind(this)]);
    675. -
    676. 1 ret = this;
    677. -
    678. 40 } else if (isString(name)) {
    679. -
    680. 40 ret = this.__getValidator(name);
    681. -
    682. } else {
    683. -
    684. 0 throw new ModelError("name is must be a string or function when validating");
    685. -
    686. }
    687. -
    688. 41 return ret;
    689. -
    690. }
    691. -
    692. }
    693. -
    694. -
    695. }).as(module);
    696. -
    -
    - -
    - - - - - -
    -
    database/logging.js
    -
    -
    - Coverage97.96 - SLOC193 - LOC49 - Missed1 -
    -
    -
    1. 1var comb = require("comb"),
    2. -
    3. define = comb.define,
    4. -
    5. Promise = comb.Promise,
    6. -
    7. isFunction = comb.isFunction,
    8. -
    9. logging = comb.logging,
    10. -
    11. Logger = logging.Logger,
    12. -
    13. hitch = comb.hitch,
    14. -
    15. format = comb.string.format,
    16. -
    17. QueryError = require("../errors").QueryError;
    18. -
    19. -
    20. -
    21. 1var LOGGER = Logger.getLogger("patio.Database");
    22. -
    23. -
    24. 1define(null, {
    25. -
    26. instance:{
    27. -
    28. /**@lends patio.Database.prototype*/
    29. -
    30. -
    31. /**
    32. -
    33. * Logs an INFO level message to the "patio.Database" logger.
    34. -
    35. */
    36. -
    37. logInfo:function () {
    38. -
    39. 8337 if (LOGGER.isInfo) {
    40. -
    41. 8337 LOGGER.info.apply(LOGGER, arguments);
    42. -
    43. }
    44. -
    45. },
    46. -
    47. -
    48. /**
    49. -
    50. * Logs a DEBUG level message to the "patio.Database" logger.
    51. -
    52. */
    53. -
    54. logDebug:function () {
    55. -
    56. 8027 if (LOGGER.isDebug) {
    57. -
    58. 8027 LOGGER.debug.apply(LOGGER, arguments);
    59. -
    60. }
    61. -
    62. },
    63. -
    64. -
    65. /**
    66. -
    67. * Logs an ERROR level message to the "patio.Database" logger.
    68. -
    69. */
    70. -
    71. logError:function (error) {
    72. -
    73. 75 if (LOGGER.isError) {
    74. -
    75. 75 LOGGER.error.apply(LOGGER, arguments);
    76. -
    77. }
    78. -
    79. },
    80. -
    81. -
    82. /**
    83. -
    84. * Logs a WARN level message to the "patio.Database" logger.
    85. -
    86. */
    87. -
    88. logWarn:function () {
    89. -
    90. 1 if (LOGGER.isWarn) {
    91. -
    92. 1 LOGGER.warn.apply(LOGGER, arguments);
    93. -
    94. }
    95. -
    96. },
    97. -
    98. -
    99. /**
    100. -
    101. * Logs a TRACE level message to the "patio.Database" logger.
    102. -
    103. */
    104. -
    105. logTrace:function () {
    106. -
    107. 1 if (LOGGER.isTrace) {
    108. -
    109. 1 LOGGER.trace.apply(LOGGER, arguments);
    110. -
    111. }
    112. -
    113. },
    114. -
    115. -
    116. /**
    117. -
    118. * Logs a FATAL level message to the "patio.Database" logger.
    119. -
    120. */
    121. -
    122. logFatal:function () {
    123. -
    124. 1 if (LOGGER.isFatal) {
    125. -
    126. 1 LOGGER.fatal.apply(LOGGER, arguments);
    127. -
    128. }
    129. -
    130. },
    131. -
    132. -
    133. /* Yield to the block, logging any errors at error level to all loggers,
    134. -
    135. * and all other queries with the duration at warn or info level.
    136. -
    137. * */
    138. -
    139. __logAndExecute:function (sql, args, cb) {
    140. -
    141. 8101 if (isFunction(args)) {
    142. -
    143. 8100 cb = args;
    144. -
    145. 8100 args = null;
    146. -
    147. }
    148. -
    149. -
    150. 8101 if (args) {
    151. -
    152. 0 sql = format("%s; %j", sql, args);
    153. -
    154. }
    155. -
    156. 8101 sql = sql.trim();
    157. -
    158. 8101 var start = new Date();
    159. -
    160. 8101 var ret = new Promise();
    161. -
    162. 8101 if (isFunction(cb)) {
    163. -
    164. 8100 this.logInfo("Executing; %s", sql);
    165. -
    166. 8100 cb().then(hitch(this, function () {
    167. -
    168. 8026 this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
    169. -
    170. 8026 ret.callback.apply(ret, arguments);
    171. -
    172. }), hitch(this, function (err) {
    173. -
    174. 74 err = new QueryError(format("%s: %s", err.message, sql));
    175. -
    176. 74 this.logError(err);
    177. -
    178. 74 ret.errback.apply(ret, [err]);
    179. -
    180. }));
    181. -
    182. } else {
    183. -
    184. 1 throw new QueryError("CB is required");
    185. -
    186. }
    187. -
    188. 8100 return ret.promise();
    189. -
    190. },
    191. -
    192. -
    193. /*Log the given SQL and then execute it on the connection, used by
    194. -
    195. *the transaction code.
    196. -
    197. * */
    198. -
    199. __logConnectionExecute:function (conn, sql) {
    200. -
    201. 2079 return this.__logAndExecute(sql, hitch(this, function () {
    202. -
    203. 2079 return conn[this.connectionExecuteMethod](sql);
    204. -
    205. }));
    206. -
    207. },
    208. -
    209. -
    210. -
    211. getters:{
    212. -
    213. /**@lends patio.Database.prototype*/
    214. -
    215. /**
    216. -
    217. * The "patio.Database" logger.
    218. -
    219. * @field
    220. -
    221. */
    222. -
    223. logger:function () {
    224. -
    225. 2 return LOGGER;
    226. -
    227. }
    228. -
    229. }
    230. -
    231. },
    232. -
    233. -
    234. "static":{
    235. -
    236. /**@lends patio.Database*/
    237. -
    238. /**
    239. -
    240. * Logs an INFO level message to the "patio.Database" logger.
    241. -
    242. */
    243. -
    244. logInfo:function () {
    245. -
    246. 1 if (LOGGER.isInfo) {
    247. -
    248. 1 LOGGER.info.apply(LOGGER, arguments);
    249. -
    250. }
    251. -
    252. },
    253. -
    254. -
    255. /**
    256. -
    257. * Logs a DEBUG level message to the "patio.Database" logger.
    258. -
    259. */
    260. -
    261. logDebug:function () {
    262. -
    263. 1 if (LOGGER.isDebug) {
    264. -
    265. 1 LOGGER.debug.apply(LOGGER, arguments);
    266. -
    267. }
    268. -
    269. },
    270. -
    271. -
    272. /**
    273. -
    274. * Logs a ERROR level message to the "patio.Database" logger.
    275. -
    276. */
    277. -
    278. logError:function () {
    279. -
    280. 1 if (LOGGER.isError) {
    281. -
    282. 1 LOGGER.error.apply(LOGGER, arguments);
    283. -
    284. }
    285. -
    286. },
    287. -
    288. -
    289. /**
    290. -
    291. * Logs a WARN level message to the "patio.Database" logger.
    292. -
    293. */
    294. -
    295. logWarn:function () {
    296. -
    297. 1 if (LOGGER.isWarn) {
    298. -
    299. 1 LOGGER.warn.apply(LOGGER, arguments);
    300. -
    301. }
    302. -
    303. },
    304. -
    305. -
    306. /**
    307. -
    308. * Logs a TRACE level message to the "patio.Database" logger.
    309. -
    310. */
    311. -
    312. logTrace:function () {
    313. -
    314. 1 if (LOGGER.isTrace) {
    315. -
    316. 1 LOGGER.trace.apply(LOGGER, arguments);
    317. -
    318. }
    319. -
    320. },
    321. -
    322. -
    323. /**
    324. -
    325. * Logs a FATAL level message to the "patio.Database" logger.
    326. +
    327. *
    328. +
    329. * @param {String|Function} name the name of the column, or a callback that accepts a function to create validators.
    330. +
    331. *
    332. +
    333. * @throws {patio.ModelError} if name is not a function or string.
    334. +
    335. * @return {patio.Model|Validator} returns a validator if name is a string, other wise returns this for chaining.
    336. */
    337. -
    338. logFatal:function () {
    339. -
    340. 1 if (LOGGER.isFatal) {
    341. -
    342. 1 LOGGER.fatal.apply(LOGGER, arguments);
    343. -
    344. }
    345. -
    346. },
    347. -
    348. -
    349. getters:{
    350. -
    351. /**@lends patio.Database*/
    352. -
    353. /**
    354. -
    355. * The "patio.Database" logger.
    356. -
    357. * @field
    358. -
    359. */
    360. -
    361. logger:function () {
    362. -
    363. 1 return LOGGER;
    364. +
    365. validate:function (name) {
    366. +
    367. 41 this.__initValidation();
    368. +
    369. 41 var ret;
    370. +
    371. 41 if (isFunction(name)) {
    372. +
    373. 1 name.apply(this, [this.__getValidator.bind(this)]);
    374. +
    375. 1 ret = this;
    376. +
    377. 40 } else if (isString(name)) {
    378. +
    379. 40 ret = this.__getValidator(name);
    380. +
    381. } else {
    382. +
    383. 0 throw new ModelError("name is must be a string or function when validating");
    384. }
    385. +
    386. 41 return ret;
    387. }
    388. }
    389. -
    390. }).as(module);
    +
  • }).as(module);
  • +
  • @@ -18333,10 +18139,10 @@
    - Coverage98.84 - SLOC2136 - LOC431 - Missed5 + Coverage97.71 + SLOC2160 + LOC436 + Missed10
    @@ -18401,17 +18207,17 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 29542 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 29542 this._super(arguments);
  • -
  • 29542 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
  • -
  • 206794 if (!this[type + "Join"]) {
  • -
  • 206794 this[type + "Join"] = conditionedJoin.bind(this, type);
  • +
  • 29216 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 29216 this._super(arguments);
  • +
  • 29216 this._static.CONDITIONED_JOIN_TYPES.forEach(function (type) {
  • +
  • 204512 if (!this[type + "Join"]) {
  • +
  • 204512 this[type + "Join"] = conditionedJoin.bind(this, type);
  • }
  • }, this);
  • -
  • 29542 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
  • -
  • 147710 if (!this[type + "Join"]) {
  • -
  • 147710 this[type + "Join"] = unConditionJoin.bind(this, type);
  • +
  • 29216 this._static.UNCONDITIONED_JOIN_TYPES.forEach(function (type) {
  • +
  • 146080 if (!this[type + "Join"]) {
  • +
  • 146080 this[type + "Join"] = unConditionJoin.bind(this, type);
  • }
  • }, this);
  • @@ -19823,29 +19629,29 @@
  • * @return {patio.Dataset} a cloned dataset with the columns selected changed.
  • */
  • select:function (args) {
  • -
  • 861 args = flatten(argsToArray(arguments));
  • -
  • 861 var columns = [];
  • -
  • 861 args.forEach(function (c) {
  • -
  • 1281 if (isFunction(c)) {
  • +
  • 535 args = flatten(argsToArray(arguments));
  • +
  • 535 var columns = [];
  • +
  • 535 args.forEach(function (c) {
  • +
  • 955 if (isFunction(c)) {
  • 23 var res = c.apply(sql, [sql]);
  • 23 columns = columns.concat(isArray(res) ? res : [res]);
  • } else {
  • -
  • 1258 columns.push(c);
  • +
  • 932 columns.push(c);
  • }
  • });
  • -
  • 861 var select = [];
  • -
  • 861 columns.forEach(function (c) {
  • -
  • 1284 if (isHash(c)) {
  • +
  • 535 var select = [];
  • +
  • 535 columns.forEach(function (c) {
  • +
  • 958 if (isHash(c)) {
  • 3 for (var i in c) {
  • 4 select.push(new AliasedExpression(new Identifier(i), c[i]));
  • }
  • -
  • 1281 } else if (isString(c)) {
  • +
  • 955 } else if (isString(c)) {
  • 406 select.push(this.stringToIdentifier(c));
  • } else {
  • -
  • 875 select.push(c);
  • +
  • 549 select.push(c);
  • }
  • }, this);
  • -
  • 861 return this.mergeOptions({select:select});
  • +
  • 535 return this.mergeOptions({select:select});
  • },
  • @@ -19859,6 +19665,30 @@
  • },
  • /**
  • +
  • * Selects the columns if only if there is not already select sources.
  • +
  • *
  • +
  • * @example
  • +
  • *
  • +
  • * var ds = DB.from("items"); //SELECT * FROM items
  • +
  • *
  • +
  • * ds.select("a"); //SELECT a FROM items;
  • +
  • * ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items;
  • +
  • * ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;
  • +
  • *
  • +
  • * @param cols columns to select if there is not already select sources.
  • +
  • * @return {patio.Dataset} a cloned dataset with the appropriate select sources.
  • +
  • */
  • +
  • selectIfNoSource:function (cols) {
  • +
  • 0 var ret;
  • +
  • 0 if (!this.hasSelectSource) {
  • +
  • 0 ret = this.select.apply(this, arguments);
  • +
  • } else {
  • +
  • 0 ret = this.mergeOptions();
  • +
  • }
  • +
  • 0 return ret;
  • +
  • },
  • +
  • +
  • /**
  • * Returns a copy of the dataset with the given columns added
  • * to the existing selected columns. If no columns are currently selected,
  • * it will select the columns given in addition to *.
  • @@ -20479,6 +20309,217 @@
  • as(module);
  • +
    + + + + + +
    +
    database/logging.js
    +
    +
    + Coverage97.96 + SLOC193 + LOC49 + Missed1 +
    +
    +
    1. 1var comb = require("comb"),
    2. +
    3. define = comb.define,
    4. +
    5. Promise = comb.Promise,
    6. +
    7. isFunction = comb.isFunction,
    8. +
    9. logging = comb.logging,
    10. +
    11. Logger = logging.Logger,
    12. +
    13. hitch = comb.hitch,
    14. +
    15. format = comb.string.format,
    16. +
    17. QueryError = require("../errors").QueryError;
    18. +
    19. +
    20. +
    21. 1var LOGGER = Logger.getLogger("patio.Database");
    22. +
    23. +
    24. 1define(null, {
    25. +
    26. instance:{
    27. +
    28. /**@lends patio.Database.prototype*/
    29. +
    30. +
    31. /**
    32. +
    33. * Logs an INFO level message to the "patio.Database" logger.
    34. +
    35. */
    36. +
    37. logInfo:function () {
    38. +
    39. 8337 if (LOGGER.isInfo) {
    40. +
    41. 8337 LOGGER.info.apply(LOGGER, arguments);
    42. +
    43. }
    44. +
    45. },
    46. +
    47. +
    48. /**
    49. +
    50. * Logs a DEBUG level message to the "patio.Database" logger.
    51. +
    52. */
    53. +
    54. logDebug:function () {
    55. +
    56. 8027 if (LOGGER.isDebug) {
    57. +
    58. 8027 LOGGER.debug.apply(LOGGER, arguments);
    59. +
    60. }
    61. +
    62. },
    63. +
    64. +
    65. /**
    66. +
    67. * Logs an ERROR level message to the "patio.Database" logger.
    68. +
    69. */
    70. +
    71. logError:function (error) {
    72. +
    73. 75 if (LOGGER.isError) {
    74. +
    75. 75 LOGGER.error.apply(LOGGER, arguments);
    76. +
    77. }
    78. +
    79. },
    80. +
    81. +
    82. /**
    83. +
    84. * Logs a WARN level message to the "patio.Database" logger.
    85. +
    86. */
    87. +
    88. logWarn:function () {
    89. +
    90. 1 if (LOGGER.isWarn) {
    91. +
    92. 1 LOGGER.warn.apply(LOGGER, arguments);
    93. +
    94. }
    95. +
    96. },
    97. +
    98. +
    99. /**
    100. +
    101. * Logs a TRACE level message to the "patio.Database" logger.
    102. +
    103. */
    104. +
    105. logTrace:function () {
    106. +
    107. 1 if (LOGGER.isTrace) {
    108. +
    109. 1 LOGGER.trace.apply(LOGGER, arguments);
    110. +
    111. }
    112. +
    113. },
    114. +
    115. +
    116. /**
    117. +
    118. * Logs a FATAL level message to the "patio.Database" logger.
    119. +
    120. */
    121. +
    122. logFatal:function () {
    123. +
    124. 1 if (LOGGER.isFatal) {
    125. +
    126. 1 LOGGER.fatal.apply(LOGGER, arguments);
    127. +
    128. }
    129. +
    130. },
    131. +
    132. +
    133. /* Yield to the block, logging any errors at error level to all loggers,
    134. +
    135. * and all other queries with the duration at warn or info level.
    136. +
    137. * */
    138. +
    139. __logAndExecute:function (sql, args, cb) {
    140. +
    141. 8101 if (isFunction(args)) {
    142. +
    143. 8100 cb = args;
    144. +
    145. 8100 args = null;
    146. +
    147. }
    148. +
    149. +
    150. 8101 if (args) {
    151. +
    152. 0 sql = format("%s; %j", sql, args);
    153. +
    154. }
    155. +
    156. 8101 sql = sql.trim();
    157. +
    158. 8101 var start = new Date();
    159. +
    160. 8101 var ret = new Promise();
    161. +
    162. 8101 if (isFunction(cb)) {
    163. +
    164. 8100 this.logInfo("Executing; %s", sql);
    165. +
    166. 8100 cb().then(hitch(this, function () {
    167. +
    168. 8026 this.logDebug("Duration: % 6dms; %s", new Date() - start, sql);
    169. +
    170. 8026 ret.callback.apply(ret, arguments);
    171. +
    172. }), hitch(this, function (err) {
    173. +
    174. 74 err = new QueryError(format("%s: %s", err.message, sql));
    175. +
    176. 74 this.logError(err);
    177. +
    178. 74 ret.errback.apply(ret, [err]);
    179. +
    180. }));
    181. +
    182. } else {
    183. +
    184. 1 throw new QueryError("CB is required");
    185. +
    186. }
    187. +
    188. 8100 return ret.promise();
    189. +
    190. },
    191. +
    192. +
    193. /*Log the given SQL and then execute it on the connection, used by
    194. +
    195. *the transaction code.
    196. +
    197. * */
    198. +
    199. __logConnectionExecute:function (conn, sql) {
    200. +
    201. 2079 return this.__logAndExecute(sql, hitch(this, function () {
    202. +
    203. 2079 return conn[this.connectionExecuteMethod](sql);
    204. +
    205. }));
    206. +
    207. },
    208. +
    209. +
    210. +
    211. getters:{
    212. +
    213. /**@lends patio.Database.prototype*/
    214. +
    215. /**
    216. +
    217. * The "patio.Database" logger.
    218. +
    219. * @field
    220. +
    221. */
    222. +
    223. logger:function () {
    224. +
    225. 2 return LOGGER;
    226. +
    227. }
    228. +
    229. }
    230. +
    231. },
    232. +
    233. +
    234. "static":{
    235. +
    236. /**@lends patio.Database*/
    237. +
    238. /**
    239. +
    240. * Logs an INFO level message to the "patio.Database" logger.
    241. +
    242. */
    243. +
    244. logInfo:function () {
    245. +
    246. 1 if (LOGGER.isInfo) {
    247. +
    248. 1 LOGGER.info.apply(LOGGER, arguments);
    249. +
    250. }
    251. +
    252. },
    253. +
    254. +
    255. /**
    256. +
    257. * Logs a DEBUG level message to the "patio.Database" logger.
    258. +
    259. */
    260. +
    261. logDebug:function () {
    262. +
    263. 1 if (LOGGER.isDebug) {
    264. +
    265. 1 LOGGER.debug.apply(LOGGER, arguments);
    266. +
    267. }
    268. +
    269. },
    270. +
    271. +
    272. /**
    273. +
    274. * Logs a ERROR level message to the "patio.Database" logger.
    275. +
    276. */
    277. +
    278. logError:function () {
    279. +
    280. 1 if (LOGGER.isError) {
    281. +
    282. 1 LOGGER.error.apply(LOGGER, arguments);
    283. +
    284. }
    285. +
    286. },
    287. +
    288. +
    289. /**
    290. +
    291. * Logs a WARN level message to the "patio.Database" logger.
    292. +
    293. */
    294. +
    295. logWarn:function () {
    296. +
    297. 1 if (LOGGER.isWarn) {
    298. +
    299. 1 LOGGER.warn.apply(LOGGER, arguments);
    300. +
    301. }
    302. +
    303. },
    304. +
    305. +
    306. /**
    307. +
    308. * Logs a TRACE level message to the "patio.Database" logger.
    309. +
    310. */
    311. +
    312. logTrace:function () {
    313. +
    314. 1 if (LOGGER.isTrace) {
    315. +
    316. 1 LOGGER.trace.apply(LOGGER, arguments);
    317. +
    318. }
    319. +
    320. },
    321. +
    322. +
    323. /**
    324. +
    325. * Logs a FATAL level message to the "patio.Database" logger.
    326. +
    327. */
    328. +
    329. logFatal:function () {
    330. +
    331. 1 if (LOGGER.isFatal) {
    332. +
    333. 1 LOGGER.fatal.apply(LOGGER, arguments);
    334. +
    335. }
    336. +
    337. },
    338. +
    339. +
    340. getters:{
    341. +
    342. /**@lends patio.Database*/
    343. +
    344. /**
    345. +
    346. * The "patio.Database" logger.
    347. +
    348. * @field
    349. +
    350. */
    351. +
    352. logger:function () {
    353. +
    354. 1 return LOGGER;
    355. +
    356. }
    357. +
    358. }
    359. +
    360. }
    361. +
    362. +
    363. }).as(module);
    +
    +
    @@ -21126,7 +21167,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(){
  • -
  • 49107 return this.__quoteIdentifiers;
  • +
  • 48455 return this.__quoteIdentifiers;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21134,20 +21175,20 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(){
  • -
  • 15274 return this.__providesAccurateRowsMatched;
  • +
  • 14948 return this.__providesAccurateRowsMatched;
  • },
  • //Whether the dataset requires SQL standard datetimes (false by default,
  • // as most allow strings with ISO 8601 format).
  • /**@ignore*/
  • requiresSqlStandardDateTimes:function(){
  • -
  • 15281 return this.__requiresSqlStandardDateTimes;
  • +
  • 14955 return this.__requiresSqlStandardDateTimes;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(){
  • -
  • 15287 return this.__supportsCte;
  • +
  • 14961 return this.__supportsCte;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • @@ -21159,25 +21200,25 @@
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(){
  • -
  • 15310 return this.__supportsIntersectExcept;
  • +
  • 14984 return this.__supportsIntersectExcept;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(){
  • -
  • 15286 return this.__supportsIntersectExceptAll;
  • +
  • 14960 return this.__supportsIntersectExceptAll;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(){
  • -
  • 15543 return this.__supportsIsTrue;
  • +
  • 15217 return this.__supportsIsTrue;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(){
  • -
  • 15285 return this.__supportsJoinUsing;
  • +
  • 14959 return this.__supportsJoinUsing;
  • },
  • //Whether modifying joined datasets is supported.
  • @@ -21189,7 +21230,7 @@
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(){
  • -
  • 15278 return this.__supportsMultipleColumnIn;
  • +
  • 14952 return this.__supportsMultipleColumnIn;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • @@ -21201,13 +21242,13 @@
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(){
  • -
  • 15274 return this.__supportsTimestampUsecs;
  • +
  • 14948 return this.__supportsTimestampUsecs;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(){
  • -
  • 15274 return this.__supportsWindowFunctions;
  • +
  • 14948 return this.__supportsWindowFunctions;
  • }
  • },
  • @@ -21218,7 +21259,7 @@
  • // Whether this dataset quotes identifiers.
  • /**@ignore*/
  • quoteIdentifiers:function(val){
  • -
  • 15286 this.__quoteIdentifiers = val;
  • +
  • 14960 this.__quoteIdentifiers = val;
  • },
  • // Whether this dataset will provide accurate number of rows matched for
  • @@ -21226,20 +21267,20 @@
  • // rows matched by the dataset's filter.
  • /**@ignore*/
  • providesAccurateRowsMatched:function(val){
  • -
  • 15274 this.__providesAccurateRowsMatched = val;
  • +
  • 14948 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){
  • -
  • 15274 this.__requiresSqlStandardDateTimes = val;
  • +
  • 14948 this.__requiresSqlStandardDateTimes = val;
  • },
  • // Whether the dataset supports common table expressions (the WITH clause).
  • /**@ignore*/
  • supportsCte:function(val){
  • -
  • 15275 this.__supportsCte = val;
  • +
  • 14949 this.__supportsCte = val;
  • },
  • // Whether the dataset supports the DISTINCT ON clause, false by default.
  • @@ -21251,25 +21292,25 @@
  • //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExcept:function(val){
  • -
  • 15276 this.__supportsIntersectExcept = val;
  • +
  • 14950 this.__supportsIntersectExcept = val;
  • },
  • //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
  • /**@ignore*/
  • supportsIntersectExceptAll:function(val){
  • -
  • 15276 this.__supportsIntersectExceptAll = val;
  • +
  • 14950 this.__supportsIntersectExceptAll = val;
  • },
  • //Whether the dataset supports the IS TRUE syntax.
  • /**@ignore*/
  • supportsIsTrue:function(val){
  • -
  • 15274 this.__supportsIsTrue = val;
  • +
  • 14948 this.__supportsIsTrue = val;
  • },
  • //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
  • /**@ignore*/
  • supportsJoinUsing:function(val){
  • -
  • 15276 this.__supportsJoinUsing = val;
  • +
  • 14950 this.__supportsJoinUsing = val;
  • },
  • //Whether modifying joined datasets is supported.
  • @@ -21281,7 +21322,7 @@
  • //Whether the IN/NOT IN operators support multiple columns when an
  • /**@ignore*/
  • supportsMultipleColumnIn:function(val){
  • -
  • 15274 this.__supportsMultipleColumnIn = val;
  • +
  • 14948 this.__supportsMultipleColumnIn = val;
  • },
  • //Whether the dataset supports timezones in literal timestamps
  • @@ -21293,13 +21334,13 @@
  • //Whether the dataset supports fractional seconds in literal timestamps
  • /**@ignore*/
  • supportsTimestampUsecs:function(val){
  • -
  • 15274 this.__supportsTimestampUsecs = val;
  • +
  • 14948 this.__supportsTimestampUsecs = val;
  • },
  • //Whether the dataset supports window functions.
  • /**@ignore*/
  • supportsWindowFunctions:function(val){
  • -
  • 15274 this.__supportsWindowFunctions = val;
  • +
  • 14948 this.__supportsWindowFunctions = val;
  • }
  • }
  • @@ -21393,8 +21434,8 @@
  • * @ignore
  • */
  • constructor:function () {
  • -
  • 29542 !Dataset && (Dataset = require("../index").Dataset);
  • -
  • 29542 this._super(arguments);
  • +
  • 29216 !Dataset && (Dataset = require("../index").Dataset);
  • +
  • 29216 this._super(arguments);
  • },
  • /**
  • From 84004ce9f9ee9bf7e750fd26b4f97185a37335a2 Mon Sep 17 00:00:00 2001 From: Doug Martin Date: Wed, 29 Aug 2012 11:49:12 -0500 Subject: [PATCH 3/3] v 0.1.1 * new patio.Dataset features * #sourceList - get all sources as identifiers * #joinSourceList - get all join sources * #hasSelectSource - returns true if there are not any select sources (i.e select *) * #seleIfNoSource - add the selects if there is not currently a select * Fixed issue with patio.Model#_setFromDb where values not in the models table columns would be in accesible, (i.e a join with a model would not show the join columns) * updated docs --- History.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index ba6c1571..65b9bcec 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,11 @@ +#0.1.1 / 2012-08-29 +* new patio.Dataset features + * #sourceList - get all sources as identifiers + * #joinSourceList - get all join sources + * #hasSelectSource - returns true if there are not any select sources (i.e select *) + * #seleIfNoSource - add the selects if there is not currently a select +* Fixed issue with patio.Model#_setFromDb where values not in the models table columns would be in accesible, (i.e a join with a model would not show the join columns) +* updated docs #0.1.0 / 2012-08-25 * Added custom getters (mbenedettini) * Added Validator plugin for models diff --git a/package.json b/package.json index 71233b46..e7f6f0b9 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "patio", "description": "Patio query engine and ORM", - "version": "0.1.0", + "version": "0.1.1", "keywords": [ "ORM", "object relation mapper",