|
Overview
|
Coverage88.83
SLOC21817
LOC5183
Missed579
|
|
database/schemaGenerators.js
|
Coverage70.83
SLOC650
LOC96
Missed28
|
- 1
var comb = require("comb-proxy"),argsToArray = comb.argsToArray,merge = comb.merge,isFunction = comb.isFunction,isDefined = comb.isDefined,isHash = comb.isHash,isString = comb.isString,isArray = comb.isArray,toArray = comb.array.toArray,methodMissing = comb.methodMissing,define = comb.define;- 1
var Generator = define(null, {instance:{/**@lends patio.SchemaGenerator.prototype*/__primaryKey:null,/*** An internal class that the user is not expected to instantiate directly.* Instances are created by {@link patio.Database#createTable}.* It is used to specify table creation parameters. It takes a Database* object and a block of column/index/constraint specifications, and* gives the Database a table description, which the database uses to* create a table.** {@link patio.SchemaGenerator} has some methods but also includes method_missing,* allowing users to specify column type as a method instead of using* the column method, which makes for a cleaner code.* @constructs* @example*comb.executeInOrder(DB, function(DB){* DB.createTable("airplane_type", function () {* this.primaryKey("id");* this.name(String, {allowNull:false});* this.max_seats(Number, {size:3, allowNull:false});* this.company(String, {allowNull:false});* });* DB.createTable("airplane", function () {* this.primaryKey("id");* this.total_no_of_seats(Number, {size:3, allowNull:false});* this.foreignKey("typeId", "airplane_type", {key:"id"});* });* DB.createTable("flight_leg", function () {* this.primaryKey("id");* this.scheduled_departure_time("time");* this.scheduled_arrival_time("time");* this.foreignKey("departure_code", "airport", {key:"airport_code", type : String, size : 4});* this.foreignKey("arrival_code", "airport", {key:"airport_code", type : String, size : 4});* this.foreignKey("flight_id", "flight", {key:"id"});* });* DB.createTable("leg_instance", function () {* this.primaryKey("id");* this.date("date");* this.arr_time("datetime");* this.dep_time("datetime");* this.foreignKey("airplane_id", "airplane", {key:"id"});* this.foreignKey("flight_leg_id", "flight_leg", {key:"id"});* });*});* @param {patio.Database} the database this generator is for*/constructor:function (db) {- 218
this.db = db;- 218
this.columns = [];- 218
this.indexes = [];- 218
this.constraints = [];- 218
this.__primaryKey = null;},/*** Add an unnamed constraint to the DDL, specified by the given block* or args:** @example* db.createTable("test", function(){* this.check({num : {between : [1,5]}})* //=> CHECK num >= 1 AND num <= 5* this.check(function(){return this.num.gt(5);});* //=> CHECK num > 5* });**/check:function () {- 0
return this.constraint.apply(this, [null].concat(argsToArray(arguments)));},/*** Add a column with the given name, type, and opts to the DDL.** <pre class="code">* DB.createTable("test", function(){* this.column("num", "integer");* //=> num INTEGER* this.column('name", String, {allowNull : false, "default" : "a");* //=> name varchar(255) NOT NULL DEFAULT 'a'* this.column("ip", "inet");* //=> ip inet* });* </pre>** You can also create columns via method missing, so the following are* equivalent:* <pre class="code">* DB.createTable("test", function(){* this.column("number", "integer");* this.number("integer");* });* </pre>** @param {String|patio.sql.Identifier} name the name of the column* @param type the datatype of the column.* @param {Object} [opts] additional options** @param [opts.default] The default value for the column.* @param [opts.deferrable] This ensure Referential Integrity will work even if* reference table will use for its foreign key a value that does not* exists(yet) on referenced table. Basically it adds* DEFERRABLE INITIALLY DEFERRED on key creation.* @param {Boolean} [opts.index] Create an index on this column.* @param {String|patio.sql.Identifier} [key] For foreign key columns, the column in the associated table* that this column references. Unnecessary if this column references the primary key of the* associated table.* @param {Boolean} [opts.allowNull] Mark the column as allowing NULL values (if true),* or not allowing NULL values (if false). If unspecified, will default* to whatever the database default is.* @param {String} [opts.onDelete] Specify the behavior of this column when being deleted* ("restrict", "cascade", "setNull", "setDefault", "noAction").* @param {String} [opts.onUpdate] Specify the behavior of this column when being updated* Valid options ("restrict", "cascade", "setNull", "setDefault", "noAction").* @param {Boolean} [opts.primaryKey] Make the column as a single primary key column. This should only* be used if you have a single, non-autoincrementing primary key column.* @param {Number} [opts.size] The size of the column, generally used with string* columns to specify the maximum number of characters the column will hold.* An array of two integers can be provided to set the size and the* precision, respectively, of decimal columns.* @param {String} [opts.unique] Mark the column as unique, generally has the same effect as* creating a unique index on the column.* @param {String} [opts.unsigned] Make the column type unsigned, only useful for integer* columns.* @param {Array} [opts.elements] Available items used for set and enum columns.***/column:function (name, type, opts) {- 549
opts = opts || {};- 549
this.columns.push(merge({name:name, type:type}, opts));- 549
if (opts.index) {- 1
this.index(name);}},/*** Adds a named constraint (or unnamed if name is nil) to the DDL,* with the given block or args.* @example* DB.createTable("test", function(){* this.constraint("blah", {num : {between : [1,5])* //=> CONSTRAINT blah CHECK num >= 1 AND num <= 5* this.check("foo", function(){* return this.num.gt(5);* }); # CONSTRAINT foo CHECK num > 5* });* @param {String|patio.sql.Identifier} name the name of the constraint* @param {...} args variable number of arguments to create the constraint filter.* See {@link patio.Dataset#filter} for valid filter arguments.* */constraint:function (name, args) {- 0
args = argsToArray(arguments).slice(1);- 0
var block = isFunction(args[args.length - 1]) ? args.pop : null;- 0
this.constraints.push({name:name, type:"check", check:block || args});},/*** Add a foreign key in the table that references another table to the DDL. See {@link patio.SchemaGenerator#column{* for options.** @example* DB.createTable("flight_leg", function () {* this.primaryKey("id");* this.scheduled_departure_time("time");* this.scheduled_arrival_time("time");* this.foreignKey("departure_code", "airport", {key:"airport_code", type : String, size : 4});* this.foreignKey("arrival_code", "airport", {key:"airport_code", type : String, size : 4});* this.foreignKey("flight_id", "flight", {key:"id"});* });**/foreignKey:function (name, table, opts) {- 37
opts = opts || {};- 37
opts = isHash(table) ? merge({}, table, opts) : isString(table) ? merge({table:table}, opts) : opts;- 37
if (isArray(name)) {- 5
return this.__compositeForeignKey(name, opts);} else {- 32
return this.column(name, "integer", opts);}},/***Add a full text index on the given columns to the DDL.** @example* DB.createTable("posts", function () {* this.title("text");* this.body("text");* this.fullTextIndex("title");* this.fullTextIndex(["title", "body"]);* });*/fullTextIndex:function (columns, opts) {- 4
opts = opts || {};- 4
return this.index(columns, merge({type:"fullText"}, opts));},/**** Check if the DDL includes the creation of a column with the given name.* @return {Boolean} true if the DDL includes the creation of a column with the given name.*/hasColumn:function (name) {- 70
return this.columns.some(function (c) {- 310
return c.name == name});},/*** Add an index on the given column(s) with the given options to the DDL.* The available options are:* @example* DB.createTable("test", function(table) {* table.primaryKey("id", "integer", {null : false});* table.column("name", "text");* table.index("name", {unique : true});* });** @param columns the column/n to create the index from.* @param {Object} [opts] Additional options* @param {String} [opts.type] The type of index to use (only supported by some databases)* @param {Boolean} [opts.unique] :: Make the index unique, so duplicate values are not allowed.* @param [opts.where] :: Create a partial index (only supported by some databases)**/index:function (columns, opts) {- 17
this.indexes.push(merge({columns:toArray(columns)}, opts || {}));},/*** Adds an auto-incrementing primary key column or a primary key constraint to the DDL.* To create a constraint, the first argument should be an array of columns* specifying the primary key columns. To create an auto-incrementing primary key* column, a single column can be used. In both cases, an options hash can be used* as the second argument.** If you want to create a primary key column that is not auto-incrementing, you* should not use this method. Instead, you should use the regular {@link patio.SchemaGenerator#column}* method with a {primaryKey : true} option.** @example* db.createTable("airplane_type", function () {* this.primaryKey("id");* //=> id integer NOT NULL PRIMARY KEY AUTOINCREMENT* this.name(String, {allowNull:false});* this.max_seats(Number, {size:3, allowNull:false});* this.company(String, {allowNull:false});* });* */primaryKey:function (name) {- 70
if (isArray(name)) {- 0
return this.__compositePrimaryKey.apply(this, arguments);} else {- 70
var args = argsToArray(arguments, 1), type;- 70
var opts = args.pop();- 70
this.__primaryKey = merge({}, this.db.serialPrimaryKeyOptions, {name:name}, opts);- 70
if (isDefined((type = args.pop()))) {- 3
merge(opts, {type:type});}- 70
merge(this.__primaryKey, opts);- 70
return this.__primaryKey;}},/*** Add a spatial index on the given columns to the DDL.*/spatialIndex:function (columns, opts) {- 2
opts = opts || {};- 2
return this.index(columns, merge({type:"spatial"}, opts));},/*** Add a unique constraint on the given columns to the DDL. See {@link patio.SchemaGenerator#constraint}* for argument types.* @example* DB.createTable("test", function(){* this.unique("name");* //=> UNIQUE (name)* });* */unique:function (columns, opts) {- 0
opts = opts || {};- 0
this.constraints.push(merge({type:"unique", columns:toArray(columns)}, opts));},/*** @private* Add a composite primary key constraint*/__compositePrimaryKey:function (columns) {- 0
var args = argsToArray(arguments, 1);- 0
var opts = args.pop() || {};- 0
this.constraints.push(merge({type:"primaryKey", columns:columns}, opts));},/*** @private* Add a composite foreign key constraint*/__compositeForeignKey:function (columns, opts) {- 5
this.constraints.push(merge({type:"foreignKey", columns:columns}, opts));},/**@ignore*/getters:{// The name of the primary key for this generator, if it has a primary key.primaryKeyName:function () {- 70
return this.__primaryKey ? this.__primaryKey.name : null;}}}});- 1
exports.SchemaGenerator = function (db, block) {- 218
var gen = new Generator(db);- 218
var prox = methodMissing(gen, function (name) {- 431
return function (type, opts) {- 431
name = name || null;- 431
opts = opts || {};- 431
if (name) {- 431
return this.column(name, type, opts);} else {- 0
throw new TypeError();}}}, Generator);- 218
block.apply(prox, [prox]);- 218
gen.columns = prox.columns;- 218
if (gen.__primaryKey && !gen.hasColumn(gen.primaryKeyName)) {- 70
gen.columns.unshift(gen.__primaryKey);}- 218
return gen;}- 1
var AlterTableGenerator = define(null, {instance:{/**@lends patio.AlterTableGenerator.prototype*//*** An internal class that the user is not expected to instantiate directly.* Instances are created by {@link patio.Database#alterTable}.* It is used to specify table alteration parameters. It takes a Database* object and a function which is called in the scope of the {@link patio.AlterTableGenerator}* to perform on the table, and gives the Database an array of table altering operations,* which the database uses to alter a table's description.** @example* DB.alterTable("xyz", function() {* this.addColumn("aaa", "text", {null : false, unique : true});* this.dropColumn("bbb");* this.renameColumn("ccc", "ddd");* this.setColumnType("eee", "integer");* this.setColumnDefault("hhh", 'abcd');* this.addIndex("fff", {unique : true});* this.dropIndex("ggg");* });** //or using the passed in generator* DB.alterTable("xyz", function(table) {* table.addColumn("aaa", "text", {null : false, unique : true});* table.dropColumn("bbb");* table.renameColumn("ccc", "ddd");* table.setColumnType("eee", "integer");* table.setColumnDefault("hhh", 'abcd');* table.addIndex("fff", {unique : true});* table.dropIndex("ggg");* });* @constructs** @param {patio.Database} db the database object which is performing the alter table operation.* @param {Function} block a block which performs the operations. The block is called in the scope* of the {@link patio.AlterTableGenerator} and is passed an instance of {@link patio.AlterTableGenerator}* as the first argument.*/constructor:function (db, block) {- 80
this.db = db;- 80
this.operations = [];- 80
block.apply(this, [this]);},/*** Add a column with the given name, type, and opts to the DDL for the table.* See {@link patio.SchemaGenerator#column} for the available options.** @example* DB.alterTable("test", function(){* this.addColumn("name", String);* //=> ADD COLUMN name varchar(255)* });**/addColumn:function (name, type, opts) {- 13
opts = opts || {};- 13
this.operations.push(merge({op:"addColumn", name:name, type:type}, opts));},/*** Add a constraint with the given name and args to the DDL for the table.* See {@link patio.SchemaGenerator#constraint}.** @example* var sql = patio.sql;* DB.alterTable("test", function(){* this.addConstraint("valid_name", sql.name.like('A%'));* //=>ADD CONSTRAINT valid_name CHECK (name LIKE 'A%')* });* */addConstraint:function (name) {- 0
var args = argsToArray(arguments);- 0
var block = isFunction(args[args.length - 1]) ? args.pop() : null;- 0
this.operations.push({op:"addConstraint", name:name, type:"check", check:block || args});},/*** Add a unique constraint to the given column(s).* See {@link patio.SchemaGenerator#constraint}.* @example* DB.alterTable("test", function(){* this.addUniqueConstraint("name");* //=> ADD UNIQUE (name)* this.addUniqueConstraint("name", {name : "uniqueName});* //=> ADD CONSTRAINT uniqueName UNIQUE (name)* });**/addUniqueConstraint:function (columns, opts) {- 0
opts = opts || {};- 0
this.operations.push(merge({op:"addConstraint", type:"unique", columns:toArray(columns)}, opts));},/*** Add a foreign key with the given name and referencing the given table* to the DDL for the table. See {@link patio.SchemaGenerator#column}* for the available options.** You can also pass an array of column names for creating composite foreign* keys. In this case, it will assume the columns exists and will only add* the constraint.** NOTE: If you need to add a foreign key constraint to a single existing column* use the composite key syntax even if it is only one column.* @example* DB.alterTable("albums", function(){* this.addForeignKey("artist_id", "table");* //=>ADD COLUMN artist_id integer REFERENCES table* this.addForeignKey(["name"], "table")* //=>ADD FOREIGN KEY (name) REFERENCES table* });*/addForeignKey:function (name, table, opts) {- 3
opts = opts;- 3
if (isArray(name)) {- 1
return this.__addCompositeForeignKey(name, table, opts);} else {- 2
return this.addColumn(name, "integer", merge({table:table}, opts));}},/*** Add a full text index on the given columns to the DDL for the table.* See @{link patio.SchemaGenerator#index} for available options.*/addFullTextIndex:function (columns, opts) {- 0
opts = opts || {};- 0
return this.addIndex(columns, merge({type:"fullText"}, opts));},/*** Add an index on the given columns to the DDL for the table. See* {@link patio.SchemaGenerator#index} for available options.* @example* DB.alterTable("table", function(){* this.addIndex("artist_id");* //=> CREATE INDEX table_artist_id_index ON table (artist_id)* });*/addIndex:function (columns, opts) {- 5
opts = opts || {};- 5
this.operations.push(merge({op:"addIndex", columns:toArray(columns)}, opts));},/*** Add a primary key to the DDL for the table. See {@link patio.SchemaGenerator#column}* for the available options. Like {@link patio.ALterTableGenerator#addForeignKey}, if you specify* the column name as an array, it just creates a constraint:** @example* DB.alterTable("albums", function(){* this.addPrimaryKey("id");* //=> ADD COLUMN id serial PRIMARY KEY* this.addPrimaryKey(["artist_id", "name"])* //=>ADD PRIMARY KEY (artist_id, name)* });*/addPrimaryKey:function (name, opts) {- 0
opts = opts || {};- 0
if (isArray(name)) {- 0
return this.__addCompositePrimaryKey(name, opts);} else {- 0
opts = merge({}, this.db.serialPrimaryKeyOptions, opts);- 0
delete opts.type;- 0
return this.addColumn(name, "integer", opts);}},/*** Add a spatial index on the given columns to the DDL for the table.* See {@link patio.SchemaGenerator#index} for available options.* */addSpatialIndex:function (columns, opts) {- 0
opts = opts || {};- 0
this.addIndex(columns, merge({}, {type:"spatial"}, opts))},/*** Remove a column from the DDL for the table.** @example* DB.alterTable("albums", function(){* this.dropColumn("artist_id");* //=>DROP COLUMN artist_id* });** @param {String|patio.sql.Identifier} name the name of the column to drop.*/dropColumn:function (name) {- 4
this.operations.push({op:"dropColumn", name:name});},/*** Remove a constraint from the DDL for the table.* @example* DB.alterTable("test", function(){* this.dropConstraint("constraint_name");* //=>DROP CONSTRAINT constraint_name* });* @param {String|patio.sql.Identifier} name the name of the constraint to drop.*/dropConstraint:function (name) {- 0
this.operations.push({op:"dropConstraint", name:name});},/*** Remove an index from the DDL for the table.** @example* DB.alterTable("albums", function(){* this.dropIndex("artist_id")* //=>DROP INDEX table_artist_id_index* this.dropIndex(["a", "b"])* //=>DROP INDEX table_a_b_index* this.dropIndex(["a", "b"], {name : "foo"})* //=>DROP INDEX foo* });*/dropIndex:function (columns, opts) {- 2
opts = opts || {};- 2
this.operations.push(merge({op:"dropIndex", columns:toArray(columns)}, opts));},/*** Modify a column's name in the DDL for the table.** @example* DB.alterTable("artist", function(){* this.renameColumn("name", "artistName");* //=> RENAME COLUMN name TO artist_name* });*/renameColumn:function (name, newName, opts) {- 51
opts = opts || {};- 51
this.operations.push(merge({op:"renameColumn", name:name, newName:newName}, opts));},/*** Modify a column's default value in the DDL for the table.* @example* DB.alterTable("artist", function(){* //=>this.setColumnDefault("artist_name", "a");* //=> ALTER COLUMN artist_name SET DEFAULT 'a'* });* */setColumnDefault:function (name, def) {- 5
this.operations.push({op:"setColumnDefault", name:name, "default":def});},/*** Modify a column's type in the DDL for the table.** @example* DB.alterTable("artist", function(){* this.setColumnType("artist_name", 'char(10)');* //=> ALTER COLUMN artist_name TYPE char(10)* });*/setColumnType:function (name, type, opts) {- 7
opts = opts || {}- 7
this.operations.push(merge({op:"setColumnType", name:name, type:type}, opts));},/*** Modify a column's NOT NULL constraint.* @example* DB.alterTable("artist", function(){* this.setColumnAllowNull("artist_name", false);* //=> ALTER COLUMN artist_name SET NOT NULL* });**/setAllowNull:function (name, allowNull) {- 1
this.operations.push({op:"setColumnNull", name:name, "null":allowNull});},/*** @private* Add a composite primary key constraint**/__addCompositePrimaryKey:function (columns, opts) {- 0
this.operations.push(merge({op:"addConstraint", type:"primaryKey", columns:columns}, opts));},/*** @private* Add a composite foreign key constraint* */__addCompositeForeignKey:function (columns, table, opts) {- 1
this.operations.push(merge({op:"addConstraint", type:"foreignKey", columns:columns, table:table}, opts));}}}).as(exports, "AlterTableGenerator");
|
index.js
|
Coverage71.74
SLOC1081
LOC92
Missed26
|
/*** @projectName patio* @github https://github.com/C2FO/patio* @includeDoc [Connecting] ../docs-md/connecting.md* @includeDoc [Models] ../docs-md/models.md* @includeDoc [Associations] ../docs-md/associations.md* @includeDoc [Model Inheritance] ../docs-md/model-inheritance.md* @includeDoc [Model Validation] ../docs-md/validation.md* @includeDoc [Model Plugins] ../docs-md/plugins.md* @includeDoc [Querying] ../docs-md/querying.md* @includeDoc [DDL] ../docs-md/DDL.md* @includeDoc [Migrations] ../docs-md/migrations.md* @includeDoc [Logging] ../docs-md/logging.md* @includeDoc [Change Log] ../History.md* @includeDoc [Test Coverage] [../docs-md/coverage.html]** @header* [](http://travis-ci.org/C2FO/patio)** Patio is a <a href="http://sequel.rubyforge.org/" target="patioapi">Sequel</a> inspired query engine.** <h3>Why Use Patio?</h3>** <p>* Patio is different because it allows the developers to choose the level of abtraction they are comfortable with.* </p>* <p>* If you want to use <a href="./models.html">ORM</a> functionality you can. If you dont you can just use the* <a href="./DDL.html">Database</a> and <a href="./querying.html">Datasets</a> as a querying API, and if you need to* you can <a href="./patio_Database.html#run">write plain SQL</a>.* </p>*** ###Getting Started** To install patio run** `npm install comb patio`** If you want to use the patio executable for migrations** `npm install -g patio`*** Create some tables.** ```* var patio = require("patio"),* comb = require("comb"),* when = comb.when,* serial = comb.serial;*** //set all db name to camelize* patio.camelize = true;* patio.configureLogging();* //connect to the db* var DB = patio.connect(<CONNECTION_URI>);** function errorHandler(error) {* console.log(error);* patio.disconnect();* };** function createTables() {* return comb.serial([* function () {* return DB.forceDropTable(["capital", "state"]);* },* function () {* return DB.createTable("state", function () {* this.primaryKey("id");* this.name(String)* this.population("integer");* this.founded(Date);* this.climate(String);* this.description("text");* });* },* function () {* return DB.createTable("capital", function () {* this.primaryKey("id");* this.population("integer");* this.name(String);* this.founded(Date);* this.foreignKey("stateId", "state", {key:"id"});* });* }* ]);* };** createTables().then(function () {* patio.disconnect();* }, errorHandler);* ```** Next lets create some models for the tables created.** ```* var State = patio.addModel("state").oneToOne("capital");* var Capital = patio.addModel("capital").manyToOne("state");* ```** Next you'll need to sync your models. **Note**: The sync operation returns a promise.** ```* patio.syncModels();* ```** Save some data to query.** ```* //comb.when waits for the save operation to complete* return comb.when(* State.save({* name:"Nebraska",* population:1796619,* founded:new Date(1867, 2, 4),* climate:"continental",* capital:{* name:"Lincoln",* founded:new Date(1856, 0, 1),* population:258379* }* }),* Capital.save({* name:"Austin",* founded:new Date(1835, 0, 1),* population:790390,* state:{* name:"Texas",* population:25674681,* founded:new Date(1845, 11, 29)* }* })* );* ```** Now we can query the states and capitals we created.** ```* State.order("name").forEach(function (state) {* //if you return a promise here it will prevent the foreach from* //resolving until all inner processing has finished.* return state.capital.then(function (capital) {* console.log("%s's capital is %s.", state.name, capital.name);* });* });* ```** ```* Capital.order("name").forEach(function (capital) {* //if you return a promise here it will prevent the foreach from* //resolving until all inner processing has finished.* return capital.state.then(function (state) {* console.log(comb.string.format("%s is the capital of %s.", capital.name, state.name));* });* });* ```*/- 1
var Dataset = require("./dataset"),Database = require("./database"),adapters = require("./adapters"),EventEmitter = require("events").EventEmitter,PatioError = require("./errors").PatioError,migrate = require("./migration"),model = require("./model"),Model = model.Model,plugins = require("./plugins"),comb = require("comb-proxy"),Time = require("./time"),date = comb.date,SQL = require("./sql").sql,hitch = comb.hitch,Promise = comb.Promise,PromiseList = comb.PromiseList,singleton = comb.singleton,isFunction = comb.isFunction,executeInOrder = comb.executeInOrder,argsToArray = comb.argsToArray,isString = comb.isString;- 1
var LOGGER = comb.logger("patio");- 1
var Patio = singleton([EventEmitter, Time], {instance:{/*** @lends patio.prototype*/__camelize:false,__underscore:false,__inImportOfModels:false,/*** A singleton class that acts as the entry point for all actions performed in patio.** @example** var patio = require("patio");** patio.createConnection(....);** patio.camelize = true;* patio.quoteIdentifiers=false;** patio.createModel("my_table");*** //CHANGING IDENTIFIER INPUT METHOD*** //use whatever is passed in* patio.identifierInputMethod = null;* //convert to uppercase* patio.identifierInputMethod = "toUpperCase";* //convert to camelCase* patio.identifierInputMethod = "camelize";* //convert to underscore* patio.identifierInputMethod = "underscore";*** //CHANGING IDENTIFIER OUTPUT METHOD** //use whatever the db returns* patio.identifierOutputMethod = null;* //convert to uppercase* patio.identifierOutputMethod = "toUpperCase";* //convert to camelCase* patio.identifierOutputMethod = "camelize";* //convert to underscore* patio.identifierOutputMethod = "underscore";** //TURN QUOTING OFF* patio.quoteIdentifiers = false** @constructs* @augments patio.Time* @param options*/constructor:function () {- 1
this._super(arguments);- 1
var constants = SQL.Constants;- 1
for (var i in constants) {- 9
this[i] = constants[i];}},/*** Returns a {@link patio.Database} object that can be used to for querying.** <p>This method is the entry point for all interactions with a database including getting* {@link patio.Dataset}s for creating queries(see {@link patio.Database#from}).* </p>** <p>The {@link patio.Database} returned can also be used to create({@link patio.Database#createTable}),* alter(@link patio.Database#alterTable}), rename({@link patio.Database#renameTable}), and* drop({@link patio.Database#dropTable}) as well as many other {@link patio.Database} actions.* </p>** @example** //connect using an object* var DB = patio.createConnection({* host : "127.0.0.1",* port : 3306,* type : "mysql",* maxConnections : 1,* minConnections : 1,* user : "test",* password : "testpass",* database : 'test'* });* //connect using a connection string* var CONNECT_STRING = "mysql://test:testpass@localhost:3306/test?maxConnections=1&minConnections=1";* var DB = patio.createConnection(CONNECT_STRING);** //...do something* DB.createTable("myTable", function(){* this.name("text");* this.value("integer");* }).then(function(){* //tables created!!!* });** @param {String|Object} options the options used to initialize the database connection.* This may be a database connetion string or object.* @param {Number} [options.maxConnections = 10] the number of connections to pool.* @param {Number} [options.minConnections = 3] the number of connections to pool.* @param {String} [options.type = "mysql"] the type of database to communicate with.* @param {String} options.user the user to authenticate as.* @param {String} options.password the password of the user.* @param {String} options.database the name of the database to use, the database* specified here is the default database for all connections.*/createConnection:function (options) {- 41
var ret = Database.connect(options);- 41
this.emit("connect", ret);- 41
return ret;},/*** @see patio#createConnection*/connect:function () {- 13
return this.createConnection.apply(this, arguments);},/*** This method allows one to connect to a database and immediately execute code.* For connection options @see patio#createConnection** @example*** var DB;* var CONNECT_STRING = "dummyDB://test:testpass@localhost/dummySchema";* var connectPromise = patio.connectAndExecute(CONNECT_STRING, function (db) {* db.dropTable("test");* db.createTable("test", function () {* this.primaryKey("id");* this.name(String);* this.age(Number);* });* });** connectPromise.then(function (db) {* DB = db;* //do more stuff!* });** @param {String|Object} options @see patio#createConnection* @param {Function} cb the function to callback once connected.** @returns {comb.Promise} a promise that is resolved once the database execution has finished.*/connectAndExecute:function (options, cb) {- 27
if (!isFunction(cb)) {- 0
throw new PatioError("callback must be a function");}- 27
var db = this.createConnection.apply(this, arguments);- 27
return executeInOrder(db, patio, function (db, patio) {- 27
cb(db, patio);- 27
return db;});},/*** Disconnects all databases in use.** @param {Function} [cb=null] a callback to call when disconnect has completed* @return {comb.Promise} a promise that is resolved once all databases have disconnected.*/disconnect:function (cb) {- 41
var ret = Database.disconnect(cb);- 41
ret.classic(hitch(this, function (err) {- 41
if (err) {- 0
this.emit("error", err);} else {- 41
this.emit("disconnect");}}));- 41
return ret.promise();},/*** Allows for the importing of multiple models so you do not have to worry about the promise that is returned from create model,* or a directory if an index.js file is used.** <p>* To import a group of model files you can do the following:* {@code* patio.import(__dirname + "/models/Flight.js",* __dirname + "/models/Airport.js",* __dirname + "/models/Airplane.js").then(function(Flight, Airport, Airplane){* //...* });** patio.import(__dirname + "/models/Flight.js",* __dirname + "/models/Airport.js",* __dirname + "/models/Airplane.js").then(function(){* var Flight = patio.getModel("flight"),* Airport = patio.getModel("airport"),* Airplane = patio.getModel("airplane");* });* </pre>* Another approach is to create an index.js file inside of a directory that requires all of the models needed.* then use {@link patio#import} on that file.* <pre class="code">* patio.import(__dirname + "/models").then(function(){* var Flight = patio.getModel("flight"),* Airport = patio.getModel("airport"),* Airplane = patio.getModel("airplane");* });* }* </p>** @param files a single or list of files to import.* @param {Function} [cb] an optional callback to call when importing is complete** @return {Promise} returns a promise that will be resolved when all models in the imported files* have been loaded.*/"import":function (files, cb) {- 0
files = argsToArray(arguments);- 0
if (isFunction(files[files.length])) {- 0
cb = files.pop();} else {- 0
cb = null;}- 0
files.map(function (file) {- 0
require(file);});- 0
return model.syncModels(cb).promise();},/*** This method is used to create a {@link patio.Model} object.** @example* var Flight = patio.addModel("flight", {* instance:{* toObject:function () {* var obj = this._super(arguments);* obj.weekdays = this.weekdaysArray;* obj.legs = this.legs.map(function (l) {* return l.toObject();* });* return obj;* },** _setWeekdays:function (weekdays) {* this.weekdaysArray = weekdays.split(",");* return weekdays;* }* },** static:{** init:function () {* this.oneToMany("legs", {* model:"flightLeg",* orderBy:"scheduledDepartureTime",* fetchType:this.fetchType.EAGER* });* },** byAirline:function (airline) {* return this.filter({airline:airline}).all();* },** arrivesAt:function (airportCode) {* return this.join(this.flightLeg.select("flightId").filter({arrivalCode:airportCode}).distinct(), {flightId:"id"}).all();* },** departsFrom:function (airportCode) {* return this.join(this.flightLeg.select("flightId").filter({departureCode:airportCode}).distinct(), {flightId:"id"}).all();* },** getters:{* flightLeg:function () {* if (!this.__flightLeg) {* this.__flightLeg = this.patio.getModel("flightLeg");* }* return this.__flightLeg;* }* }* }* });*** @param {String|patio.Dataset} table the table to use as the base for the model.* @param {patio.Model|patio.Model[]} Parent models of this model.* See {@link patio.plugins.ClassTableInheritancePlugin}.* @param {Object} [proto] an object to be used as the prototype for the model. See* <a href="http://c2fo.github.com/comb/symbols/comb.html#.define">comb.define</a>.* @param [Object[]] [proto.plugins] this can be used to specify additional plugins to use such as.* <ul>* <li>{@link patio.plugins.TimeStampPlugin</li>* <li>{@link patio.plugins.CachePlugin</li>* </ul>****/addModel:function (table, supers, proto) {- 93
return model.create.apply(model, arguments);},/*** Returns a model from the name of the table for which the model was created.** {@code* var TestModel = patio.addModel("test_model").sync(function(err){* if(err){* console.log(err.stack);* }else{* var TestModel = patio.getModel("test_model");* }* });* }** If you have two tables with the same name in different databases then you can use the db parameter also.** {@code** var DB1 = patio.createConnection("mysql://test:testpass@localhost:3306/test_1");* var DB2 = patio.createConnection("mysql://test:testpass@localhost:3306/test_2");* var Test1 = patio.addModel(DB1.from("test");* var Test2 = patio.addModel(DB2.from("test");** //sync the models* patio.syncModels().then(function(){* //now you can use them* var test1Model = new Test1();* var test2Model = new Test2();* });* }*** @param {String} name the name of the table that the model represents.* @param {@patio.Database} [db] optional database in case you have two models with the same table names in* different databases.*/getModel:function (name, db) {- 83
return model.getModel(name, db);},/*** Helper method to sync all models at once.** @example** var User = patio.addModel("user");* var Blog = patio.addModel("blog");** //using promise api* patio.syncModels().then(function(){* var user = new User();* }, function(error){* console.log(err);* });** //using a callback** patio.syncModels(function(err){* if(err){* console.log(err);* }else{* var user = new User();* }* });** @param {Function} [cb] an optional callback to be invoked when all models have been synced* @return {comb.Promise} a promise that will be resolved when the models have been synced.*/syncModels:function (cb) {- 35
return model.syncModels(cb);},resetIdentifierMethods:function () {- 51
this.quoteIdentifiers = true;- 51
this.identifierOutputMethod = null;- 51
this.identifierInputMethod = null;- 51
Model.identifierOutputMethod = null;- 51
Model.identifierInputMethod = null;},/*** Migrates the database using migration files found in the supplied directory.* <br/>* <br/>* <div>* <h3>Integer Migrations</h3>* Integer migrations are the simpler of the two migrations but are less flexible than timestamp based migrations.* In order for patio to determine which versions to use the file names must end in <versionNumber>.js where* versionNumber is a integer value representing the version number. <b>NOTE:</b>With integer migrations* missing versions are not allowed.* <br/>* <br/>* An example directory structure might look like the following:** <pre class="code">* -migrations* - createFirstTables.0.js* - shortDescription.1.js* - another.2.js* .* .* .* -lastMigration.n.js* </pre>* In order to easily identify where certain schema alterations have taken place it is a good idea to provide a brief* but meaningful migration name.* <pre class="code">* createEmployee.0.js* alterEmployeeNameColumn.1.js* </pre>*</div>** <div>* <h3>Timestamp Migrations</h3>* Timestamp migrations are the more complex of the two migrations but offer greater flexibility especially* with development teams. This is because Timestamp migrations do not require consecutive version numbers,* ,allow for duplicate version numbers(but this should be avoided), keeps track of all currently applied migrations,* and it will merge missing migrations. In order for patio to determine the order of the migration files* the file names must end in <timestamp>.js where the timestamp can be any form of a time stamp.* <pre class="code">* //yyyyMMdd* 20110131* //yyyyMMddHHmmss* 20110131123940* //unix epoch timestamp* 1328035161* </pre>* as long as it is greater than 20000101 other wise it will be assumed to be part of an integer migration.* <br/>* <br/>* An example directory structure might look like the following:** <pre class="code">* -migrations* - createFirstTables.1328035161.js* - shortDescription.1328035360.js* - another.1328035376.js* .* .* .* -lastMigration.n.js* </pre>* In order to easily identify where certain schema alterations have taken place it is a good idea to provide a brief* but meaningful migration name.* <pre class="code">* createEmployee.1328035161.js* alterEmployeeNameColumn.1328035360.js* </pre>*</div>** <b>NOTE:</b>If you start with IntegerBased migrations and decide to transition to Timestamp migrations the* patio will attempt the migrate the current schema to the timestamp based migration schema.** <div>* In order to run a migraton all one has to do is call patio.migrate(DB, directory, options);** <pre class="code">* var DB = patio.connect("my://connection/string");* patio.migrate(DB, __dirname + "/migrations").then(function(){* console.log("migrations finished");* });* </pre>** <b>Example migration file</b>* <pre class="code">** //Up function used to migrate up a version* exports.up = function(db) {* //create a new table* db.createTable("company", function() {* this.primaryKey("id");* this.companyName(String, {size : 20, allowNull : false});* });* db.createTable("employee", function(table) {* this.primaryKey("id");* this.firstName(String);* this.lastName(String);* this.middleInitial("char", {size : 1});* });*};** //Down function used to migrate down version*exports.down = function(db) {* db.dropTable("employee", "company");*};* </pre>**</div>** @param {String|patio.Database} db the database or connection string to a database to migrate.* @param {String} directory directory that the migration files reside in* @param {Object} [opts={}] optional parameters.* @param {String} [opts.column] the column in the table that version information should be stored.* @param {String} [opts.table] the table that version information should be stored.* @param {Number} [opts.target] the target migration(i.e the migration to migrate up/down to).* @param {String} [opts.current] the version that the database is currently at if the current version* is not provided it is retrieved from the database.** @return {Promise} a promise that is resolved once the migration is complete.*/migrate:function (db) {- 31
db = isString(db) ? this.connect(db) : db;- 31
var args = argsToArray(arguments);- 31
args.splice(0, 1);- 31
return migrate.run.apply(migrate, [db].concat(args));},/*** This can be used to configure logging. If a options* hash is passed in then it will passed to the comb.logging.PropertyConfigurator.* If the options are omitted then a ConsoleAppender will be added and the level will* be set to info.** @example* var config = {* "patio" : {* level : "INFO",* appenders : [* {* type : "RollingFileAppender",* file : "/var/log/patio.log",* },* {* type : "RollingFileAppender",* file : "/var/log/patio-error.log",* name : "errorFileAppender",* level : "ERROR"* }* ]* };** patio.configureLogging(config);** @param opts*/configureLogging:function (opts) {- 0
comb.logger.configure(opts);- 0
if (!opts) {- 0
LOGGER.level = "info";}},/*** Logs an INFO level message to the "patio" logger.*/logInfo:function () {- 0
if (LOGGER.isInfo) {- 0
LOGGER.info.apply(LOGGER, arguments);}},/*** Logs a DEBUG level message to the "patio" logger.*/logDebug:function () {- 0
if (LOGGER.isDebug) {- 0
LOGGER.debug.apply(LOGGER, arguments);}},/*** Logs an ERROR level message to the "patio" logger.*/logError:function () {- 0
if (LOGGER.isError) {- 0
LOGGER.error.apply(LOGGER, arguments);}},/*** Logs a WARN level message to the "patio" logger.*/logWarn:function () {- 0
if (LOGGER.isWarn) {- 0
LOGGER.warn.apply(LOGGER, arguments);}},/*** Logs a TRACE level message to the "patio" logger.*/logTrace:function () {- 0
if (LOGGER.isTrace) {- 0
LOGGER.trace.apply(LOGGER, arguments);}},/*** Logs a FATAL level message to the "patio" logger.*/logFatal:function () {- 0
if (LOGGER.isFatal) {- 0
LOGGER.fatal.apply(LOGGER, arguments);}},/**@ignore*/getters:{/**@lends patio.prototype*//*** An array of databases that are currently connected.* @field* @type patio.Database[]* @default []*/DATABASES:function () {- 733
return Database.DATABASES;},/*** Returns the default database. This is the first database created using {@link patio#connect}.* @field* @type patio.Database* @default null*/defaultDatabase:function () {- 392
return this.DATABASES.length ? this.DATABASES[0] : null;},/**@ignore*/Database:function () {- 9
return Database;},/**@ignore*/Dataset:function () {- 57
return Dataset;},/**@ignore*/SQL:function () {- 25
return SQL;},/**@ignore*/sql:function () {- 8
return SQL;},/**@ignore*/plugins:function () {- 7
return plugins;},/**@ignore*/migrations:function () {- 0
return migrate;},/*** Returns the root comb logger using this logger you* can set the levels add appenders etc.** @type Logger* @field* @default comb.logger("patio")*/LOGGER:function () {- 0
return LOGGER;},/*** Returns the default method used to transform identifiers sent to the database.* See (@link patio.Database.identifierInputMethod}* @ignore* @field* @type String* @default Database.identifierInputMethod*/identifierInputMethod:function () {- 3
return Database.identifierInputMethod;},/*** Returns the default method used to transform identifiers returned from the database.* See (@link patio.Database.identifierOutputMethod}* @ignore* @field* @type String**/identifierOutputMethod:function () {- 3
return Database.identifierOutputMethod;},/*** @ignore* @type Boolean* Returns whether or not identifiers are quoted before being sent to the database.*/quoteIdentifiers:function (value) {- 1
return Database.quoteIdentifiers;},/**@ignore*/camelize:function () {- 1
return this.__camelize;},/**@ignore*/underscore:function () {- 1
return this.__underscore;}},/**@ignore*/setters:{/**@lends patio.prototype*//*** Set the method to call on identifiers going into the database. This affects* how identifiers are sent to the database. So if you use camelCased and the db identifiers are all underscored* use camelize. The method can include* <ul>* <li>toUpperCase</li>* <li>toLowerCase</li>* <li>camelize</li>* <li>underscore</li>* <li>Other String instance method names.</li>* </ul>** patio uses toUpperCase identifiers in all SQL strings for most databases.** @field* @type String* @ignoreCode* @example* //use whatever is passed in* patio.identifierInputMethod = null;* //convert to uppercase* patio.identifierInputMethod = "toUpperCase";* //convert to camelCase* patio.identifierInputMethod = "camelize";* //convert to underscore* patio.identifierInputMethod = "underscore";** */identifierInputMethod:function (value) {- 79
Database.identifierInputMethod = value;},/*** Set the method to call on identifiers coming out of the database. This affects* the how identifiers are represented by calling the method on them.* The method can include* <ul>* <li>toUpperCase</li>* <li>toLowerCase</li>* <li>camelize</li>* <li>underscore</li>* <li>Other String instance method names.</li>* </ul>* most database implementations in patio use toLowerCase* @ignoreCode* @field* @type String* @example* //use whatever the db returns* patio.identifierOutputMethod = null;* //convert to uppercase* patio.identifierOutputMethod = "toUpperCase";* //convert to camelCase* patio.identifierOutputMethod = "camelize";* //convert to underscore* patio.identifierOutputMethod = "underscore";** */identifierOutputMethod:function (value) {- 79
Database.identifierOutputMethod = value;},/*** Set whether to quote identifiers for all databases by default. By default,* patio quotes identifiers in all SQL strings.** @ignoreCode* @field* @type Boolean** @example* //Turn quoting off* patio.quoteIdentifiers = false* */quoteIdentifiers:function (value) {- 81
Database.quoteIdentifiers = value;},/*** Sets the whether or not to camelize identifiers coming from the database and to underscore* identifiers when sending identifiers to the database. Setting this property to true has the same effect* as:* <pre class="code">* patio.identifierOutputMethod = "camelize";* patio.identifierInputMethod = "underscore";* </pre>* @field* @ignoreCode* @example* patio.camelize = true;* patio.connectAndExecute("mysql://test:testpass@localhost:3306/airports", function (db) {* db.createTable("airport", function () {* this.primaryKey("id");* this.airportCode(String, {size:4, allowNull:false, unique:true});* this.name(String, {allowNull:false});* this.city(String, {allowNull:false});* this.state(String, {size:2, allowNull:false});* });* //=> CREATE TABLE `airport`(* // id integer PRIMARY KEY AUTO_INCREMENT,* // airport_code varchar(4) UNIQUE NOT NULL,* // name varchar(255) NOT NULL,* // city varchar(255) NOT NULL,* // state varchar(2) NOT NULL* //);* }):** @param {Boolean} camelize set to true to camelize all identifiers coming from the database and to* underscore all identifiers sent to the database.*/camelize:function (camelize) {- 21
camelize = camelize === true;- 21
Model.camelize = camelize;- 21
this.identifierOutputMethod = camelize ? "camelize" : "underscore";- 21
this.identifierInputMethod = camelize ? "underscore" : "camelize";- 21
this.__underscore = !camelize;- 21
this.__camelize = camelize;},/*** Sets the whether or not to underscore identifiers coming from the database and to camelize* identifiers when sending identifiers to the database. Setting this property to true has the same effect* as:* <pre class="code">* patio.identifierOutputMethod = "underscore";* patio.identifierInputMethod = "camelize";* </pre>*** @field* @ignoreCode* @example* patio.camelize = true;* patio.connectAndExecute("mysql://test:testpass@localhost:3306/airports", function (db) {* db.createTable("airport", function () {* this.primaryKey("id");* this.airport_code(String, {size:4, allowNull:false, unique:true});* this.name(String, {allowNull:false});* this.city(String, {allowNull:false});* this.state(String, {size:2, allowNull:false});* });* //=> CREATE TABLE `airport`(* // id integer PRIMARY KEY AUTO_INCREMENT,* // airportCode varchar(4) UNIQUE NOT NULL,* // name varchar(255) NOT NULL,* // city varchar(255) NOT NULL,* // state varchar(2) NOT NULL* //);* }):** @param {Boolean} camelize set to true to underscore all identifiers coming from the database and to* camelize all identifiers sent to the database.*/underscore:function (underscore) {- 1
underscore = underscore === true;- 1
Model.underscore = underscore;- 1
this.identifierOutputMethod = underscore ? "underscore" : "camelize";- 1
this.identifierInputMethod = underscore ? "camelize" : "underscore";- 1
this.__camelize = !underscore;- 1
this.__underscore = underscore;}}}});- 1
var patio = exports;- 1
module.exports = patio = new Patio();- 1
patio.__Patio = Patio;- 1
var adapters = Database.ADAPTERS;- 1
for (var i in adapters) {- 3
patio[i] = adapters[i];}
|
adapters/postgres.js
|
Coverage73.00
SLOC824
LOC300
Missed81
|
- 1
var pg = require("pg"),PgTypes = require("pg/lib/types"),StringDecoder = require("string_decoder").StringDecoder,comb = require("comb"),asyncArray = comb.async.array,string = comb.string,pad = string.pad,format = string.format,hitch = comb.hitch,when = comb.when,serial = comb.serial,array = comb.array,toArray = array.toArray,zip = array.zip,flatten = array.flatten,Promise = comb.Promise,isUndefinedOrNull = comb.isUndefinedOrNull,isString = comb.isString,isArray = comb.isArray,isEmpty = comb.isEmpty,isBoolean = comb.isBoolean,isObject = comb.isObject,isFunction = comb.isFunction,define = comb.define,merge = comb.merge,isDefined = comb.isDefined,isInstanceOf = comb.isInstanceOf,QueryError = require("../errors").QueryError,Dataset = require("../dataset"),Database = require("../database"),sql = require("../sql").sql,stringToIdentifier = sql.stringToIdentifier,DateTime = sql.DateTime,Time = sql.Time,Year = sql.Year,literal = sql.literal,StringExpression = sql.StringExpression,Double = sql.Double,identifier = sql.identifier,BooleanExpression = sql.BooleanExpression,LiteralString = sql.LiteralString,Subscript = sql.Subscript,patio;- 1
var getPatio = function () {- 42
return patio || (patio = require("../index.js"));};- 1
var isBlank = function (obj) {- 374
var ret = false;- 374
if (isUndefinedOrNull(obj)) {- 313
ret = true;- 61
} else if (isString(obj) || isArray(obj)) {- 61
ret = obj.length === 0;- 0
} else if (isBoolean(obj) && !obj) {- 0
ret = true;- 0
} else if (isObject(obj) && isEmpty(obj)) {- 0
ret = true;}- 374
return ret;};- 1
var byteaParser = function (val) {- 12
if (val.toString().indexOf("\\x") === 0) {- 12
val = val.toString().replace(/^\\x/, "");- 12
return new Buffer(val, "hex");} else {- 0
val = val.toString().replace(/\\([0-7]{3})/g,function (full_match, code) {- 0
return String.fromCharCode(parseInt(code, 8));}).replace(/\\\\/g, "\\");- 0
return new Buffer(val, "binary");}};- 1
PgTypes.setTypeParser(17, "text", byteaParser);- 1
var timestampOrig = PgTypes.getTypeParser(1114, "text");//PgTypes.setTypeParser(25, "text", byteaParser);- 1
PgTypes.setTypeParser(1114, "text", function (val) {- 38
val = String(val);- 38
if (!val.match(/\.(\d{0,3})/)) {- 0
val += ".000";} else {- 38
val = val.replace(/\.(\d{0,3})$/, function (m, m1) {- 38
return "." + pad(m1, 3, "0", true);});}- 38
return getPatio().stringToTimeStamp(val.toString(), DS.TIMESTAMP_FORMAT).date;});- 1
PgTypes.setTypeParser(1184, "text", function (val) {- 0
return getPatio().stringToDate(val.toString());});- 1
PgTypes.setTypeParser(1082, "text", function (val) {- 4
return getPatio().stringToDate(val.toString());});- 1
PgTypes.setTypeParser(1083, "text", function (val) {- 0
return getPatio().stringToTime(val.toString(), DS.TIME_FORMAT);});- 1
var Connection = define(null, {instance:{connection:null,constructor:function (conn) {- 64
this.connection = conn;},closeConnection:function () {- 64
this.connection.end();- 64
return new Promise().callback().promise();},query:function (query) {- 7582
var ret = new Promise();- 7582
try {- 7582
this.connection.setMaxListeners(0);- 7582
var fields = [];- 7582
var q = this.connection.query(query, hitch(this, function (err, results) {- 7582
q.handleRowDescription = orig;- 7582
if (err) {- 71
return ret.errback(err);} else {- 7511
return ret.callback(results.rows, fields);}}));- 7582
var orig = q.handleRowDescription;- 7582
q.handleRowDescription = function (msg) {- 4372
fields = msg.fields;- 4372
return orig.apply(q, arguments);};} catch (e) {- 0
patio.logError(e);}- 7582
return ret.promise();}}});- 1
var DS = define(Dataset, {instance:{complexExpressionSql:function (op, args) {- 5982
var ret = "";- 5982
if (op === "^") {- 0
var j = this._static.XOR_OP, c = false;- 0
args.forEach(function (a) {- 0
if (c) {- 0
ret += j;}- 0
ret += this.literal(a);- 0
c = true;}, true);} else {- 5982
return this._super(arguments);}- 0
return ret;},forShare:function () {- 0
return this.lockStyle("share");},fullTextSearch:function (cols, terms, opts) {- 3
opts = opts || {};- 3
var lang = opts.language || 'simple';- 3
if (Array.isArray(terms)) {- 1
terms = terms.join(' | ');}- 3
return this.filter("to_tsvector(?, ?) @@ to_tsquery(?, ?)", lang, this.__fullTextStringJoin(toArray(cols).map(function (c) {- 4
return stringToIdentifier(c);})), lang, terms);},/*** Lock all tables in the datasets from clause (but not in JOINs), in the specified mode. If* a function is passed in as the last argument* @para {String} mode the lock mode (e.g. 'EXCLUSIVE').* @param {Object} [opts] see {@link patio.Database#transaction} for options.* @param {Function} [cb] of provided then a new {@link patio.Database} transaction is started.**/lock:function (mode, opts, cb) {- 3
if (isFunction(opts)) {- 1
cb = opts;- 1
opts = null;} else {- 2
opts = opts || {};}- 3
if (isFunction(cb)) {- 1
return this.db.transaction(opts, function () {- 1
return serial([this.lock.bind(this, mode, opts),cb.bind(this)]);}.bind(this));} else {- 2
return this.db.execute(format(this._static.LOCK, [this._sourceList(this.__opts.from), mode]), opts);}},multiInsertSql:function (columns, values) {- 1
var ret = literal('VALUES ');- 1
ret += this.__expressionList(values.map(function (r) {- 2
return toArray(r);}));- 1
return [this.insertSql(columns.map(function (c) {- 2
return stringToIdentifier(c);}), literal(ret))];},_deleteFromSql:function () {- 930
var self = this._static, space = self.SPACE;- 930
return [space, self.FROM, space, this._sourceList(this.__opts.from[0])].join("");},_deleteUsingSql:function () {- 930
return this._joinFromSql("USING");},_joinFromSql:function (type) {- 1136
var from = this.__opts.from.slice(1), join = this.__opts.join, ret = "";- 1136
if (!from.length) {- 1136
if (!isEmpty(join)) {- 0
throw new QueryError("Need multiple FROM tables if updating/deleteing a dataset with joins");}} else {- 0
var space = this._static.SPACE;- 0
ret = [space, type.toString(), space, this._sourceList(from), this._selectJoinSql()].join("");}- 1136
return ret;},_selectLockSql:function () {- 3079
if (this.__opts.lock === "share") {- 0
return this._static.FOR_SHARE;} else {- 3079
return this._super(arguments);}},_selectWithSql:function () {- 5535
var optsWith = this.__opts["with"];- 5535
if (!isEmpty(optsWith) && optsWith.some(function (w) {- 0
return w.recursive;})) {- 0
return this._static.SQL_WITH_RECURSIVE;} else {- 5535
return this._super(arguments);}},_updateFromSql:function () {- 206
return this._joinFromSql("FROM");},_updateTableSql:function () {- 206
return [this._static.SPACE, this._sourceList(this.__opts.from.slice(0, 1))].join("");},_quotedIdentifier:function (c) {- 25470
return format('"%s"', c);},__fullTextStringJoin:function (cols) {- 5
var EMPTY_STRING = this._static.EMPTY_STRING;- 5
cols = toArray(cols).map(function (x) {- 7
return sql.COALESCE(x, EMPTY_STRING);});- 5
cols = flatten(zip(cols, array.multiply([this._static.SPACE], cols.length)));- 5
cols.pop();- 5
return StringExpression.fromArgs(['||'].concat(cols));},insert:function () {- 2626
var args = arguments;- 2626
if (this.__opts.returning) {- 1313
return this._super(arguments);} else {- 1313
var ret = new Promise();- 1313
this.primaryKey(this.__opts.from).then(function (res) {- 1313
var pks = res.map(function (r) {- 1147
return r.name;});- 1313
var ds = this.returning.apply(this, pks);- 1313
var dsPromise = ds.insert.apply(ds, args), l = res.length;- 1313
if (l) {- 1147
dsPromise.then(function (insertRes) {- 1147
if (l === 1) {- 1147
ret.callback(insertRes.map(function (i) {- 1147
return i[pks[0]];}).pop());} else {- 0
ret.callback(insertRes.pop());}}, ret);} else {- 166
dsPromise.then(ret);}}.bind(this), ret);- 1313
return ret.promise();}},primaryKey:function () {- 1313
return this.db.primaryKey(this.__opts.from[0]);},fetchRows:function (sql) {- 3059
var oi = this.outputIdentifier.bind(this);- 3059
return asyncArray(this.execute(sql).chain(function (rows, fields) {- 3059
var cols = [];- 3059
if (rows && rows.length) {- 2563
cols = this.__columns = fields && fields.length ? fields.map(function (f) {- 19239
return f.name;}) : Object.keys(rows[0]);//the pg driver does auto type coercion- 2563
cols = cols.map(function (c) {- 19239
return [oi(c), function (o) {- 28729
return o;}, c];}, this);}- 3059
return this.__processRows(rows, cols);}.bind(this)));},__processRows:function (rows, cols) {//dp this so the callbacks are called in appropriate order also.- 3059
return comb(rows).map(function (row, i) {- 3673
var h = {};- 3673
cols.forEach(function (col) {- 28729
h[col[0]] = col[1](row[col[2]]);});- 3673
return h;});},_literalTimestamp:function (v) {- 28
return this.literal(literal("TIMESTAMP " + this._super(arguments) + ""));},_literalBuffer:function (b) {- 8
return this.literal(literal("decode('" + b.toString("hex") + "', 'hex')"));},getters:{columns:function () {- 6
var ret = new Promise();- 6
if (this.__columns) {- 0
ret.callback(this.__columns);} else {- 6
this.db.schema(this.firstSourceTable).then(function (schema) {- 6
this.__columns = schema ? Object.keys(schema) : [];- 6
ret.callback(this.__columns);}, ret);}- 6
return ret.promise();},supportsCteInSubqueries:function () {- 0
return true;},supportsDistinctOn:function () {- 12697
return true;},supportsModifyingJoins:function () {- 15178
return true;},supportsTimestampTimezones:function () {- 12695
return true;}}},"static":{ACCESS_SHARE:'ACCESS SHARE',ACCESS_EXCLUSIVE:'ACCESS EXCLUSIVE',BOOL_FALSE:'false',BOOL_TRUE:'true',COMMA_SEPARATOR:', ',DELETE_CLAUSE_METHODS:Dataset.clauseMethods("delete", 'qualify with from using where returning'),EXCLUSIVE:'EXCLUSIVE',EXPLAIN:'EXPLAIN ',EXPLAIN_ANALYZE:'EXPLAIN ANALYZE ',FOR_SHARE:' FOR SHARE',INSERT_CLAUSE_METHODS:Dataset.clauseMethods("insert", 'with into columns values returning'),LOCK:'LOCK TABLE %s IN %s MODE',NULL:literal('NULL'),QUERY_PLAN:'QUERY PLAN',ROW_EXCLUSIVE:'ROW EXCLUSIVE',ROW_SHARE:'ROW SHARE',SELECT_CLAUSE_METHODS:Dataset.clauseMethods("select", '' +'qualify with distinct columns from join where group having compounds order limit lock'),SHARE:'SHARE',SHARE_ROW_EXCLUSIVE:'SHARE ROW EXCLUSIVE',SHARE_UPDATE_EXCLUSIVE:'SHARE UPDATE EXCLUSIVE',SQL_WITH_RECURSIVE:"WITH RECURSIVE ",TIMESTAMP_FORMAT:"yyyy-MM-dd HH:mm:ss.SSS",TIME_FORMAT:"HH:mm:ss.SSS",UPDATE_CLAUSE_METHODS:Dataset.clauseMethods("update", 'with table set from where returning'),XOR_OP:' # ',CRLF:"\r\n",BLOB_RE:/[\000-\037\047\134\177-\377]/,WINDOW:" WINDOW ",EMPTY_STRING:literal("''")}});- 1
var DB = define(Database, {instance:{EXCLUDE_SCHEMAS:/pg_*|information_schema/i,PREsPARED_ARG_PLACEHOLDER:new LiteralString('$'),RE_CURRVAL_ERROR:/currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/,SYSTEM_TABLE_REGEXP:/^pg|sql/,type:"postgres",constructor:function () {- 34
this._super(arguments);- 34
this.__primaryKeys = {};},createConnection:function (opts) {- 64
delete opts.query;- 64
var conn = new pg.Client(merge({}, opts, {typeCast:false}));- 64
conn.connect();//conn.useDatabase(opts.database)- 64
return new Connection(conn);},closeConnection:function (conn) {- 64
return conn.closeConnection();},validate:function (conn) {- 2181
return new Promise().callback(true).promise();},execute:function (sql, opts, conn) {- 5782
return when(conn || this._getConnection()).chain(function (conn) {- 5782
return this.__execute(conn, sql, opts);}.bind(this));},__execute:function (conn, sql, opts, cb) {- 5782
return this.__logAndExecute(sql, comb("query").bindIgnore(conn, sql)).both(comb("_returnConnection").bindIgnore(this, conn));},// Use the pg_* system tables to determine indexes on a tableindexes:function (table, opts) {- 0
opts = opts || {};- 0
var m = this.outputIdentifierFunc;- 0
var im = this.inputIdentifierFunc;- 0
var parts = this.__schemaAndTable(table), schema = parts[0];- 0
table = parts[1];- 0
var ret = new Promise();- 0
when(this.serverVersion()).then(function (version) {- 0
var attNums;- 0
if (version >= 80100) {- 0
attNums = sql.ANY("ind__indkey");} else {- 0
attNums = [];- 0
for (var i = 0; i < 32; i++) {- 0
attNums.push(Subscript("ind__indkey", [i]));}}- 0
var orderRange = [];- 0
for (var j = 0; j < 32; j++) {- 0
orderRange.push(new Subscript("ind__indkey", [j]));}- 0
orderRange = sql["case"](orderRange, 32, "att__attnum");- 0
var ds = this.metadataDataset.from("pg_class___tab").join("pg_index___ind", [[identifier("indrelid"), identifier("oid")],[im(table), "relname"]]).join("pg_class___indc", [[identifier("oid"), identifier("indexrelid")]]).join("pg_attribute___att", [[identifier("attrelid"), identifier("tab__oid")],[identifier("attnum"), attNums]]).filter({indc__relkind:'i', ind__indisprimary:false, indexprs:null, indpred:null}).order("indc__relname", orderRange).select("indc__relname___name", "ind__indisunique___unique", "att__attname___column");- 0
if (schema) {- 0
ds = ds.join("pg_namespace___nsp", {oid:identifier("tab__relnamespace"), nspname:schema.toString()});}- 0
if (version >= 80200) {- 0
ds = ds.filter({indisvalid:true});}- 0
if (version >= 80300) {- 0
ds = ds.filter({indisready:true, indcheckxmin:false});}- 0
var indexes = {};- 0
ds.forEach(function (r) {- 0
var ident = m(r.name), i = indexes[ident];- 0
if (!i) {- 0
i = indexes[ident] = {columns:[], unique:r.unique};}- 0
i.columns.push(r.column);}).then(function () {- 0
ret.callback(indexes);}, ret);}, ret);- 0
return ret.promise();},locks:function () {- 2
return this.dataset.from("pg_class").join("pg_locks", {relation:identifier("relfilenode")}).select("pg_class__relname", identifier("pg_locks").all());},// Get version of postgres server, used for determined capabilities.serverVersion:function () {- 1
var ret = new Promise();- 1
if (!this.__serverVersion) {- 1
this.get(identifier("version").sqlFunction).then(hitch(this, function (version) {- 1
var m = version.match(/PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/);- 1
this._serverVersion = (parseInt(m[1], 10) * 10000) + (parseInt(m[2], 10) * 100) + parseInt(m[3], 10);- 1
ret.callback(this._serverVersion);}), ret);} else {- 0
ret.callback(this._serverVersion);}- 1
return ret.promise();},/*** Return an array of table names in the current database.* The dataset used is passed to the block if one is provided,* otherwise, an a promise resolved with an array of table names.** Options:* @param {Object} [opts = {}] options* @param {String|patio.sql.Identifier} [opts.schema] The schema to search (default_schema by default)* @param {Function} [cb = null] an optional callback that is invoked with the dataset to retrieve tables.* @return {Promise} a promise resolved with the table names or the result of the cb if one is provided.*/tables:function (opts, cb) {- 0
return this.__pgClassRelname('r', opts, cb);},/*** Return an array of view names in the current database.** Options:* @param {Object} [opts = {}] options* @param {String|patio.sql.Identifier} [opts.schema] The schema to search (default_schema by default)* @return {Promise} a promise resolved with the view names.*/views:function (opts) {- 0
return this.__pgClassRelname('v', opts);},primaryKey:function (table, opts) {- 1313
var ret, quotedTable = this.__quoteSchemaTable(table).toString(), pks = this.__primaryKeys;- 1313
if (pks.hasOwnProperty(quotedTable.toString())) {- 1243
ret = pks[quotedTable];} else {- 70
ret = (pks[quotedTable] = this.__primarykey(table));}- 1313
return ret.promise();},__primarykey:function (table) {- 70
var parts = this.__schemaAndTable(table);- 70
var m2 = this.inputIdentifierFunc;- 70
var schema = parts[0];- 70
table = parts[1];- 70
var ds = this.from(table).select("pg_attribute__attname___name").from("pg_index", "pg_class", "pg_attribute", "pg_namespace").where([[identifier("pg_class__oid"), identifier("pg_attribute__attrelid")],[identifier("pg_class__relnamespace"), identifier("pg_namespace__oid")],[identifier("pg_class__oid"), identifier("pg_index__indrelid")],[identifier("pg_index__indkey").sqlSubscript(0), identifier("pg_attribute__attnum")],[identifier("indisprimary"), true],[identifier("pg_class__relname"), m2(table.toString())]]);- 70
if (schema) {- 0
ds.filter({pg_namespace__nspname:m2(schema)});}- 70
return ds.all();},schemaParseTable:function (tableName, opts) {- 65
var m = this.outputIdentifierFunc,m2 = this.inputIdentifierFunc;- 65
var ds = this.metadataDataset.select("pg_attribute__attname___name",sql.format_type("pg_type__oid", "pg_attribute__atttypmod").as(literal('"dbType"')),sql.pg_get_expr("pg_attrdef__adbin", "pg_class__oid").as(literal('"default"')),sql.NOT("pg_attribute__attnotnull").as(literal('"allowNull"')),sql.COALESCE(BooleanExpression.fromValuePairs({pg_attribute__attnum:sql.ANY("pg_index__indkey")}), false).as(literal('"primaryKey"')),"pg_namespace__nspname").from("pg_class").join("pg_attribute", {attrelid:identifier("oid")}).join("pg_type", {oid:identifier("atttypid")}).join("pg_namespace", {oid:identifier("pg_class__relnamespace")}).leftOuterJoin("pg_attrdef", {adrelid:identifier("pg_class__oid"), adnum:identifier("pg_attribute__attnum")}).leftOuterJoin("pg_index", {indrelid:identifier("pg_class__oid"), indisprimary:true}).filter({pg_attribute__attisdropped:false}).filter({pg_attribute__attnum:{gt:0}}).filter({pg_class__relname:m2(tableName)}).order("pg_attribute__attnum");- 65
ds = this.__filterSchema(ds, opts);- 65
var currentSchema = null;- 65
return ds.map(function (row) {- 374
var sch = row.nspname;- 374
delete row.nspname;- 374
if (currentSchema) {- 309
if (sch !== currentSchema) {- 0
var error = new Error("columns from two tables were returned please specify a schema");- 0
this.logError(error);}} else {- 65
currentSchema = sch;}- 374
if (isBlank(row["default"])) {- 313
row["default"] = null;}- 374
row.type = this.schemaColumnType(row.dbType);- 374
var fieldName = m(row.name);- 374
delete row.name;- 374
return [fieldName, row];}.bind(this));},__commitTransaction:function (conn, opts) {- 856
opts = opts || {};- 856
var s = opts.prepare;- 856
if (s && this.__transactionDepth <= 1) {- 0
return this.__logConnectionExecute(conn, ["PREPARE TRANSACTION ", this.literal(s)].join(""));} else {- 856
return this._super(arguments);}},//Backbone of the tables and views support.__pgClassRelname:function (type, opts, cb) {- 0
var ret = new Promise();- 0
var ds = this.metadataDataset.from("pg_class").filter({relkind:type}).select("relname").exclude({relname:{like:this.SYSTEM_TABLE_REGEXP}}).join("pg_namespace", {oid:identifier("relnamespace")});- 0
ds = this.__filterSchema(ds, opts);- 0
var m = this.outputIdentifierFunc;- 0
if (cb) {- 0
when(cb(ds)).then(ret);} else {- 0
ds.map(function (r) {- 0
return m(r.relname);}).then(ret);}- 0
return ret.promise();},//If opts includes a :schema option, or a default schema is used, restrict the dataset to// that schema. Otherwise, just exclude the default PostgreSQL schemas except for public.__filterSchema:function (ds, opts) {- 65
opts = opts || {};- 65
var schema = opts.schema, ret = ds;- 65
if (schema) {- 0
ds = ds.filter({pg_namespace__nspname:schema});} else {- 65
ds = ds.exclude({pg_namespace__nspname:this.EXCLUDE_SCHEMAS});}- 65
return ds;},__indexDefinitionSql:function (tableName, index) {- 9
tableName = stringToIdentifier(tableName);- 9
var cols = index.columns.map(function (col) {- 10
return stringToIdentifier(col);}),indexName = index.name || this.__defaultIndexName(tableName, cols),o = index.opclass,indexType = index.type,unique = index.unique ? "UNIQUE" : "",filter = index.where || index.filter,expr;- 9
filter = filter ? ["WHERE ", this.__filterExpr(filter)].join("") : "";- 9
if (isDefined(o)) {- 1
expr = ["(", cols.map(function (c) {- 1
return [this.literal(c), o].join(" ");}, this).join(", "), ")"].join("");} else {- 8
expr = this.literal(toArray(cols));}- 9
switch (indexType) {case "fullText":- 2
expr = ["(to_tsvector(", this.literal(index.language || "simple"), ", ", this.literal(this.dataset.__fullTextStringJoin(cols)), "))"].join("");- 2
indexType = "gin";- 2
break;case "spatial" :- 1
indexType = "gist";- 1
break;}- 9
return ["CREATE", unique, "INDEX", this.__quoteIdentifier(indexName), "ON", this.__quoteSchemaTable(tableName), indexType ? "USING " + indexType : "", expr, filter ].join(" ");},/*todo might need this?__insertResult:function (conn, table, values) {},*/__renameTableSql:function (name, newName) {- 1
return ["ALTER TABLE ", this.__quoteSchemaTable(name), " RENAME TO ", this.__quoteIdentifier(this.__schemaAndTable(newName).pop())].join("");},__schemaAutoincrementingPrimaryKey:function (schema) {- 0
return this._super(arguments) && schema.dbType.match(/^(?:integer|bigint)$/i) && schema["default"].match(/^nextval/i);},__typeLiteralGenericNumeric:function (column) {- 2
return column.size ? format("numeric(%s)", array.toArray(column.size).join(', ')) : column.isInt ? "integer" : column.isDouble ? "double precision" : "numeric";},__typeLiteralGenericDateTime:function (column) {- 6
return "timestamp";},//handle bigserial__typeLiteralGenericBigint:function (column) {- 1
return column.serial ? "bigserial" : this.__typeLiteralSpecific(column);},__typeLiteralGenericBlob:function (column) {- 8
return "bytea";},//handle serial type__typeLiteralGenericInteger:function (column) {- 133
return column.serial ? "serial" : this.__typeLiteralSpecific(column);},// PostgreSQL prefers the text datatype. If a fixed size is requested,// the char type is used. If the text type is specifically// disallowed or there is a size specified, use the varchar type.// Otherwise use the type type.__typeLiteralGenericString:function (column) {- 157
if (column.fixed) {- 0
return ["char(", column.size || 255, ")"].join("");- 157
} else if (column.text === false || column.size) {- 139
return ["varchar(", column.size || 255, ")"].join("");} else {- 18
return 'text';}},getters:{connectionExecuteMethod:function () {- 1800
return "query";},dataset:function () {- 948
return new DS(this);},serialPrimaryKeyOptions:function () {- 62
return {primaryKey:true, serial:true, type:"integer"};},supportsSavepoints:function () {- 10140
return true;},supportsTransactionIsolationLevels:function () {- 900
return true;},identifierInputMethodDefault:function () {- 0
return null;},identifierOutputMethodDefault:function () {- 0
return null;}}},"static":{init:function () {- 1
this.setAdapterType("pg");}}}).as(exports, "PostgresDatabase");
|
ConnectionPool.js
|
Coverage73.33
SLOC190
LOC60
Missed16
|
- 1
var comb = require("comb"),Promise = comb.Promise,PromiseList = comb.PromiseList,isFunction = comb.isFunction,Queue = comb.collections.Queue,merge = comb.merge,define = comb.define,Pool = comb.collections.Pool;- 1
define(Pool, {instance:{/**@lends patio.ConnectionPool.prototype*//*** ConnectionPool object used internall by the {@link patio.Database} class;* @constructs;* @param options*/constructor:function (options) {- 122
options = options || {};- 122
if (!options.createConnection || !isFunction(options.createConnection)) {- 0
throw "patio.adapters.clients.ConnectionPool : create connection CB required.";}- 122
if (!options.closeConnection || !isFunction(options.closeConnection)) {- 0
throw "patio.adapters.clients.ConnectionPool : close connection CB required.";}- 122
options.minObjects = parseInt(options.minConnections || 0, 10);- 122
options.maxObjects = parseInt(options.maxConnections || 10, 10);- 122
this.__deferredQueue = new Queue();- 122
this._options = options;- 122
this.__createConnectionCB = options.createConnection;- 122
this.__closeConnectionCB = options.closeConnection;- 122
this.__validateConnectionCB = options.validateConnection;- 122
this._super(arguments);},/*** Checks all deferred connection requests.*/__checkQueries:function () {- 2593
var fc = this.freeCount, def, defQueue = this.__deferredQueue;- 2593
while (fc-- >= 0 && defQueue.count) {- 0
def = defQueue.dequeue();- 0
var conn = this.getObject();- 0
if (conn) {- 0
def.callback(conn);} else {//we didnt get a conneciton so assume we're out.- 0
break;}- 0
fc--;}},/*** Performs a query on one of the connection in this Pool.** @return {comb.Promise} A promise to called back with a connection.*/getConnection:function () {- 2597
var ret = new Promise();//todo override getObject to make async so creating a connetion can execute setup sql- 2597
var conn = this.getObject();- 2597
if (!conn) {//we need to deffer it- 0
this.__deferredQueue.enqueue(ret);} else {- 2597
ret.callback(conn);}- 2597
return ret.promise();},/*** Override comb.collections.Pool to allow async validation to allow* pools to do any calls to reset a connection if it needs to be done.** @param {*} connection the connection to return.**/returnObject:function (obj) {- 2593
if (this.count <= this.__maxObjects) {- 2593
this.validate(obj).then(function (valid) {- 2593
if (valid) {- 2593
this.__freeObjects.enqueue(obj);- 2593
var index;- 2593
if ((index = this.__inUseObjects.indexOf(obj)) > -1) {- 2593
this.__inUseObjects.splice(index, 1);}- 2593
this.__checkQueries();} else {- 0
this.removeObject(obj);}}.bind(this));} else {- 0
this.removeObject(obj);}},/*** Removes a connection from the pool.* @param conn*/removeConnection:function (conn) {- 0
this.closeConnection(conn);- 0
return this.removeObject(conn);},/*** Return a connection to the pool.** @param {*} connection the connection to return.** @return {*} an adapter specific connection.*/returnConnection:function (connection) {- 2593
this.returnObject(connection);},createObject:function () {- 80
return this.createConnection();},/*** Override to implement the closing of all connections.** @return {comb.Promise} called when all connections are closed.*/endAll:function () {- 41
this.__ending = true;- 41
var conn, fQueue = this.__freeObjects, count = this.count, ps = [];- 41
while ((conn = this.__freeObjects.dequeue()) != undefined) {- 67
ps.push(this.closeConnection(conn));}- 41
var inUse = this.__inUseObjects;- 41
for (var i = inUse.length - 1; i >= 0; i--) {- 4
ps.push(this.closeConnection(inUse[i]));}- 41
this.__inUseObjects.length = 0;- 41
return new PromiseList(ps).promise();},/*** Override to provide any additional validation. By default the promise is called back with true.** @param {*} connection the conneciton to validate.** @return {comb.Promise} called back with a valid or invalid state.*/validate:function (conn) {- 2593
if (!this.__validateConnectionCB) {- 0
var ret = new Promise();- 0
ret.callback(true);- 0
return ret;} else {- 2593
return this.__validateConnectionCB();}},/*** Override to create connections to insert into this ConnectionPool.*/createConnection:function () {- 80
return this.__createConnectionCB(this._options);},/*** Override to implement close connection functionality;* @param {*} conn the connection to close;** @return {comb.Promise} called back when the connection is closed.*/closeConnection:function (conn) {- 71
return this.__closeConnectionCB(conn);}},"static":{/**@lends patio.ConnectionPool*/getPool:function (opts, createConnection, closeConnection, validateConnection) {- 122
return new this(merge(opts, {createConnection:createConnection,closeConnection:closeConnection,validateConnection:validateConnection}));}}}).as(module);
|
associations/oneToMany.js
|
Coverage78.40
SLOC352
LOC162
Missed35
|
- 1
var comb = require("comb-proxy"),isArray = comb.isArray,isUndefinedOrNull = comb.isUndefinedOrNull,isBoolean = comb.isBoolean,define = comb.define,hitch = comb.hitch,hitchIgnore = comb.hitchIgnore,Promise = comb.Promise,isNull = comb.isNull,when = comb.when,isInstanceOf = comb.isInstanceOf,serial = comb.serial,PromiseList = comb.PromiseList,isUndefined = comb.isUndefined,singularize = comb.singularize,_Association = require("./_Association");/*** @class Class to define a one to many association.** </br>* <b>NOT to be instantiated directly</b>* Its just documented for reference.** Adds the following methods to each model.* <ul>* <li>add{ModelName} - add an association</li>* <li>add{comb.pluralize(ModelName)} - add multiple associations</li>* <li>remove{ModelName} - remove an association</li>* <li>remove{comb.pluralize(ModelName)} - remove multiple association</li>* <li>removeAll - removes all associations of this type</li>* </ul>** @name OneToMany* @augments patio.associations.Association* @memberOf patio.associations** */- 1
module.exports = define(_Association, {instance:{/**@lends patio.associations.OneToMany.prototype*/type:"oneToMany",createSetter:true,_postSave:function (next, model) {- 325
var loaded = this.associationLoaded(model), vals;- 325
if (loaded && (vals = this.getAssociation(model))) {- 78
if (isArray(vals) && vals.length) {- 78
this._clearAssociations(model);- 78
var pl = this.addAssociations(vals, model);- 78
if (this.isEager()) {- 24
pl = pl.chain(hitch(this, "fetch", model), next);}- 78
pl.classic(next);} else {- 0
next();}- 247
} else if (this.isEager() && !loaded) {- 90
this.fetch(model).classic(next);} else {- 157
next();}},_postUpdate:function (next, model) {- 2
var removeAssociationFlagName = this.removeAssociationFlagName;- 2
if (model[removeAssociationFlagName]) {- 0
var oldVals = this._getCachedOldVals(model);- 0
this._clearCachedOldVals(model);- 0
var pl = oldVals.length ? this.removeItems(oldVals, model, false) : null;- 0
when(pl).chain(function () {- 0
this.addAssociations(this.getAssociation(model), model).classic(next);}.bind(this)).classic(next);- 0
model[removeAssociationFlagName] = false;} else {- 2
next();}},_postLoad:function (next, model) {- 864
if (this.isEager() && !this.associationLoaded(model)) {- 192
this.fetch(model).classic(next);} else {- 672
next();}},/*** Middleware called before a model is removed.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being acted up.*/_preRemove:function (next, model) {- 89
this.removeAllItems(model).classic(next);},_getCachedOldVals:function (model) {- 0
return model[this.oldAssocationCacheName] || [];},_clearCachedOldVals:function (model) {- 0
model[this.oldAssocationCacheName] = [];},_cacheOldVals:function (model) {- 0
var oldVals = model[this.oldAssocationCacheName] || [];- 0
oldVals = oldVals.concat(this.getAssociation(model));- 0
model[this.oldAssocationCacheName] = oldVals;},_setter:function (vals, model) {- 78
if (!isUndefined(vals)) {- 78
if (model.isNew) {- 78
if (!isNull(vals)) {- 78
this.addAssociations(vals, model);//this.__setValue(model, vals);} else {- 0
this.__setValue(model, []);}} else {- 0
model.__isChanged = true;- 0
model[this.removeAssociationFlagName] = true;- 0
this._cacheOldVals(model);- 0
if (!isNull(vals)) {//ensure its an array!- 0
vals = (isArray(vals) ? vals : [vals]).map(function (m) {- 0
return this._toModel(m);}, this);} else {- 0
vals = [];}- 0
this.__setValue(model, vals);}}},addAssociation:function (item, model, reload) {- 262
reload = isBoolean(reload) ? reload : false;- 262
var ret = new Promise().callback(model);- 262
if (!isUndefinedOrNull(item)) {- 262
if (!model.isNew) {- 150
item = this._toModel(item);- 150
var loaded = this.associationLoaded(model);- 150
this._setAssociationKeys(model, item);- 150
var recip = this.model._findAssociation(this);- 150
if (recip) {- 149
recip[1].__setValue(item, model);}- 150
ret = model._checkTransaction(hitch(this, function () {- 150
var ret = new Promise();- 150
serial([item.save.bind(item),function () {- 150
if (loaded && reload) {- 3
return this.parent._reloadAssociationsForType(this.type, this.model, model);}}.bind(this)]).then(hitchIgnore(ret, "callback", model), ret);- 150
return ret.promise();}));} else {- 112
item = this._toModel(item);- 112
var items = this.getAssociation(model);- 112
if (isUndefinedOrNull(items)) {- 39
this.__setValue(model, [item]);} else {- 73
items.push(item);}}}- 262
return ret.promise();},addAssociations:function (items, model) {- 174
var ret = new Promise(), pl;- 174
if (model.isNew) {- 78
(isArray(items) ? items : [items]).map(function (item) {- 224
return this.addAssociation(item, model, false);}, this);- 78
ret.callback(model);} else {- 96
pl = model._checkTransaction(hitch(this, function () {- 96
return new PromiseList((isArray(items) ? items : [items]).map(function (item) {- 278
return this.addAssociation(item, model, false);}, this));}));- 96
pl.then(hitch(this, function () {- 96
if (!model.isNew && this.associationLoaded(model)) {- 6
this.parent._reloadAssociationsForType(this.type, this.model, model).then(hitchIgnore(ret, "callback", model), ret);} else {- 90
ret.callback(model);}}), ret);}- 174
return ret.promise();},removeItem:function (item, model, remove, reload) {- 56
reload = isBoolean(reload) ? reload : false;- 56
remove = isBoolean(remove) ? remove : false;- 56
var ret = new Promise().callback(model);- 56
if (!isUndefinedOrNull(item)) {- 56
if (!model.isNew) {- 56
if (isInstanceOf(item, this.model) && !item.isNew) {- 56
if (!remove) {- 28
this._setAssociationKeys(model, item, null);}- 56
var loaded = this.associationLoaded(model);- 56
return model._checkTransaction(hitch(this, function () {- 56
var ret = new Promise();- 56
serial([item[remove ? "remove" : "save"].bind(item),function () {- 56
if (loaded && reload) {- 18
return this.parent._reloadAssociationsForType(this.type, this.model, model);}}.bind(this)]).then(hitchIgnore(ret, "callback", model), ret);- 56
return ret.promise();}));}} else {- 0
item = this._toModel(item);- 0
var items = this.getAssociation(model), index;- 0
if (!isUndefinedOrNull(items) && (index = items.indexOf(item)) !== -1) {- 0
items.splice(index, 1);}}}- 0
return ret.promise();},removeItems:function (items, model, remove) {//todo make this more efficient!!!!- 36
var ret = new Promise();- 36
if (model.isNew) {- 0
(isArray(items) ? items : [items]).map(function (item) {- 0
return this.removeItem(item, model, remove, false);}, this);- 0
ret.callback(model);} else {- 36
var pl = model._checkTransaction(hitch(this, function () {- 36
return new PromiseList((isArray(items) ? items : [items]).map(function (item) {- 76
return this.removeItem(item, model, remove, false);}, this));}));- 36
pl.then(hitch(this, function () {- 36
if (this.associationLoaded(model)) {- 36
this.parent._reloadAssociationsForType(this.type, this.model, model).then(hitchIgnore(ret, "callback", model), ret);} else {- 0
ret.callback(model);}}), ret);}- 36
return ret.promise();},removeAllItems:function (model, remove) {- 93
remove = isBoolean(remove) ? remove : false;- 93
var ret = new Promise();- 93
if (!model.isNew) {- 93
var q = {}, removeQ = {};- 93
this._setAssociationKeys(model, q);- 93
this._setAssociationKeys(model, removeQ, null);- 93
var loaded = this.associationLoaded(model);- 93
return model._checkTransaction(hitch(this, function () {- 93
var ds = model[this.associatedDatasetName], ret = new Promise();- 93
this._filter(model).forEach(function (m) {- 99
return remove ? m.remove() : m.update(removeQ);}).then(function () {- 93
if (loaded) {- 35
this.parent._reloadAssociationsForType(this.type, this.model, model).then(hitchIgnore(ret, "callback", model), ret);} else {- 58
ret.callback(model);}}.bind(this), ret);- 93
return ret.promise();}));} else {//todo we may want to check if any of the items were previously saved items;- 0
this._clearAssociations(model);- 0
ret.callback(model);}- 0
return ret.promise();},inject:function (parent, name) {- 31
this._super(arguments);- 31
var singular = singularize(name);- 31
if (this._model === name) {- 23
this._model = singular;}- 31
singular = singular.charAt(0).toUpperCase() + singular.slice(1);- 31
if (!this.readOnly) {- 31
this.removedKey = "__removed" + name + "";- 31
this.addedKey = "__added_" + name + "";- 31
parent.prototype[this.removedKey] = [];- 31
parent.prototype[this.addedKey] = [];- 31
var self = this;- 31
name = name.charAt(0).toUpperCase() + name.slice(1);- 31
var addName = "add" + singular;- 31
var addNames = "add" + name;- 31
var removeName = "remove" + singular;- 31
var removeNames = "remove" + name;- 31
var removeAllName = "removeAll" + name;- 31
parent.prototype[addName] = function (item) {- 18
return isArray(item) ? self.addAssociations(item, this) : self.addAssociation(item, this, true);};- 31
parent.prototype[addNames] = function (items) {- 20
return isArray(items) ? self.addAssociations(items, this) : self.addAssociation(items, this);};- 31
parent.prototype[removeName] = function (item, remove) {- 36
return isArray(item) ? self.removeItems(item, this, remove) : self.removeItem(item, this, remove, true);};- 31
parent.prototype[removeNames] = function (item, remove) {- 36
return isArray(item) ? self.removeItems(item, this, remove) : self.removeItem(item, this, remove);};- 31
parent.prototype[removeAllName] = function (remove) {- 8
return self.removeAllItems(this, remove);};}},getters:{oldAssocationCacheName:function () {- 0
return "_" + this.name + "OldValues";},//Returns our modelmodel:function () {- 7702
try {- 7702
return this.__model__ || (this.__model__ = this.patio.getModel(this._model, this.parent.db));} catch (e) {- 0
return this.__model__ = this.patio.getModel(this.name, this.parent.db)}}}}});
|
database/index.js
|
Coverage78.76
SLOC300
LOC113
Missed24
|
- 1
var comb = require("comb"),format = comb.string.format,merge = comb.merge,hitch = comb.hitch,isNull = comb.isNull,define = comb.define,isBoolean = comb.isBoolean,isUndefined = comb.isUndefined,isFunction = comb.isFunction,isString = comb.isString,isObject = comb.isObject,isDate = comb.isDate,isArray = Array.isArray,isHash = comb.isHash,isNumber = comb.isNumber,isInstanceOf = comb.isInstanceOf,isEmpty = comb.isEmpty,sql = require("../sql").sql,DateTime = sql.DateTime,TimeStamp = sql.TimeStamp,Year = sql.Year,Time = sql.Time,ConnectionPool = require("../ConnectionPool"),DatabaseError = require("../errors").DatabaseError,ConnectDB = require("./connect"),DatasetDB = require("./dataset"),DefaultsDB = require("./defaults"),LoggingDB = require("./logging"),QueryDB = require("./query"),SchemaDB = require("./schema"), patio;- 1
var DATABASES = [];- 1
var Database = define([ConnectDB, DatasetDB, DefaultsDB, LoggingDB, QueryDB, SchemaDB], {instance:{/**@lends patio.Database.prototype*//*** A Database object represents a virtual connection to a database.* The Database class is meant to be subclassed by database adapters in order* to provide the functionality needed for executing queries.** @constructs* @param {Object} opts options used to create the database** @property {String} uri A database URI used to create the database connection. This property is* available even if an object was used to create the database connection.* @property {patio.Dataset} dataset returns an empty adapter specific {@link patio.Dataset} that can* be used to query the {@link patio.Database} with.*/constructor:function (opts) {- 122
opts = opts || {};- 122
if (!patio) {- 1
patio = require("../index");}- 122
this.patio = patio;- 122
this._super(arguments, [opts]);- 122
opts = merge(this.connectionPoolDefaultOptions, opts);- 122
this.schemas = {};- 122
this.type = opts.type;- 122
this.defaultSchema = opts.defaultSchema || this.defaultSchemaDefault;- 122
this.preparedStatements = {};- 122
this.opts = opts;- 122
this.pool = ConnectionPool.getPool(opts, this.createConnection.bind(this), this.closeConnection.bind(this), this.validate.bind(this));},/*** Casts the given type to a SQL type.** @example* DB.castTypeLiteral(Number) //=> numeric* DB.castTypeLiteral("foo") //=> foo* DB.castTypeLiteral(String) //=> varchar(255)* DB.castTypeLiteral(Boolean) //=> boolean**@param type the javascript type to cast to a SQL type.** @return {String} the SQL data type.**/castTypeLiteral:function (type) {- 2
return this.typeLiteral({type:type});},/*** This function acts as a proxy to {@link patio.Dataset#literal}.** See {@link patio.Dataset#literal}.**/literal:function (v) {- 157
return this.dataset.literal(v);},/*** Typecast the value to the given columnType. Calls* typecastValue{ColumnType} if the method exists,* otherwise returns the value.** @example* DB.typeCastValue("boolean", 0) //=> false* DB.typeCastValue("boolean", 1) //=> true* DB.typeCastValue("timestamp", '2004-02-01 12:12:12')* //=> new patio.sql.TimeStamp(2004, 1, 1, 12, 12, 12);** @throws {patio.DatabaseError} if there is an error converting the value to the column type.** @param {String} columnType the SQL datatype of the column* @param value the value to typecast.** @return the typecasted value.* */typecastValue:function (columnType, value) {- 33146
if (isNull(value) || isUndefined(value)) {- 5879
return null;}- 27267
var meth = "__typecastValue" + columnType.charAt(0).toUpperCase() + columnType.substr(1).toLowerCase();- 27267
try {- 27267
if (isFunction(this[meth])) {- 27267
return this[meth](value);} else {- 0
return value;}} catch (e) {- 9
throw e;}},// Typecast the value to true, false, or null__typecastValueBoolean:function (value) {- 9
if (isBoolean(value)) {- 7
return value;- 2
} else if (value === 0 || value === "0" || (isString(value) && value.match(/^f(alse)?$/i) !== null)) {- 1
return false;} else {- 1
return (isObject(value) && isEmpty(value)) || !value ? null : true;}},// Typecast the value to blob, false, or null__typecastValueBlob:function (value) {- 28
if (isInstanceOf(value, Buffer)) {- 12
return value;- 16
} else if (isArray(value) || isString(value)) {- 14
return new Buffer(value);} else {- 2
throw new Error("Invalid value for blob " + value);}},// Typecast the value to true, false, or null__typecastValueText:function (value) {- 374
return value.toString();},// Typecast the value to a Date__typecastValueDate:function (value) {- 15
if (isDate(value)) {- 12
return value;- 3
} else if (isString(value)) {- 2
var ret = patio.stringToDate(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for date %j", [value]));}- 1
return ret;- 1
} else if (isHash(value) && !isEmpty(value)) {- 0
return new Date(value.year, value.month, value.day);} else {- 1
throw new DatabaseError(format("Invalid value for date %j", [value]));}},// Typecast the value to a patio.sql.DateTime.__typecastValueDatetime:function (value) {- 62
var ret;- 62
if (isInstanceOf(value, DateTime)) {- 0
return value;- 62
} else if (isDate(value)) {- 60
ret = value;- 2
} else if (isHash(value) && !isEmpty(value)) {- 0
ret = new Date(value.year, value.month, value.day, value.hour, value.minute, value.second);- 2
} else if (isString(value)) {- 2
ret = patio.stringToDateTime(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for datetime %j", [value]));}- 1
ret = ret.date;} else {- 0
throw new DatabaseError(format("Invalid value for datetime %j", [value]));}- 61
return new DateTime(ret);},// Typecast the value to a patio.sql.DateTime__typecastValueTimestamp:function (value) {- 1
var ret;- 1
if (isInstanceOf(value, TimeStamp)) {- 0
return ret;- 1
} else if (isDate(value)) {- 0
ret = value;- 1
} else if (isHash(value) && !isEmpty(value)) {- 0
ret = new Date(value.year, value.month, value.day, value.hour, value.minute, value.second);- 1
} else if (isString(value)) {- 1
ret = patio.stringToTimeStamp(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for timestamp %j", [value]));}- 1
ret = ret.date;} else {- 0
throw new DatabaseError(format("Invalid value for timestamp %j", [value]));}- 1
return new TimeStamp(ret);},// Typecast the value to a patio.sql.Year__typecastValueYear:function (value) {- 1
if (isInstanceOf(value, Year)) {- 0
return value;- 1
} else if (isNumber(value)) {- 0
return new Year(value);- 1
} else if (isString(value)) {- 1
var ret = patio.stringToYear(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for date %j", [value]));}- 1
return ret;- 0
} else if (isHash(value) && !isEmpty(value)) {- 0
return new Date(value.year, value.month, value.day);} else {- 0
throw new DatabaseError(format("Invalid value for date %j", [value]));}},// Typecast the value to a patio.sql.Time__typecastValueTime:function (value) {- 2
var ret;- 2
if (isInstanceOf(value, Time)) {- 0
return value;- 2
} else if (isDate(value)) {- 0
ret = value;- 2
} else if (isString(value)) {- 2
ret = patio.stringToTime(value);- 1
if (!ret) {- 0
throw new DatabaseError(format("Invalid value for time %j", [value]));}- 1
ret = ret.date;- 0
} else if (isHash(value) && !isEmpty(value)) {- 0
ret = new Date(0, 0, 0, value.hour, value.minute, value.second);} else {- 0
throw new DatabaseError(format("Invalid value for time %j", [value]));}- 1
return new Time(ret);},// Typecast the value to a Number__typecastValueDecimal:function (value) {- 36
var ret = parseFloat(value);- 36
if (isNaN(ret)) {- 1
throw new DatabaseError(format("Invalid value for decimal %j", [value]));}- 35
return ret;},// Typecast the value to a Number__typecastValueFloat:function (value) {- 325
var ret = parseFloat(value);- 325
if (isNaN(ret)) {- 1
throw new DatabaseError(format("Invalid value for float %j", [value]));}- 324
return ret;},// Typecast the value to a Number__typecastValueInteger:function (value) {- 6581
var ret = parseInt(value, 10);- 6581
if (isNaN(ret)) {- 1
throw new DatabaseError(format("Invalid value for integer %j", [value]));}- 6580
return ret;},// Typecast the value to a String__typecastValueString:function (value) {- 19833
return "" + value;}},"static":{/**@lends patio.Database*//*** A list of currently connected Databases.* @type patio.Database[]*/DATABASES:DATABASES}}).as(module);
|
associations/manyToOne.js
|
Coverage80.00
SLOC100
LOC30
Missed6
|
- 1
var comb = require("comb"),when = comb.when,hitch = comb.hitch,Promise = comb.Promise,PromiseList = comb.PromiseList,define = comb.define,isNull = comb.isNull,isUndefinedOrNull = comb.isUndefinedOrNull,_Association = require("./_Association");/*** @class Class to define a many to one association.** </br>* <b>NOT to be instantiated directly</b>* Its just documented for reference.** @name ManyToOne* @augments patio.associations.Association* @memberOf patio.associations** */- 1
module.exports = exports = define(_Association, {instance:{/**@lends patio.associations.ManyToOne.prototype*/_fetchMethod:"one",type:"manyToOne",isOwner:false,__checkAndSetAssociation:function (next, model) {- 328
var assoc;- 328
if (this.associationLoaded(model) && !isUndefinedOrNull((assoc = this.getAssociation(model)))) {- 280
if (assoc.isNew) {- 8
assoc.save().both(hitch(this, function () {- 8
var recip = this.model._findAssociation(this);- 8
if (recip) {//set up our association- 8
recip[1]._setAssociationKeys(assoc, model);}})).classic(next);} else {- 272
next();}} else {- 48
this._clearAssociations(model);- 48
next();}},_preSave:function (next, model) {- 199
this.__checkAndSetAssociation(next, model);},_preUpdate:function (next, model) {- 129
this.__checkAndSetAssociation(next, model);},//override//@see _Association_postSave:function (next, model) {- 199
if (this.isEager() && !this.associationLoaded(model)) {- 12
this.fetch(model).classic(next);} else {- 187
next();}},//override//@see _Association_postLoad:function (next, model) {- 724
if (this.isEager() && !this.associationLoaded(model)) {- 92
this.fetch(model).classic(next);} else {- 632
next();}},//override//@see _Association_setter:function (val, model) {- 8
if (!isUndefinedOrNull(val)) {- 8
val = this._toModel(val);- 8
this.__setValue(model, val);- 8
if (!val.isNew) {- 0
var recip = this.model._findAssociation(this);- 0
if (recip) {//set up our association- 0
recip[1]._setAssociationKeys(val, model);}}- 0
} else if (!model.isNew && isNull(val)) {- 0
var keys = this._getAssociationKey(model)[0].forEach(function (k) {- 0
model[k] = null;});}}}});
|
adapters/mysql.js
|
Coverage80.71
SLOC879
LOC337
Missed65
|
- 1
var mysql = require("mysql"),comb = require("comb"),asyncArray = comb.async.array,define = comb.define,merge = comb.merge,string = comb.string,argsToArray = comb.argsToArray,format = string.format,hitch = comb.hitch,Promise = comb.Promise,isString = comb.isString,array = comb.array,toArray = array.toArray,isArray = comb.isArray,isHash = comb.isHash,when = comb.when,isInstanceOf = comb.isInstanceOf,isFunction = comb.isFunction,isUndefinedOrNull = comb.isUndefinedOrNull,isUndefined = comb.isUndefined,isEmpty = comb.isEmpty,QueryError = require("../errors").QueryError,Dataset = require("../dataset"),Database = require("../database"),sql = require("../sql").sql,DateTime = sql.DateTime,Time = sql.Time,Year = sql.Year,Double = sql.Double,patio;- 1
var convertDate = function (v, m, convertDateTime) {- 15
try {- 15
return patio[m](v);} catch (e) {- 15
if (convertDateTime === null) {- 3
return null;- 12
} else if (convertDateTime === String || (isString(convertDateTime) && convertDateTime.match(/string/i))) {- 9
return v;} else {- 3
throw e;}}};- 1
var Connection = define(null, {instance:{connection:null,constructor:function (conn) {- 3
this.connection = conn;},closeConnection:function () {- 3
var ret = new Promise();- 3
this.connection.end(ret.resolve.bind(ret));- 3
return ret.promise();},query:function (query) {- 295
var ret = new Promise();- 295
try {- 295
this.connection.setMaxListeners(0);- 295
this.connection.query(query, ret.resolve.bind(ret));} catch (e) {- 0
patio.logError(e);}- 295
return ret.promise();}}});- 1
var DS = define(Dataset, {instance:{__providesAccurateRowsMatched:false,__supportsDistinctOn:true,__supportsIntersectExcept:false,__supportsModifyingJoins:true,__supportsTimestampUsecs:false,// MySQL specific syntax for LIKE/REGEXP searches, as well as// string concatenation.complexExpressionSql:function (op, args) {- 23
var likeOps = ["~", "~*", "LIKE", "ILIKE"];- 23
var notLikeOps = ["!~", "!~*", "NOT LIKE", "NOT ILIKE"];- 23
var regExpOps = ["~", "!~", "~*", "!~*"];- 23
var binaryOps = ["~", "!~", "LIKE", "NOT LIKE"];- 23
if (likeOps.indexOf(op) !== -1 || notLikeOps.indexOf(op) !== -1) {- 10
return format("(%s%s %s%s %s)", this.literal(args[0]), notLikeOps.indexOf(op) !== -1 ? " NOT" : "",regExpOps.indexOf(op) !== -1 ? "REGEXP" : "LIKE", binaryOps.indexOf(op) !== -1 ? " BINARY" : "",this.literal(args[1]));- 13
} else if (op === "||") {- 5
if (args.length > 1) {- 3
return format("CONCAT(%s)", args.map(this.literal, this).join(", "));} else {- 2
return this.literal(args[0]);}- 8
} else if (op === "B~") {- 0
return format("CAST(~%s AS SIGNED INTEGER)", this.literal(args[0]));} else {- 8
return this._super(arguments);}},// Use GROUP BY instead of DISTINCT ON if arguments are provided.distinct:function (args) {- 2
args = argsToArray(arguments);- 2
return !args.length ? this._super(arguments) : this.group.apply(this, args);},//Return a cloned dataset which will use LOCK IN SHARE MODE to lock returned rows.forShare:function () {- 1
return this.lockStyle("share");},//Adds full text filterfullTextSearch:function (cols, terms, opts) {- 3
opts = opts || {};- 3
cols = toArray(cols).map(this.stringToIdentifier, this);- 3
return this.filter(sql.literal(this.fullTextSql(cols, terms, opts)));},//MySQL specific full text search syntax.fullTextSql:function (cols, term, opts) {- 3
opts = opts || {};- 3
return format("MATCH %s AGAINST (%s%s)", this.literal(toArray(cols)),this.literal(toArray(term).join(" ")), opts.boolean ? " IN BOOLEAN MODE" : "");},//MySQL allows HAVING clause on ungrouped datasets.having:function (cond, cb) {- 3
var args = argsToArray(arguments);- 3
return this._filter.apply(this, ["having"].concat(args));},// Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.//Raises an error on use of :full_outer type, since MySQL doesn't support it.joinTable:function (type, table, expr, tableAlias) {- 12
tableAlias = tableAlias || {};- 12
if (type === "cross" && !isUndefinedOrNull(expr)) {- 1
type = "inner";}- 12
if (type === "fullOuter") {- 1
throw new QueryError("MySQL does not support FULL OUTER JOIN");}- 11
return this._super(arguments, [type, table, expr, tableAlias]);},// Transforms :natural_inner to NATURAL LEFT JOIN and straight to//STRAIGHT_JOIN._joinTypeSql:function (joinType) {- 11
if (joinType === "straight") {- 2
return "STRAIGHT_JOIN";- 9
} else if (joinType === "naturalInner") {- 1
return "NATURAL LEFT JOIN";} else {- 8
return this._super(arguments);}},insertIgnore:function () {- 2
return this.mergeOptions({insertIgnore:true});},onDuplicateKeyUpdate:function (args) {- 3
args = argsToArray(arguments).map(function (c) {- 5
return isString(c) ? this.stringToIdentifier(c) : c;}, this);- 3
return this.mergeOptions({onDuplicateKeyUpdate:args});},// MySQL specific syntax for inserting multiple values at once.multiInsertSql:function (columns, values) {- 8
return [this.insertSql(columns, sql.literal('VALUES ' + values.map(function (r) {- 16
return this.literal(toArray(r));}, this).join(this._static.COMMA_SEPARATOR)))];},//MySQL uses the nonstandard ` (backtick) for quoting identifiers._quotedIdentifier:function (c) {- 51
return format("`%s`", c);},// MySQL specific syntax for REPLACE (aka UPSERT, or update if exists,//insert if it doesn't).replaceSql:function (values) {- 9
var ds = this.mergeOptions({replace:true});- 9
return ds.insertSql.apply(ds, argsToArray(arguments));},//If this is an replace instead of an insert, use replace instead_insertSql:function () {- 51
return this.__opts.replace ? this._clauseSql("replace") : this._super(arguments);},//Consider the first table in the joined dataset is the table to delete//from, but include the others for the purposes of selecting rows._deleteFromSql:function (sql) {- 10
if (this._joinedDataset) {- 0
return format(" %s FROM %s%s", this._sourceList(this.__opts.from[0]), this._sourceList(this.__opts.from), this._selectJoinSql());} else {- 10
return this._super(arguments);}},//alias replace_clause_methods insert_clause_methods//MySQL doesn't use the standard DEFAULT VALUES for empty values._insertColumnsSql:function (sql) {- 51
var values = this.__opts.values;- 51
if (isArray(values) && !values.length) {- 9
return " ()";} else {- 42
return this._super(arguments);}},//MySQL supports INSERT IGNORE INTO_insertIgnoreSql:function (sql) {- 51
return this.__opts.insertIgnore ? " IGNORE" : "";},//MySQL supports INSERT ... ON DUPLICATE KEY UPDATE_insertOnDuplicateKeyUpdateSql:function (sql) {- 51
return this.__opts.onDuplicateKeyUpdate ? this.onDuplicateKeyUpdateSql() : "";},//MySQL doesn't use the standard DEFAULT VALUES for empty values._insertValuesSql:function (sql) {- 51
var values = this.__opts.values;- 51
if (isArray(values) && !values.length) {- 9
return " VALUES ()";} else {- 42
return this._super(arguments);}},//MySQL allows a LIMIT in DELETE and UPDATE statements.limitSql:function (sql) {- 12
return this.__opts.limit ? format(" LIMIT %s", this.__opts.limit) : "";},_deleteLimitSql:function () {- 10
return this.limitSql.apply(this, arguments);},_updateLimitSql:function () {- 2
return this.limitSql.apply(this, arguments);},//MySQL specific syntax for ON DUPLICATE KEY UPDATEonDuplicateKeyUpdateSql:function () {- 3
var ret = "";- 3
var updateCols = this.__opts.onDuplicateKeyUpdate;- 3
if (updateCols) {- 3
var updateVals = null, l, last;- 3
if ((l = updateCols.length) > 0 && isHash((last = updateCols[l - 1]))) {- 2
updateVals = last;- 2
updateCols = l === 2 ? [updateCols[0]] : updateCols.slice(0, l - 2);}- 3
var updating = updateCols.map(function (c) {- 3
var quoted = this.quoteIdentifier(c);- 3
return format("%s=VALUES(%s)", quoted, quoted);}, this);- 3
for (var i in updateVals) {- 2
if (i in updateVals) {- 2
updating.push(format("%s=%s", this.quoteIdentifier(i), this.literal(updateVals[i])));}}- 3
if (updating || updateVals) {- 3
ret =format(" ON DUPLICATE KEY UPDATE %s", updating.join(this._static.COMMA_SEPARATOR));}}- 3
return ret;},//Support FOR SHARE locking when using the :share lock style._selectLockSql:function (sql) {- 86
return this.__opts.lock === "share" ? this._static.FOR_SHARE : this._super(arguments);},// Delete rows matching this datasetremove:function () {- 10
return this.executeDui(this.deleteSql).chain(function (c, info) {- 10
return c.affectedRows;});},fetchRows:function (sql) {- 87
return asyncArray(this.execute(sql).chain(function (r, fields) {- 87
var i = -1;- 87
var cols;- 87
cols = fields.map(function (col) {- 304
var fieldName = col.name;- 304
var type = col.type;- 304
var length = col.fieldLength;- 304
return [this.outputIdentifier(fieldName), DB.convertMysqlType(type === 1 && length !== 1 ? 2 : type), fieldName];}, this);- 87
this.__columns = cols.map(function (c) {- 304
return c[0];});- 87
return this.__processRows(r, cols);}.bind(this)));},__processRows:function (rows, cols) {//dp this so the callbacks are called in appropriate order also.- 87
return comb(rows).map(function (row) {- 118
var h = {};- 118
cols.forEach(function (col) {- 468
h[col[0]] = col[1](row[col[2]]);});- 115
return h;});},//Don't allow graphing a dataset that splits multiple statementsgraph:function () {- 0
if (this.__opts.splitMultipleResultSels) {- 0
throw new QueryError("Can't graph a dataset that splits multiple result sets");}- 0
this._super(arguments);},//Insert a new value into this datasetinsert:function () {- 32
return this.executeDui(this.insertSql.apply(this, arguments)).chain(function (c, info) {- 32
return c.insertId;});},// Replace (update or insert) the matching row.replace:function () {- 9
return this.executeDui(this.replaceSql.apply(this, arguments)).chain(function (c, info) {- 9
return c.insertId;});},splitMultipleResultSets:function () {- 0
if (this.__opts.graph) {- 0
throw new QueryError("Can't split multiple statements on a graphed dataset");}- 0
var ds = this.mergeOptions({splitMultipleResultSets:true});- 0
var rowCb = this.rowCb;- 0
if (rowCb) {- 0
ds.rowCb = function (x) {- 0
return x.map(rowCb, this);};}- 0
return ds;},//Update the matching rows.update:function () {- 0
return this.executeDui(this.updateSql.apply(this, arguments)).chain(function (c, info) {- 0
return c.affectedRows;});},//Set the :type option to select if it hasn't been set.execute:function (sql, opts) {- 87
opts = opts || {};- 87
return this._super([sql, merge({type:"select"}, opts)]);},//Set the :type option to :select if it hasn't been set.executeDui:function (sql, opts) {- 59
opts = opts || {};- 59
return this._super([sql, merge({type:"dui"}, opts)]);},_literalString:function (v) {- 51
return "'" + v.replace(/[\0\n\r\t\\\'\"\x1a]/g, function (s) {- 1
switch (s) {case "0":- 0
return "\\0";case "\n":- 0
return "\\n";case "\r":- 0
return "\\r";case "\b":- 0
return "\\b";case "\t":- 0
return "\\t";case "\x1a":- 0
return "\\Z";default:- 1
return "\\" + s;}}) + "'";}},"static":{BOOL_TRUE:'1',BOOL_FALSE:'0',COMMA_SEPARATOR:', ',FOR_SHARE:' LOCK IN SHARE MODE',DELETE_CLAUSE_METHODS:Dataset.clauseMethods("delete", "qualify from where order limit"),INSERT_CLAUSE_METHODS:Dataset.clauseMethods("insert", "ignore into columns values onDuplicateKeyUpdate"),REPLACE_CLAUSE_METHODS:Dataset.clauseMethods("insert", "ignore into columns values onDuplicateKeyUpdate"),SELECT_CLAUSE_METHODS:Dataset.clauseMethods("select", "qualify distinct columns from join where group having compounds order limit lock"),UPDATE_CLAUSE_METHODS:Dataset.clauseMethods("update", "table set where order limit")}});- 1
var DB = define(Database, {instance:{PRIMARY:'PRIMARY',type:"mysql",__supportsSavePoints:true,__supportsTransactionIsolationLevels:true,createConnection:function (opts) {- 3
delete opts.query;- 3
var conn = mysql.createConnection(merge({}, opts, {typeCast:false}));//conn.useDatabase(opts.database)- 3
return new Connection(conn);},closeConnection:function (conn) {- 3
return conn.closeConnection();},validate:function (conn) {- 277
return new Promise().callback(true).promise();},execute:function (sql, opts, conn) {- 277
return when(conn || this._getConnection()).chain(function (conn) {- 277
return this.__execute(conn, sql, opts);}.bind(this));},__execute:function (conn, sql, opts, cb) {- 277
return this.__logAndExecute(sql, comb("query").bindIgnore(conn, sql)).both(comb("_returnConnection").bindIgnore(this, conn));},// MySQL's cast rules are restrictive in that you can't just cast to any possible// database type.castTypeLiteral:function (type) {- 0
var ret = null, meth;- 0
if (isString(type)) {- 0
ret = this._static.CAST_TYPES[type] || this._super(arguments);- 0
} else if (type === String) {- 0
meth += "CHAR";- 0
} else if (type === Number) {- 0
meth += "DECIMAL";- 0
} else if (type === DateTime) {- 0
meth += "DATETIME";- 0
} else if (type === Year) {- 0
meth += "Year";- 0
} else if (type === Time) {- 0
meth += "DATETIME";- 0
} else if (type === Double) {- 0
meth += "DECIMAL";} else {- 0
ret = this._super(arguments);}- 0
return ret;},// Use SHOW INDEX FROM to get the index information for the table.indexes:function (table, opts) {- 3
var indexes = {};- 3
var removeIndexes = [];- 3
var m = this.outputIdentifierFunc;- 3
var im = this.inputIdentifierFunc;- 3
var ret = new Promise();- 3
this.metadataDataset.withSql("SHOW INDEX FROM ?", isInstanceOf(table, sql.Identifier) ? table : sql.identifier(im(table))).forEach(function (r) {- 2
var name = r[m("Key_name")];- 2
if (name !== "PRIMARY") {- 2
name = m(name);- 2
if (r[m("Sub_part")]) {- 1
removeIndexes.push(name);}- 2
var i = indexes[name] || (indexes[name] = {columns:[], unique:r[m("Non_unique")] !== 1});- 2
i.columns.push(m(r[m("Column_name")]));}}).then(function () {- 3
var r = {};- 3
for (var i in indexes) {- 2
if (removeIndexes.indexOf(i) === -1) {- 1
r[i] = indexes[i];}}- 3
ret.callback(r);}, ret);- 3
return ret.promise();},// Get version of MySQL server, used for determined capabilities.serverVersion:function () {- 1
var ret = new Promise();- 1
if (!this.__serverVersion) {- 1
this.get(sql.version().sqlFunction).then(hitch(this, function (version) {- 1
var m = version.match(/(\d+)\.(\d+)\.(\d+)/);- 1
this._serverVersion = (parseInt(m[1], 10) * 10000) + (parseInt(m[2], 10) * 100) + parseInt(m[3], 10);- 1
ret.callback(this._serverVersion);}), ret);} else {- 0
ret.callback(this._serverVersion);}- 1
return ret.promise();},//Return an array of strings specifying table names in the current database.tables:function (opts) {- 0
var m = this.outputIdentifierFunc;- 0
return this.metadataDataset.withSql('SHOW TABLES').map(function (r) {- 0
return m(r[Object.keys(r)[0]]);});},use:function (dbName) {- 0
var ret = new Promise();- 0
this.disconnect().then(hitch(this, function () {- 0
this.run("USE " + dbName).then(hitch(this, function () {- 0
this.opts.database = dbName;- 0
this.schemas = {};- 0
ret.callback(this);}));}), ret);- 0
return ret.promise();},//Use MySQL specific syntax for rename column, set column type, and// drop index cases.__alterTableSql:function (table, op) {- 21
var ret = new Promise();- 21
if (op.op === "addColumn") {- 7
var related = op.table;- 7
if (related) {- 2
delete op.table;- 2
this._super(arguments).then(hitch(this, function (sql) {- 2
op.table = related;- 2
ret.callback([sql, format("ALTER TABLE %s ADD FOREIGN KEY (%s)%s",this.__quoteSchemaTable(table), this.__quoteIdentifier(op.name),this.__columnReferencesSql(op))]);}), ret);} else {- 5
ret = this._super(arguments);}- 14
} else if (['renameColumn', "setColumnType", "setColumnNull", "setColumnDefault"].indexOf(op.op) !== -1) {- 11
var o = op.op;- 11
this.schema(table).then(hitch(this, function (schema) {- 11
var name = op.name;- 11
var opts = schema[Object.keys(schema).filter(function (i) {- 28
return i === name;})[0]];- 11
opts = merge({}, opts || {});- 11
opts.name = op.newName || name;- 11
opts.type = op.type || opts.dbType;- 11
opts.allowNull = isUndefined(op["null"]) ? opts.allowNull : op["null"];- 11
opts["default"] = op["default"] || opts.jsDefault;- 11
if (isUndefinedOrNull(opts["default"])) {- 5
delete opts["default"];}- 11
ret.callback(format("ALTER TABLE %s CHANGE COLUMN %s %s", this.__quoteSchemaTable(table),this.__quoteIdentifier(op.name), this.__columnDefinitionSql(merge(op, opts))));}), ret);- 3
} else if (op.op === "dropIndex") {- 0
ret.callback(format("%s ON %s", this.__dropIndexSql(table, op), this.__quoteSchemaTable(table)));} else {- 3
ret = this._super(arguments);}- 21
return ret.promise();},//MySQL needs to set transaction isolation before beginning a transaction__beginNewTransaction:function (conn, opts) {- 9
return this.__setTransactionIsolation(conn, opts).chain(comb("__logConnectionExecute").bindIgnore(this, conn, this.beginTransactionSql));},// Use XA START to start a new prepared transaction if the :prepare//option is given.__beginTransaction:function (conn, opts) {- 9
opts = opts || {};- 9
var s;- 9
if ((s = opts.prepare)) {- 0
return this.__logConnectionExecute(conn, comb("XA START %s").format(this.literal(s)));} else {- 9
return this._super(arguments);}},// MySQL doesn't allow default values on text columns, so ignore if it the// generic text type is used__columnDefinitionSql:function (column) {- 144
if (isString(column.type) && column.type.match(/string/i) && column.text) {- 1
delete column["default"];}- 144
return this._super(arguments, [column]);},// Prepare the XA transaction for a two-phase commit if the// prepare option is given.__commitTransaction:function (conn, opts) {- 9
opts = opts || {};- 9
var s = opts.prepare;- 9
if (s) {- 0
return this.__logConnectionExecute(conn, comb("XA END %s").format(this.literal(s))).chain(comb("__logConnectionExecute").bindIgnore(this, comb("XA PREPARE %s").format(this.literal(s))))} else {- 9
return this._super(arguments);}},//Use MySQL specific syntax for engine type and character encoding__createTableSql:function (name, generator, options) {- 48
options = options || {};- 48
var engine = options.engine, charset = options.charset, collate = options.collate;- 48
if (isUndefined(engine)) {- 40
engine = this._static.defaultEngine;}- 48
if (isUndefined(charset)) {- 43
charset = this._static.defaultCharset;}- 48
if (isUndefined(collate)) {- 46
collate = this._static.defaultCollate;}- 48
generator.columns.forEach(function (c) {- 126
var t = c.table;- 126
if (t) {- 2
delete c.table;- 2
generator.foreignKey([c.name], t, merge({}, c, {name:null, type:"foreignKey"}));}});- 48
return format(" %s%s%s%s", this._super(arguments), engine ? " ENGINE=" + engine : "",charset ? " DEFAULT CHARSET=" + charset : "", collate ? " DEFAULT COLLATE=" + collate : "");},//Handle MySQL specific index SQL syntax__indexDefinitionSql:function (tableName, index) {- 7
var indexName = this.__quoteIdentifier(index.name || this.__defaultIndexName(tableName,index.columns)), t = index.type, using = "";- 7
var indexType = "";- 7
if (t === "fullText") {- 2
indexType = "FULLTEXT ";- 5
} else if (t === "spatial") {- 1
indexType = "SPATIAL ";} else {- 4
indexType = index.unique ? "UNIQUE " : "";- 4
using = t ? " USING " + t : "";}- 7
return format("CREATE %sINDEX %s%s ON %s %s", indexType, indexName, using,this.__quoteSchemaTable(tableName), this.literal(index.columns.map(function (c) {- 8
return isString(c) ? sql.identifier(c) : c;})));},// Rollback the currently open XA transaction__rollbackTransaction:function (conn, opts) {- 0
opts = opts || {};- 0
var s = opts.prepare;- 0
var logConnectionExecute = comb("__logConnectionExecute");- 0
if (s) {- 0
s = this.literal(s);- 0
return this.__logConnectionExecute(conn, "XA END " + s).chain(logConnectionExecute.bindIngore(this, conn, "XA PREPARE " + s)).chain(logConnectionExecute.bindIngore(this, conn, "XA ROLLBACK " + s))} else {- 0
return this._super(arguments);}},// MySQL treats integer primary keys as autoincrementing._schemaAutoincrementingPrimaryKey:function (schema) {- 0
return this._super(arguments) && schema.dbType.match(/int/i);},//Use the MySQL specific DESCRIBE syntax to get a table description.schemaParseTable:function (tableName, opts) {- 15
var m = this.outputIdentifierFunc;- 15
var im = this.inputIdentifierFunc;- 15
return this.metadataDataset.withSql("DESCRIBE ?", sql.identifier(im(tableName))).map(hitch(this,function (row) {- 39
var ret = {};- 39
var e = row[m("Extra")];- 39
var allowNull = row[m("Null")];- 39
var key = row[m("Key")];- 39
ret.autoIncrement = e.match(/auto_increment/i) !== null;- 39
ret.allowNull = allowNull.match(/Yes/i) !== null;- 39
ret.primaryKey = key.match(/PRI/i) !== null;- 39
var defaultValue = row[m("Default")];- 39
ret["default"] = Buffer.isBuffer(defaultValue) ? defaultValue.toString() : defaultValue;- 39
if (isEmpty(row["default"])) {- 39
row["default"] = null;}- 39
ret.dbType = row[m("Type")];- 39
if (Buffer.isBuffer(ret.dbType)) {//handle case for field type being returned at 252 (i.e. BLOB)- 39
ret.dbType = ret.dbType.toString();}- 39
ret.type = this.schemaColumnType(ret.dbType.toString("utf8"));- 39
var fieldName = m(row[m("Field")]);- 39
return [fieldName, ret];}));},//Convert tinyint(1) type to boolean if convert_tinyint_to_bool is trueschemaColumnType:function (dbType) {- 39
return this._static.convertTinyintToBool && dbType === 'tinyint(1)' ? "boolean" : this._super(arguments);},//MySQL doesn't have a true boolean class, so it uses tinyint(1)__typeLiteralGenericBoolean:function (column) {- 2
return 'tinyint(1)';},getters:{identifierInputMethodDefault:function () {- 1
return null;},identifierOutputMethodDefault:function () {- 1
return null;},connectionExecuteMethod:function () {- 18
return "query";},dataset:function () {- 176
return new DS(this);}}},"static":{__convertTinyintToBool:true,__convertInvalidDateTime:false,CAST_TYPES:{string:"CHAR", integer:"SIGNED", time:"DATETIME", datetime:"DATETIME", numeric:"DECIMAL"},AUTOINCREMENT:'AUTO_INCREMENT',init:function () {- 1
this.setAdapterType("mysql");},FIELD_TYPES:{FIELD_TYPE_DECIMAL:0x00,FIELD_TYPE_TINY:0x01,FIELD_TYPE_SHORT:0x02,FIELD_TYPE_LONG:0x03,FIELD_TYPE_FLOAT:0x04,FIELD_TYPE_DOUBLE:0x05,FIELD_TYPE_NULL:0x06,FIELD_TYPE_TIMESTAMP:0x07,FIELD_TYPE_LONGLONG:0x08,FIELD_TYPE_INT24:0x09,FIELD_TYPE_DATE:0x0a,FIELD_TYPE_TIME:0x0b,FIELD_TYPE_DATETIME:0x0c,FIELD_TYPE_YEAR:0x0d,FIELD_TYPE_NEWDATE:0x0e,FIELD_TYPE_VARCHAR:0x0f,FIELD_TYPE_BIT:0x10,FIELD_TYPE_NEWDECIMAL:0xf6,FIELD_TYPE_ENUM:0xf7,FIELD_TYPE_SET:0xf8,FIELD_TYPE_TINY_BLOB:0xf9,FIELD_TYPE_MEDIUM_BLOB:0xfa,FIELD_TYPE_LONG_BLOB:0xfb,FIELD_TYPE_BLOB:0xfc,FIELD_TYPE_VAR_STRING:0xfd,FIELD_TYPE_STRING:0xfe,FIELD_TYPE_GEOMETRY:0xff},convertMysqlType:function (type) {- 304
var convert = this.convertTinyintToBool, convertDateTime = this.__convertInvalidDateTime;- 304
if (!patio) {- 1
patio = require("../index");}- 304
return hitch(this.FIELD_TYPES, function (type, o) {- 468
var ret = o;- 468
if (o !== null) {- 347
switch (type) {case this.FIELD_TYPE_TIMESTAMP:case this.FIELD_TYPE_DATETIME:- 5
ret = convertDate(o, "stringToDateTime", convertDateTime);- 4
break;case this.FIELD_TYPE_DATE:case this.FIELD_TYPE_NEWDATE:- 5
ret = convertDate(o, "stringToDate", convertDateTime);- 4
break;case this.FIELD_TYPE_TIME:- 5
ret = convertDate(o, "stringToTime", convertDateTime);- 4
break;case this.FIELD_TYPE_TINY:- 3
ret = convert ? parseInt(o, 10) === 1 : parseInt(o, 10);- 3
break;case this.FIELD_TYPE_YEAR:- 0
ret = convertDate(o, "stringToYear", convertDateTime);- 0
break;case this.FIELD_TYPE_SHORT:case this.FIELD_TYPE_LONG:case this.FIELD_TYPE_LONGLONG:case this.FIELD_TYPE_INT24:- 68
ret = parseInt(o, 10);- 68
break;case this.FIELD_TYPE_FLOAT:case this.FIELD_TYPE_DOUBLE:case this.FIELD_TYPE_DECIMAL:// decimal types cannot be parsed as floats because// V8 Numbers have less precision than some MySQL Decimals- 1
ret = parseFloat(o);- 1
break;case this.FIELD_TYPE_TINY_BLOB:case this.FIELD_TYPE_MEDIUM_BLOB:case this.FIELD_TYPE_LONG_BLOB:case this.FIELD_TYPE_BLOB:- 68
ret = new Buffer(o);- 68
break;}}- 465
return ret;}, type);},getters:{convertTinyintToBool:function () {- 343
return this.__convertTinyintToBool;},convertInvalidDateTime:function () {- 0
return this.__convertInvalidDateTime;}},setters:{convertTinyintToBool:function (convert) {- 0
this.__convertTinyintToBool = convert;},convertInvalidDateTime:function (convert) {- 8
this.__convertInvalidDateTime = convert;}}}}).as(exports, "MySQLDatabase");
|
plugins/query.js
|
Coverage82.31
SLOC655
LOC130
Missed23
|
- 1
var comb = require("comb"),asyncArray = comb.async.array,when = comb.when,isBoolean = comb.isBoolean,isArray = comb.isArray,isHash = comb.isHash,isUndefined = comb.isUndefined,isInstanceOf = comb.isInstanceOf,isEmpty = comb.isEmpty,serial = comb.serial,Dataset = require("../dataset"),ModelError = require("../errors").ModelError,hitch = comb.hitch,hitchIgnore = comb.hitchIgnore,Promise = comb.Promise,PromiseList = comb.PromiseList;- 1
var QueryPlugin = comb.define(null, {instance:{/**@lends patio.Model.prototype*/_getPrimaryKeyQuery:function () {- 2027
var q = {}, pk = this.primaryKey;- 2027
for (var i = 0, l = pk.length; i < l; i++) {- 2027
var k = pk[i];- 2027
q[k] = this[k];}- 2027
return q;},_clearPrimaryKeys:function () {- 537
var pk = this.primaryKey;- 537
for (var i = 0, l = pk.length; i < l; i++) {- 537
this.__values[pk[i]] = null;}},reload:function () {- 15
if (this.synced) {- 15
return serial([this._hook.bind(this, "pre", "load"),this.__reload.bind(this),this._hook.bind(this, "post", "load")]).chain(this);} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},/*** Forces the reload of the data for a particular model instance. The Promise returned is resolved with the* model.** @example** myModel.reload().then(function(model){* //model === myModel* });** @return {comb.Promise} resolved with the model instance.*/__reload:function () {- 1319
if (!this.__isNew) {- 1319
return this.dataset.naked().filter(this._getPrimaryKeyQuery()).one().chain(function (values) {- 1319
this.__setFromDb(values, true);- 1319
return this;}.bind(this));} else {- 0
return when(this);}},/*** This method removes the instance of the model. If the model {@link patio.Model#isNew} then the promise is* resolved with a 0 indicating no rows were affected. Otherwise the model is removed, primary keys are cleared* and the model's isNew flag is set to true.** @example* myModel.remove().then(function(){* //model is deleted* assert.isTrue(myModel.isNew);* });** //dont use a transaction to remove this model* myModel.remove({transaction : false}).then(function(){* //model is deleted* assert.isTrue(myModel.isNew);* });** @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* removing the model.** @return {comb.Promise} called back after the deletion is successful.*/remove:function (options) {- 537
if (this.synced) {- 537
if (!this.__isNew) {- 537
return this._checkTransaction(options, hitch(this, function () {- 537
return serial([this._hook.bind(this, "pre", "remove", [options]),this._remove.bind(this, options),this._hook.bind(this, "post", "remove", [options]),function () {- 537
this._clearPrimaryKeys();- 537
this.__isNew = true;- 537
if (this._static.emitOnRemove) {- 537
this.emit("remove", this);- 537
this._static.emit("remove", this);}}.bind(this)]).chain(this);}));} else {- 0
return when(0);}} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},_remove:function () {- 531
return this.dataset.filter(this._getPrimaryKeyQuery()).remove();},/*** @private* Called after a save action to reload the model properties,* abstracted out so this can be overidden by sub classes*/_saveReload:function (options) {- 1146
options || (options = {});- 1146
var reload = isBoolean(options.reload) ? options.reload : this._static.reloadOnSave;- 1146
return reload ? this.__reload() : when(this);},/*** @private* Called after an update action to reload the model properties,* abstracted out so this can be overidden by sub classes*/_updateReload:function (options) {- 162
options = options || {};- 162
options || (options = {});- 162
var reload = isBoolean(options.reload) ? options.reload : this._static.reloadOnUpdate;- 162
return reload ? this.__reload() : when(this);},/*** Updates a model. This action checks if the model is not new and values have changed.* If the model is new then the {@link patio.Model#save} action is called.** When updating a model you can pass values you want set as the first argument.** {@code** someModel.update({* myVal1 : "newValue1",* myVal2 : "newValue2",* myVal3 : "newValue3"* }).then(function(){* //do something* }, errorHandler);** }** Or you can set values on the model directly** {@code** someModel.myVal1 = "newValue1";* someModel.myVal2 = "newValue2";* someModel.myVal3 = "newValue3";** //update model with current values* someModel.update().then(function(){* //do something* });** }** Update also accepts an options object as the second argument allowing the overriding of default behavior.** To override <code>useTransactions</code> you can set the <code>transaction</code> option.** {@code* someModel.update(null, {transaction : false});* }** You can also override the <code>reloadOnUpdate</code> property by setting the <code>reload</code> option.* {@code* someModel.update(null, {reload : false});* }** @param {Object} [vals] optional values hash to set on the model before saving.* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* updating the model.* @param {Boolean} [options.reload] boolean indicating if the model should be reloaded after the update. This will take* precedence over {@link patio.Model.reloadOnUpdate}** @return {comb.Promise} resolved when the update action has completed.*/update:function (vals, options) {- 158
if (this.synced) {- 158
if (!this.__isNew) {- 158
return this._checkTransaction(options, hitch(this, function () {- 158
if (isHash(vals)) {- 107
this.__set(vals);}- 158
var saveChange = !isEmpty(this.__changed);- 158
return serial([this._hook.bind(this, "pre", "update", [options]),function () {- 158
return saveChange ? this._update(options) : null;}.bind(this),this._hook.bind(this, "post", "update", [options]),this._updateReload.bind(this, options),function () {- 158
if (this._static.emitOnUpdate) {- 158
this.emit("update", this);- 158
this._static.emit("update", this);}}.bind(this)]).chain(this);}));- 0
} else if (this.__isNew && this.__isChanged) {- 0
return this.save(vals, options);} else {- 0
return when(this);}} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},_update:function (options) {- 139
var ret = this.dataset.filter(this._getPrimaryKeyQuery()).update(this.__changed);- 139
this.__changed = {};- 139
return ret;},/*** Saves a model. This action checks if the model is new and values have changed.* If the model is not new then the {@link patio.Model#update} action is called.** When saving a model you can pass values you want set as the first argument.** {@code** someModel.save({* myVal1 : "newValue1",* myVal2 : "newValue2",* myVal3 : "newValue3"* }).then(function(){* //do something* }, errorHandler);** }** Or you can set values on the model directly** {@code** someModel.myVal1 = "newValue1";* someModel.myVal2 = "newValue2";* someModel.myVal3 = "newValue3";** //update model with current values* someModel.save().then(function(){* //do something* });** }** Save also accepts an options object as the second argument allowing the overriding of default behavior.** To override <code>useTransactions</code> you can set the <code>transaction</code> option.** {@code* someModel.save(null, {transaction : false});* }** You can also override the <code>reloadOnSave</code> property by setting the <code>reload</code> option.* {@code* someModel.save(null, {reload : false});* }*** @param {Object} [vals] optional values hash to set on the model before saving.* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* saving the model.* @param {Boolean} [options.reload] boolean indicating if the model should be reloaded after the save. This will take* precedence over {@link patio.Model.reloadOnSave}** @return {comb.Promise} resolved when the save action has completed.*/save:function (vals, options) {- 1233
if (this.synced) {- 1233
if (this.__isNew) {- 1185
return this._checkTransaction(options, hitch(this, function () {- 1185
if (isHash(vals)) {- 0
this.__set(vals);}- 1185
return serial([this._hook.bind(this, "pre", "save", [options]),this._save.bind(this, options),this._hook.bind(this, "post", "save", [options]),this._saveReload.bind(this, options),function () {- 1141
if (this._static.emitOnSave) {- 1141
this.emit("save", this);- 1141
this._static.emit("save", this);}}.bind(this)]).chain(this);}));} else {- 48
return this.update(vals, options);}} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},_save:function (options) {- 1136
var pk = this._static.primaryKey[0];- 1136
return this.dataset.insert(this._toObject()).chain(function (id) {- 1136
this.__ignore = true;- 1136
if (id) {- 1136
this[pk] = id;}- 1136
this.__ignore = false;- 1136
this.__isNew = false;- 1136
this.__isChanged = false;- 1136
return this;}.bind(this));},getUpdateSql:function () {- 4
return this.updateDataset.filter(this._getPrimaryKeyQuery()).updateSql(this.__changed);},getInsertSql:function () {- 4
return this.insertDataset.insertSql(this._toObject());},getRemoveSql:function () {- 2
return this.removeDataset.filter(this._getPrimaryKeyQuery()).deleteSql;},getters:{updateSql:function () {- 4
return this.getUpdateSql();},insertSql:function () {- 4
return this.getInsertSql();},removeSql:function () {- 2
return this.getRemoveSql();},deleteSql:function () {- 1
return this.removeSql;}}},static:{/**@lends patio.Model*//*** Set to false to prevent the emitting on an event when a model is saved.* @default true*/emitOnSave:true,/*** Set to false to prevent the emitting on an event when a model is updated.* @default true*/emitOnUpdate:true,/*** Set to false to prevent the emitting on an event when a model is removed.* @default true*/emitOnRemove:true,/*** Retrieves a record by the primaryKey/s of a table.** @example** var User = patio.getModel("user");** User.findById(1).then(function(userOne){** });** //assume the primary key is a compostie of first_name and last_name* User.findById(["greg", "yukon"]).then(function(userOne){** });*** @param {*} id the primary key of the record to find.** @return {comb.Promise} called back with the record or null if one is not found.*/findById:function (id) {- 3
var pk = this.primaryKey;- 3
pk = pk.length == 1 ? pk[0] : pk;- 3
var q = {};- 3
if (isArray(id) && isArray(pk)) {- 0
if (id.length === pk.length) {- 0
pk.forEach(function (k, i) {- 0
q[k] = id[i];});} else {- 0
throw new ModelError("findById : ids length does not equal the primaryKeys length.");}} else {- 3
q[pk] = id;}- 3
return this.filter(q).one();},/*** Finds a single model according to the supplied filter.* See {@link patio.Dataset#filter} for filter options.**** @param id*/find:function (id) {- 0
return this.filter.apply(this, arguments).first();},/*** Finds a single model according to the supplied filter.* See {@link patio.Dataset#filter} for filter options. If the model* does not exist then a new one is created as passed back.* @param q*/findOrCreate:function (q) {- 0
return this.find(q).chain(hitch(this, function (res) {- 0
return res || this.create(q);}));},/*** Update multiple rows with a set of values.** @example* var User = patio.getModel("user");** //BEGIN* //UPDATE `user` SET `password` = NULL WHERE (`last_accessed` <= '2011-01-27')* //COMMIT* User.update({password : null}, function(){* return this.lastAccessed.lte(comb.date.add(new Date(), "year", -1));* });* //same as* User.update({password : null}, {lastAccess : {lte : comb.date.add(new Date(), "year", -1)}});** //UPDATE `user` SET `password` = NULL WHERE (`last_accessed` <= '2011-01-27')* User.update({password : null}, function(){* return this.lastAccessed.lte(comb.date.add(new Date(), "year", -1));* }, {transaction : false});** @param {Object} vals a hash of values to update. See {@link patio.Dataset#update}.* @param query a filter to apply to the UPDATE. See {@link patio.Dataset#filter}.** @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* updating the models.** @return {Promise} a promise that is resolved once the update statement has completed.*/update:function (vals, /*?object*/query, options) {- 2
options = options || {};- 2
var args = comb(arguments).toArray();- 2
return this._checkTransaction(options, hitch(this, function () {- 2
var dataset = this.dataset;- 2
if (!isUndefined(query)) {- 2
dataset = dataset.filter(query);}- 2
return dataset.update(vals);}));},/*** Remove(delete) models. This can be used to do a mass delete of models.** @example* var User = patio.getModel("user");** //remove all users* User.remove();** //remove all users who's names start with a.* User.remove({name : /A%/i});** //remove all users who's names start with a, without a transaction.* User.remove({name : /A%/i}, {transaction : false});** @param {Object} [q] query to filter the rows to remove. See {@link patio.Dataset#filter}.* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* removing the models.* @param {Boolean} [options.load=true] boolean set to prevent the loading of each model. This is more efficient* but the pre/post remove hooks not be notified of the deletion.** @return {comb.Promise} called back when the removal completes.*/remove:function (q, options) {- 377
options = options || {};- 377
var loadEach = isBoolean(options.load) ? options.load : true;//first find all records so we call alert the middleware for each model- 377
return this._checkTransaction(options, hitch(this, function () {- 377
var ds = this.dataset;- 377
ds = ds.filter.call(ds, q);- 377
if (loadEach) {- 377
return ds.map(function (r) {//todo this sucks find a better way!- 447
return r.remove(options);});} else {- 0
return ds.remove();}}));},/*** Similar to remove but takes an id or an array for a composite key.** @example** User.removeById(1);** @param id id or an array for a composite key, to find the model by* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* removing the model.** @return {comb.Promise} called back when the removal completes.*/removeById:function (id, options) {- 0
return this._checkTransaction(options, hitch(this, function () {- 0
return this.findById(id).chain(function (model) {- 0
if (model) {- 0
return model.remove(options);}});}));},/*** Save either a new model or list of models to the database.** @example* var Student = patio.getModel("student");* Student.save([* {* firstName:"Bob",* lastName:"Yukon",* gpa:3.689,* classYear:"Senior"* },* {* firstName:"Greg",* lastName:"Horn",* gpa:3.689,* classYear:"Sohpmore"* },* {* firstName:"Sara",* lastName:"Malloc",* gpa:4.0,* classYear:"Junior"* },* {* firstName:"John",* lastName:"Favre",* gpa:2.867,* classYear:"Junior"* },* {* firstName:"Kim",* lastName:"Bim",* gpa:2.24,* classYear:"Senior"* },* {* firstName:"Alex",* lastName:"Young",* gpa:1.9,* classYear:"Freshman"* }* ]).then(function(users){* //work with the users* });** Save a single record* MyModel.save(m1);** @param {patio.Model|Object|patio.Model[]|Object[]} record the record/s to save.* @param {Object} [options] additional options.* @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when* saving the models.** @return {comb.Promise} called back with the saved record/s.*/save:function (items, options) {- 40
options = options || {};- 40
var isArr = isArray(items);- 40
return this._checkTransaction(options, hitch(this, function () {- 40
return asyncArray(items).map(function (o) {- 537
if (!isInstanceOf(o, this)) {- 57
o = new this(o);}- 537
return o.save(null, options);}, this).chain(function (res) {- 40
return isArr ? res : res[0];});}));}}}).as(exports, "QueryPlugin");- 1
Dataset.ACTION_METHODS.concat(Dataset.QUERY_METHODS).forEach(function (m) {- 162
if (!QueryPlugin[m]) {- 98
QueryPlugin[m] = function () {- 358
if (this.synced) {- 358
var ds = this.dataset;- 358
return ds[m].apply(ds, arguments);} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}}}});
|
database/connect.js
|
Coverage83.67
SLOC154
LOC49
Missed8
|
- 1
var comb = require("comb"),define = comb.define,merge = comb.merge,isHash = comb.isHash,hitch = comb.hitch,isString = comb.isString,PromiseList = comb.PromiseList,format = comb.string.format,errors = require("../errors"),NotImplemented = errors.NotImplemented,URL = require("url"),DatabaseError = errors.DatabaseError;- 1
var ADAPTERS = {};- 1
var DB = define(null, {instance:{/**@lends patio.Database.prototype*//*** Disconnects the database closing all connections.* @return Promise a promise that is resolved once all the connections have been closed.*/disconnect:function () {- 41
return this.pool.endAll().then(this.onDisconnect.bind(this, this));},onDisconnect:function () {},/*** This is an abstract method that should be implemented by adapters to create* a connection to the database.* @param {Object} options options that are adapter specific.*/createConnection:function (options) {- 0
throw new NotImplemented("Create connection must be implemented by the adapter");},/*** This is an abstract method that should be implemented by adapters to close* a connection to the database.* @param conn the database connection to close.*/closeConnection:function (conn) {- 0
throw new NotImplemented("Close connection must be implemented by the adapter");},/*** Validates a connection before it is returned to the {@link patio.ConnectionPool}. This* method should be implemented by the adapter.* @param conn*/validate:function (conn) {- 0
throw new NotImplemented("Validate must be implemented by the adapter");},/*** @ignore*/getters:{uri:function () {/*** @ignore*/- 2
if (!this.opts.uri) {- 0
var opts = {protocol:this.type,hostname:this.opts.host,auth:format("{user}:{password}", this.opts),port:this.opts.port,pathname:"/" + this.opts.database};- 0
return URL.format(opts);}- 2
return this.opts.uri;},url:function () {- 1
return this.uri;}}},"static":{/**@lends patio.Database*//*** Creates a connection to a Database see {@link patio#createConnection}.*/connect:function (connectionString, opts) {- 41
opts = opts || {};- 41
if (isString(connectionString)) {- 41
var url = URL.parse(connectionString, true);- 41
if (url.auth) {- 40
var parts = url.auth.split(":");- 40
if (!opts.user) {- 40
opts.user = parts[0];}- 40
if (!opts.password) {- 40
opts.password = parts[1];}}- 41
opts.type = url.protocol.replace(":", "");- 41
opts.host = url.hostname;- 41
if (url.pathname) {- 41
var path = url.pathname;- 41
var pathParts = path.split("/").slice(1);- 41
if (pathParts.length >= 1) {- 41
opts.database = pathParts[0];}}- 41
opts = merge(opts, url.query, {uri:connectionString});} else {- 0
opts = merge({}, connectionString, opts);}- 41
if (opts && isHash(opts) && (opts.adapter || opts.type)) {- 41
var type = (opts.type = opts.adapter || opts.type);- 41
var Adapter = ADAPTERS[type];- 41
if (Adapter) {- 41
var adapter = new Adapter(opts);- 41
this.DATABASES.push(adapter);- 41
return adapter;} else {- 0
throw DatabaseError(type + " adapter was not found");}} else {- 0
throw new DatabaseError("Options required when connecting.");}},setAdapterType:function (type) {- 5
type = type.toLowerCase();- 5
this.type = type;- 5
ADAPTERS[type] = this;},disconnect:function (cb) {- 41
var dbs = this.DATABASES;- 41
var ret = new PromiseList(dbs.map(function (d) {- 40
return d.disconnect();}), true);- 41
dbs.length = 0;- 41
ret.classic(cb);- 41
return ret.promise();},ADAPTERS:ADAPTERS}}).as(module);- 1
DB.setAdapterType("default");
|
associations/_Association.js
|
Coverage83.75
SLOC522
LOC160
Missed26
|
- 1
var comb = require("comb-proxy"),define = comb.define,isUndefined = comb.isUndefined,isUndefinedOrNull = comb.isUndefinedOrNull,isBoolean = comb.isBoolean,isString = comb.isString,isHash = comb.isHash,when = comb.when,isFunction = comb.isFunction,isInstanceOf = comb.isInstanceOf,Promise = comb.Promise,PromiseList = comb.PromiseList,hitch = comb.hitch,array = comb.array,toArray = array.toArray,isArray = comb.isArray,Middleware = comb.plugins.Middleware,PatioError = require("../errors").PatioError;- 1
var fetch = {LAZY:"lazy",EAGER:"eager"};/*** @class* Base class for all associations.** </br>* <b>NOT to be instantiated directly</b>* Its just documented for reference.** @constructs* @param {Object} options* @param {String} options.model a string to look up the model that we are associated with* @param {Function} options.filter a callback to find association if a filter is defined then* the association is read only* @param {Object} options.key object with left key and right key* @param {String|Object} options.orderBy<String|Object> - how to order our association @see Dataset.order* @param {fetch.EAGER|fetch.LAZY} options.fetchType the fetch type of the model if fetch.Eager is supplied then* the associations are automatically filled, if fetch.Lazy is supplied* then a promise is returned and is called back with the loaded models* @property {Model} model the model associatied with this association.* @name Association* @memberOf patio.associations* */- 1
define(Middleware, {instance:{/**@lends patio.associations.Association.prototype*/type:"",//Our associated model_model:null,/*** Fetch type*/fetchType:fetch.LAZY,/**how to order our association*/orderBy:null,/**Our filter method*/filter:null,__hooks:null,isOwner:true,createSetter:true,isCascading:false,supportsStringKey:true,supportsHashKey:true,supportsCompositeKey:true,supportsLeftAndRightKey:true,/****Method to call to look up association,*called after the model has been filtered**/_fetchMethod:"all",constructor:function (options, patio, filter) {- 55
options = options || {};- 55
if (!options.model) {- 0
throw new Error("Model is required for " + this.type + " association");}- 55
this._model = options.model;- 55
this.patio = patio;- 55
this.__opts = options;- 55
!isUndefined(options.isCascading) && (this.isCascading = options.isCascading);- 55
this.filter = filter;- 55
this.readOnly = isBoolean(options.readOnly) ? options.readOnly : false;- 55
this.__hooks ={before:{add:null, remove:null, "set":null, load:null}, after:{add:null, remove:null, "set":null, load:null}};- 55
var hooks = ["Add", "Remove", "Set", "Load"];- 55
["before", "after"].forEach(function (h) {- 110
hooks.forEach(function (a) {- 440
var hookName = h + a, hook;- 440
if (isFunction((hook = options[hookName]))) {- 0
this.__hooks[h][a.toLowerCase()] = hook;}}, this);}, this);- 55
this.fetchType = options.fetchType || fetch.LAZY;},_callHook:function (hook, action, args) {- 0
var func = this.__hooks[hook][action], ret;- 0
if (isFunction(func)) {- 0
ret = func.apply(this, args);}- 0
return ret;},_clearAssociations:function (model) {- 126
if (!this.readOnly) {- 126
delete model.__associations[this.name];}},_forceReloadAssociations:function (model) {- 243
if (!this.readOnly) {- 243
delete model.__associations[this.name];- 243
return model[this.name];}},/*** @return {Boolean} true if the association is eager.*/isEager:function () {- 2336
return this.fetchType === fetch.EAGER;},_checkAssociationKey:function (parent) {- 802
var q = {};- 802
this._setAssociationKeys(parent, q);- 802
return Object.keys(q).every(function (k) {- 802
return q[k] !== null;});},_getAssociationKey:function () {- 5265
var options = this.__opts, key, ret = [], lk, rk;- 5265
if (!isUndefinedOrNull((key = options.key))) {- 735
if (this.supportsStringKey && isString(key)) {//normalize the key first!- 369
ret = [[this.isOwner ? this.defaultLeftKey : key],[this.isOwner ? key : this.defaultRightKey]];- 366
} else if (this.supportsHashKey && isHash(key)) {- 366
var leftKey = Object.keys(key)[0];- 366
var rightKey = key[leftKey];- 366
ret = [[leftKey],[rightKey]];- 0
} else if (this.supportsCompositeKey && isArray(key)) {- 0
ret = [[key],null];}- 4530
} else if (this.supportsLeftAndRightKey && (!isUndefinedOrNull((lk = options.leftKey)) && !isUndefinedOrNull((rk = options.rightKey)))) {- 140
ret = [toArray(lk), toArray(rk)];} else {//todo handle composite primary keys- 4390
ret = [[this.defaultLeftKey],[this.defaultRightKey]];}- 5265
return ret;},_setAssociationKeys:function (parent, model, val) {- 1600
var keys = this._getAssociationKey(parent), leftKey = keys[0], rightKey = keys[1], i = leftKey.length - 1;- 1600
if (leftKey && rightKey) {- 1600
for (; i >= 0; i--) {- 1600
model[rightKey[i]] = !isUndefined(val) ? val : parent[leftKey[i]] ? parent[leftKey[i]] : null;}} else {- 0
for (; i >= 0; i--) {- 0
var k = leftKey[i];- 0
model[k] = !isUndefined(val) ? val : parent[k] ? parent[k] : null;}}},_setDatasetOptions:function (ds) {- 988
var options = this.__opts || {};- 988
var order, limit, distinct, select, query;//allow for multi key ordering- 988
if (!isUndefined((select = this.select))) {- 0
ds = ds.select.apply(ds, toArray(select));}- 988
if (!isUndefined((query = options.query)) || !isUndefined((query = options.conditions))) {- 0
ds = ds.filter(query);}- 988
if (isFunction(this.filter)) {- 334
var ret = this.filter.apply(this, [ds]);- 334
if (isInstanceOf(ret, ds._static)) {- 334
ds = ret;}}- 988
if (!isUndefined((distinct = options.distinct))) {- 0
ds = ds.limit.apply(ds, toArray(distinct));}- 988
if (!isUndefined((order = options.orderBy)) || !isUndefined((order = options.order))) {- 0
ds = ds.order.apply(ds, toArray(order));}- 988
if (!isUndefined((limit = options.limit))) {- 0
ds = ds.limit.apply(ds, toArray(limit));}- 988
return ds;},/***Filters our associated dataset to load our association.**@return {Dataset} the dataset with all filters applied.**/_filter:function (parent) {- 660
var options = this.__opts || {};- 660
var ds;- 660
if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {- 0
ds = ds.apply(parent, [parent]);}- 660
if (!ds) {- 660
var q = {};- 660
this._setAssociationKeys(parent, q);- 660
ds = this.model.dataset.naked().filter(q);- 660
var recip = this.model._findAssociation(this);- 660
recip && (recip = recip[1]);- 660
ds.rowCb = hitch(this, function (item) {- 505
var ret = new Promise();- 505
var model = this._toModel(item, true);- 505
recip && recip.__setValue(model, parent);//call hook to finish other model associations- 505
return model._hook("post", "load").chain(model);});- 0
} else if (!ds.rowCb && this.model) {- 0
ds.rowCb = hitch(this, function (item) {- 0
var model = this._toModel(item, true);//call hook to finish other model associations- 0
return model._hook("post", "load").chain(model);});}- 660
return this._setDatasetOptions(ds);},__setValue:function (parent, model) {- 2092
parent.__associations[this.name] = this._fetchMethod=== "all" ? !isArray(model) ? [model] : model : isArray(model) ? model[0] : model;- 2092
return parent.__associations[this.name];},fetch:function (parent) {- 802
var ret = new Promise();- 802
if (this._checkAssociationKey(parent)) {- 747
return this._filter(parent)[this._fetchMethod]().chain(function (result) {- 747
this.__setValue(parent, result);- 747
parent = null;- 747
return result;}.bind(this));} else {- 55
this.__setValue(parent, null);- 55
ret.callback(null);}- 55
return ret;},/*** Middleware called before a model is removed.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being acted up.*/_preRemove:function (next, model) {- 224
if (this.isOwner && !this.isCascading) {- 46
var q = {};- 46
this._setAssociationKeys(model, q, null);- 46
model[this.associatedDatasetName].update(q).classic(next);} else {- 178
next();}},/*** Middleware called aft era model is removed.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_postRemove:function (next, model) {- 516
next();},/*** Middleware called before a model is saved.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_preSave:function (next, model) {- 325
next();},/*** Middleware called after a model is saved.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_postSave:function (next, model) {- 0
next();},/*** Middleware called before a model is updated.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_preUpdate:function (next, model) {- 2
next();},/*** Middleware called before a model is updated.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_postUpdate:function (next, model) {- 129
next();},/*** Middleware called before a model is loaded.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_preLoad:function (next, model) {- 1588
next();},/*** Middleware called after a model is loaded.* </br>* <b> This is called in the scope of the model</b>* @param {Function} next function to pass control up the middleware stack.* @param {_Association} self reference to the Association that is being called.*/_postLoad:function (next, model) {- 0
next();},/*** Alias used to explicitly set an association on a model.* @param {*} val the value to set the association to* @param {_Association} self reference to the Association that is being called.*/_setter:function (val, model) {- 0
model.__associations[this.name] = val;},associationLoaded:function (model) {- 2795
return model.__associations.hasOwnProperty(this.name);},getAssociation:function (model) {- 891
return model.__associations[this.name];},/*** Alias used to explicitly get an association on a model.* @param {_Association} self reference to the Association that is being called.*/_getter:function (model) {//if we have them return them;- 616
if (this.associationLoaded(model)) {- 224
var assoc = this.getAssociation(model);- 224
return this.isEager() ? assoc : when(assoc)- 392
} else if (model.isNew) {- 0
return null;} else {- 392
return this.fetch(model);}},_toModel:function (val, fromDb) {- 1428
var Model = this.model;- 1428
if (!isUndefinedOrNull(Model)) {- 1428
if (!isInstanceOf(val, Model)) {- 1183
val = new this.model(val, fromDb);}} else {- 0
throw new PatioError("Invalid model " + this.name);}- 1428
return val;},/*** Method to inject functionality into a model. This method alters the model* to prepare it for associations, and initializes all required middleware calls* to fulfill requirements needed to loaded the associations.** @param {Model} parent the model that is having an associtaion set on it.* @param {String} name the name of the association.*/inject:function (parent, name) {- 55
this.name = name;- 55
var self = this;- 55
this.parent = parent;- 55
var parentProto = parent.prototype;- 55
parentProto.__defineGetter__(name, function () {- 616
return self._getter(this);});- 55
parentProto.__defineGetter__(this.associatedDatasetName, function () {- 146
return self._filter(this);});- 55
if (!this.readOnly && this.createSetter) {//define a setter because we arent read only- 55
parentProto.__defineSetter__(name, function (vals) {- 139
self._setter(vals, this);});}//set up all callbacks- 55
["pre", "post"].forEach(function (op) {- 110
["save", "update", "remove", "load"].forEach(function (type) {- 440
parent[op](type, function (next) {- 5646
return self["_" + op + type.charAt(0).toUpperCase() + type.slice(1)](next, this);});}, this);}, this);},getters:{select:function () {- 660
return this.__opts.select;},defaultLeftKey:function () {- 2882
var ret = "";- 2882
if (this.isOwner) {- 2107
ret = this.__opts.primaryKey || this.parent.primaryKey[0]} else {- 775
ret = this.model.tableName + "Id";}- 2882
return ret;},defaultRightKey:function () {- 2769
return this.associatedModelKey;},//Returns our modelmodel:function () {- 3023
return this.__model__ || (this.__model__ = this.patio.getModel(this._model, this.parent.db));},associatedModelKey:function () {- 2769
var ret = "";- 2769
if (this.isOwner) {- 1866
ret = this.__opts.primaryKey || this.parent.tableName + "Id";} else {- 903
ret = this.model.primaryKey[0];}- 2769
return ret;},associatedDatasetName:function () {- 201
return this.name + "Dataset";},removeAssociationFlagName:function () {- 27
return "__remove" + this.name + "association";}}},static:{/**@lends patio.associations.Association*/fetch:{LAZY:"lazy",EAGER:"eager"}}}).as(module);
|
associations/manyToMany.js
|
Coverage85.71
SLOC304
LOC133
Missed19
|
- 1
var comb = require("comb-proxy"),define = comb.define,isUndefined = comb.isUndefined,isUndefinedOrNull = comb.isUndefinedOrNull,isFunction = comb.isFunction,isInstanceOf = comb.isInstanceOf,sql = require("../sql").sql,hitch = comb.hitch,array = comb.array,isBoolean = comb.isBoolean,serial = comb.serial,when = comb.when,zip = array.zip,Promise = comb.Promise,PromiseList = comb.PromiseList,hitchIgnore = comb.hitchIgnore,OneToMany = require("./oneToMany"),pluralize = comb.pluralize,AssociationError = require("../errors").AssociationError;- 1
var LOGGER = comb.logger("comb.associations.ManyToMany");/*** @class Class to define a manyToMany association.** </br>* <b>NOT to be instantiated directly</b>* Its just documented for reference.** @name ManyToMany* @augments patio.associations.OneToMany* @memberOf patio.associations** @param {String} options.joinTable the joinTable of the association.*** @property {String} joinTable the join table used in the relation.* */- 1
module.exports = define(OneToMany, {instance:{/**@lends patio.associations.ManyToMany.prototype*/type:"manyToMany",_fetchMethod:"all",supportsStringKey:false,supportsCompositeKey:false,_filter:function (parent) {- 328
var keys = this._getAssociationKey(parent);- 328
var options = this.__opts || {};- 328
var ds;- 328
if (!isUndefined((ds = options.dataset)) && isFunction(ds)) {- 0
ds = ds.apply(parent, [parent]);}- 328
if (!ds) {- 328
ds = this.model.dataset.naked().innerJoin(this.joinTableName, zip(keys[1], this.modelPrimaryKey.map(function (k) {- 328
return sql.stringToIdentifier(k);})).concat(zip(keys[0], this.parentPrimaryKey.map(function (k) {- 328
return parent[k];}))));- 328
var recip = this.model._findAssociation(this);- 328
if (recip) {- 328
recip = recip[1];}- 328
ds.rowCb = hitch(this, function (item) {- 340
var model = this._toModel(item, true);- 340
if (recip) {- 340
recip.__setValue(model, parent);}//call hook to finish other model associations- 340
return model._hook("post", "load").chain(model);});- 0
} else if (!ds.rowCb && this.model) {- 0
ds.rowCb = hitch(this, function (item) {- 0
var model = this._toModel(item, true);//call hook to finish other model associations- 0
return model._hook("post", "load").chain(model);});}- 328
return this._setDatasetOptions(ds);},_setAssociationKeys:function (parent, model, val) {- 537
var keys = this._getAssociationKey(parent),leftKey = keys[0],parentPk = this.parentPrimaryKey;- 537
if (!(leftKey && leftKey.length === parentPk.length)) {- 0
throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);}- 537
for (var i = 0; i < leftKey.length; i++) {- 537
model[leftKey[i]] = !isUndefined(val) ? val : parent[parentPk[i]];}},__createJoinTableInsertRemoveQuery:function (model, item) {- 176
var q = {};- 176
var keys = this._getAssociationKey(model),leftKey = keys[0],rightKey = keys[1],parentPk = this.parentPrimaryKey,modelPk = this.modelPrimaryKey;- 176
if (!(leftKey && leftKey.length === parentPk.length)) {- 0
throw new AssociationError("Invalid leftKey for " + this.name + " : " + leftKey);}- 176
if (!(rightKey && rightKey.length === modelPk.length)) {- 0
throw new AssociationError("Invalid rightKey for " + this.name + " : " + rightKey);}- 176
for (var i = 0; i < leftKey.length; i++) {- 176
q[leftKey[i]] = model[parentPk[i]];}- 176
for (i = 0; i < rightKey.length; i++) {- 176
q[rightKey[i]] = item[modelPk[i]];}- 176
return q;},_preRemove:function (next, model) {- 203
if (this.isOwner && !this.isCascading) {- 203
var q = {};- 203
this._setAssociationKeys(model, q);- 203
this.joinTable.where(q).remove().classic(next);} else {- 0
next();}},addAssociation:function (item, model, reload) {- 260
reload = isBoolean(reload) ? reload : false;- 260
var ret = new Promise().callback(model);- 260
if (!isUndefinedOrNull(item)) {- 260
if (!model.isNew) {- 148
item = this._toModel(item);- 148
var loaded = this.associationLoaded(model);- 148
var recip = this.model._findAssociation(this), save = item.isNew;- 148
ret = model._checkTransaction(hitch(this, function () {- 148
var joinTable = this.joinTable, ret = new Promise();- 148
return serial([function () {- 148
return save ? item.save() : null;},function () {- 148
return joinTable.insert(this.__createJoinTableInsertRemoveQuery(model, item));}.bind(this),function () {- 148
if (recip) {- 148
recip[1].__setValue(item, [model]);}},function () {- 148
if (loaded && reload) {- 3
return this.parent._reloadAssociationsForType(this.type, this.model, model);} else {- 145
return model;}}.bind(this)]).chain(model);}));} else {- 112
item = this._toModel(item);- 112
var items = this.getAssociation(model);- 112
if (isUndefinedOrNull(items)) {- 39
this.__setValue(model, [item]);} else {- 73
items.push(item);}}}- 260
return ret.promise();},removeItem:function (item, model, remove, reload) {- 56
reload = isBoolean(reload) ? reload : false;- 56
remove = isBoolean(remove) ? remove : false;- 56
if (!isUndefinedOrNull(item)) {- 56
if (!model.isNew) {- 56
if (isInstanceOf(item, this.model) && !item.isNew) {- 56
var loaded = this.associationLoaded(model);- 56
remove = remove && !item.isNew;- 56
return model._checkTransaction(hitch(this, function () {- 56
return remove ? item.remove() : this.joinTable.where(this.__createJoinTableInsertRemoveQuery(model, item)).remove();})).chain(hitch(this, function () {- 56
if (loaded && reload) {- 18
return this.parent._reloadAssociationsForType(this.type, this.model, model);}})).chain(model);}} else {- 0
item = this._toModel(item);- 0
var items = this.getAssociation(model), index;- 0
if (!isUndefinedOrNull(items) && (index = items.indexOf(item)) !== -1) {- 0
items.splice(index, 1);}- 0
return when(model);}}},removeAllItems:function (model, remove) {- 4
remove = isBoolean(remove) ? remove : false;- 4
var ret = new Promise();- 4
if (!model.isNew) {- 4
var q = {}, removeQ = {};- 4
this._setAssociationKeys(model, q);- 4
this._setAssociationKeys(model, removeQ, null);- 4
var loaded = this.associationLoaded(model);- 4
return model._checkTransaction(hitch(this, function () {- 4
var ds = this.model.dataset, ret = new Promise();- 4
when(remove ? this._filter(model).forEach(function (m) {- 6
return m.remove();}) : this.joinTable.filter(q).update(removeQ)).then(function () {- 4
if (loaded) {- 2
this.parent._reloadAssociationsForType(this.type, this.model, model).then(hitchIgnore(ret, "callback", model), ret);} else {- 2
ret.callback(model);}}.bind(this), ret);- 4
return ret.promise();}));} else {//todo we may want to check if any of the items were previously saved items;- 0
this._clearAssociations(model);- 0
ret.callback(model);}- 0
return ret.promise();},getters:{select:function () {- 328
return this.__select;},defaultLeftKey:function () {- 1749
return this.__opts.leftKey || this.parent.tableName + "Id";},defaultRightKey:function () {- 1749
return this.__opts.rightKey || this.model.tableName + "Id";},parentPrimaryKey:function () {- 1041
return this.__opts.leftPrimaryKey || this.parent.primaryKey;},modelPrimaryKey:function () {- 504
return this.__opts.rightPrimaryKey || this.model.primaryKey;},joinTableName:function () {- 346
if (!this._joinTable) {- 18
var options = this.__opts;- 18
var joinTable = options.joinTable;- 18
if (isUndefined(joinTable)) {- 18
var defaultJoinTable = this.defaultJoinTable;- 18
if (isUndefined(defaultJoinTable)) {- 0
throw new Error("Unable to determine jointable for " + this.name);} else {- 18
this._joinTable = defaultJoinTable;}} else {- 0
this._joinTable = joinTable;}}- 346
return this._joinTable;},//returns our join table modeljoinTable:function () {- 381
if (!this.__joinTableDataset) {- 18
var ds = this.__joinTableDataset = this.model.dataset.db.from(this.joinTableName), model = this.model, options = this.__opts;- 18
var identifierInputMethod = isUndefined(options.identifierInputMethod) ? model.identifierInputMethod : options.identifierInputMethod,identifierOutputMethod = isUndefined(options.identifierOutputMethod) ? model.identifierOutputMethod : options.identifierOutputMethod;- 18
if (identifierInputMethod) {- 14
ds.identifierInputMethod = identifierInputMethod;}- 18
if (identifierOutputMethod) {- 14
ds.identifierOutputMethod = identifierOutputMethod;}}- 381
return this.__joinTableDataset;},defaultJoinTable:function () {- 18
var ret;- 18
var recip = this.model._findAssociation(this);- 18
if (recip && recip.length) {- 18
var names = [pluralize(this._model), pluralize(recip[1]._model)].sort();- 18
names[1] = names[1].charAt(0).toUpperCase() + names[1].substr(1);- 18
ret = names.join("");}- 18
return ret;}}}});
|
dataset/index.js
|
Coverage86.11
SLOC457
LOC72
Missed10
|
- 1
var comb = require("comb"),hitch = comb.hitch,logging = comb.logging,Logger = logging.Logger,errors = require("../errors"),QueryError = errors.QueryError,DatasetError = errors.DatasetError,Promise = comb.Promise,PromiseList = comb.PromiseList,isUndefined = comb.isUndefined,isUndefinedOrNull = comb.isUndefinedOrNull,isString = comb.isString,isInstanceOf = comb.isInstanceOf,isString = comb.isString,isFunction = comb.isFunction,isNull = comb.isNull,merge = comb.merge,define = comb.define,graph = require("./graph"),actions = require("./actions"),features = require("./features"),query = require("./query"),sql = require("./sql"),SQL = require("../sql").sql,AliasedExpression = SQL.AliasedExpression,Identifier = SQL.Identifier,QualifiedIdentifier = SQL.QualifiedIdentifier;- 1
var LOGGER = comb.logger("patio.Dataset");- 1
define([actions, graph, features, query, sql], {instance:{/**@lends patio.Dataset.prototype*//*** Class that is used for querying/retirving datasets from a database.** <p> Dynamically genertated methods include* <ul>* <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and* {@link patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call* to {@link patio.Dataset#joinTable}, so to invoke include all arguments that* {@link patio.Dataset#joinTable} requires except the type parameter. The default list includes.* <ul>* <li>Conditioned join types that accept conditions.* <ul>* <li>inner - INNER JOIN</li>* <li>fullOuter - FULL OUTER</li>* <li>rightOuter - RIGHT OUTER JOIN</li>* <li>leftOuter - LEFT OUTER JOIN</li>* <li>full - FULL JOIN</li>* <li>right - RIGHT JOIN</li>* <li>left - LEFT JOIN</li>* </ul>* </li>* <li>Unconditioned join types that do not accept join conditions* <ul>* <li>natural - NATURAL JOIN</li>* <li>naturalLeft - NATURAL LEFT JOIN</li>* <li>naturalRight - NATURAL RIGHT JOIN</li>* <li>naturalFull - NATURA FULLL JOIN</li>* <li>cross - CROSS JOIN</li>* </ul>* </li>* </ul>* </li>* </li>* </ul>** <p>* <h4>Features:</h4>* <p>* Features that a particular {@link patio.Dataset} supports are shown in the example below.* If you wish to implement an adapter please override these values depending on the database that* you are developing the adapter for.* </p>* <pre class="code">* var ds = DB.from("test");** //The default values returned** //Whether this dataset quotes identifiers.* //Whether this dataset quotes identifiers.* ds.quoteIdentifiers //=>true** //Whether this dataset will provide accurate number of rows matched for* //delete and update statements. Accurate in this case is the number of* //rows matched by the dataset's filter.* ds.providesAccurateRowsMatched; //=>true** //Times Whether the dataset requires SQL standard datetimes (false by default,* // as most allow strings with ISO 8601 format).* ds.requiresSqlStandardDate; //=>false** //Whether the dataset supports common table expressions (the WITH clause).* ds.supportsCte; //=>true** //Whether the dataset supports the DISTINCT ON clause, false by default.* ds.supportsDistinctOn; //=>false** //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.* ds.supportsIntersectExcept; //=>true** //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default* ds.supportsIntersectExceptAll; //=>true** //Whether the dataset supports the IS TRUE syntax.* ds.supportsIsTrue; //=>true** //Whether the dataset supports the JOIN table USING (column1, ...) syntax.* ds.supportsJoinUsing; //=>true** //Whether modifying joined datasets is supported.* ds.supportsModifyingJoin; //=>false** //Whether the IN/NOT IN operators support multiple columns when an* ds.supportsMultipleColumnIn; //=>true** //Whether the dataset supports timezones in literal timestamps* ds.supportsTimestampTimezone; //=>false** //Whether the dataset supports fractional seconds in literal timestamps* ds.supportsTimestampUsecs; //=>true** //Whether the dataset supports window functions.* ds.supportsWindowFunctions; //=>false* </pre>* <p>* <p>* <h4>Actions</h4>* <p>* Each dataset does not actually send any query to the database until an action method has* been called upon it(with the exception of {@link patio.Dataset#graph} because columns* from the other table might need retrived in order to set up the graph). Each action* returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important* that you account for errors otherwise it can be difficult to track down issues.* The list of action methods is:* <ul>* <li>{@link patio.Dataset#all}</li>* <li>{@link patio.Dataset#one}</li>* <li>{@link patio.Dataset#avg}</li>* <li>{@link patio.Dataset#count}</li>* <li>{@link patio.Dataset#columns}</li>* <li>{@link patio.Dataset#remove}</li>* <li>{@link patio.Dataset#forEach}</li>* <li>{@link patio.Dataset#empty}</li>* <li>{@link patio.Dataset#first}</li>* <li>{@link patio.Dataset#get}</li>* <li>{@link patio.Dataset#import}</li>* <li>{@link patio.Dataset#insert}</li>* <li>{@link patio.Dataset#save}</li>* <li>{@link patio.Dataset#insertMultiple}</li>* <li>{@link patio.Dataset#saveMultiple}</li>* <li>{@link patio.Dataset#interval}</li>* <li>{@link patio.Dataset#last}</li>* <li>{@link patio.Dataset#map}</li>* <li>{@link patio.Dataset#max}</li>* <li>{@link patio.Dataset#min}</li>* <li>{@link patio.Dataset#multiInsert}</li>* <li>{@link patio.Dataset#range}</li>* <li>{@link patio.Dataset#selectHash}</li>* <li>{@link patio.Dataset#selectMap}</li>* <li>{@link patio.Dataset#selectOrderMap}</li>* <li>{@link patio.Dataset#set}</li>* <li>{@link patio.Dataset#singleRecord}</li>* <li>{@link patio.Dataset#singleValue}</li>* <li>{@link patio.Dataset#sum}</li>* <li>{@link patio.Dataset#toCsv}</li>* <li>{@link patio.Dataset#toHash}</li>* <li>{@link patio.Dataset#truncate}</li>* <li>{@link patio.Dataset#update}</li>* </ul>** </p>* </p>** @constructs*** @param {patio.Database} db the database this dataset should use when querying for data.* @param {Object} opts options to set on this dataset instance** @property {Function} rowCb callback to be invoked for each row returned from the database.* the return value will be used as the result of query. The rowCb can also return a promise,* The resolved value of the promise will be used as result.** @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.* This value will be defaulted to whatever the identifierInputMethod* is on the database used in initialization.** @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.* This value will be defaulted to whatever the identifierOutputMethod* is on the database used in initialization.* @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.* throws a {patio.DatasetError} tf the dataset doesn't have a table.* <pre class="code">* DB.from("table").firstSourceAlias;* //=> "table"** DB.from("table___t").firstSourceAlias;* //=> "t"* </pre>** @property {String} firstSourceTable The first source (primary table) for this dataset. If the dataset doesn't* have a table, raises a {@link patio.erros.DatasetError}.*<pre class="code">** DB.from("table").firstSourceTable;* //=> "table"** DB.from("table___t").firstSourceTable;* //=> "t"* </pre>* @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.* <pre class="code">* DB.from("items").isSimpleSelectAll; //=> true* DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false* </pre>* @property {boolean} [quoteIdentifiers=true] Whether this dataset quotes identifiers.* @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for* delete and update statements. Accurate in this case is the number of* rows matched by the dataset's filter.* @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,* as most allow strings with ISO 8601 format).* @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).* @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.* @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.* @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.* @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.* @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.* @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.* @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an* @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps* @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps* @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.* @property {patio.sql.Identifier[]} [sourceList=[]] a list of sources for this dataset.* @property {patio.sql.Identifier[]} [joinSourceList=[]] a list of join sources* @property {Boolean} hasSelectSource true if this dataset already has a select sources.*/constructor:function (db, opts) {- 28756
this._super(arguments);- 28756
this.db = db;- 28756
this.__opts = {};- 28756
this.__rowCb = null;- 28756
if (db) {- 14960
this.__quoteIdentifiers = db.quoteIdentifiers;- 14960
this.__identifierInputMethod = db.identifierInputMethod;- 14960
this.__identifierOutputMethod = db.identifierOutputMethod;}},/*** Returns a new clone of the dataset with with the given options merged into the current datasets options.* If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached* columns are deleted. This method should generally not be called* directly by user code.** @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.* @return [patio.Dataset] a cloned dataset with the merged options**/mergeOptions:function (opts) {- 14971
opts = isUndefined(opts) ? {} : opts;- 14971
var ds = new this._static(this.db, {});- 14971
ds.rowCb = this.rowCb;- 14971
this._static.FEATURES.forEach(function (f) {- 209594
ds[f] = this[f];}, this);- 14971
var dsOpts = ds.__opts = merge({}, this.__opts, opts);- 14971
ds.identifierInputMethod = this.identifierInputMethod;- 14971
ds.identifierOutputMethod = this.identifierOutputMethod;- 14971
var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;- 14971
if (Object.keys(opts).some(function (o) {- 13535
return columnChangeOpts.indexOf(o) !== -1;})) {- 2359
dsOpts.columns = null;}- 14971
return ds;},/*** Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},* or {@link patio.sql.AliasedExpression}, depending on the format:** <ul>* <li>For columns : table__column___alias.</li>* <li>For tables : schema__table___alias.</li>* </ul>* each portion of the identifier is optional. See example below** @example** ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");* ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);* ds.stringToIdentifier("table__column___alias");* //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);** @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},* or {@link patio.sql.AliasedExpression}.** @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.*/stringToIdentifier:function (name) {- 14544
if (isString(name)) {- 9701
var parts = this._splitString(name);- 9701
var schema = parts[0], table = parts[1], alias = parts[2];- 9701
return (schema && table && alias? new AliasedExpression(new QualifiedIdentifier(schema, table), alias): (schema && table? new QualifiedIdentifier(schema, table): (table && alias? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));} else {- 4843
return name;}},/*** Can either be a string or null.*** @example* //columns* table__column___alias //=> table.column as alias* table__column //=> table.column* //tables* schema__table___alias //=> schema.table as alias* schema__table //=> schema.table** //name and alias* columnOrTable___alias //=> columnOrTable as alias**** @return {String[]} an array with the elements being:* <ul>* <li>For columns :[table, column, alias].</li>* <li>For tables : [schema, table, alias].</li>* </ul>*/_splitString:function (s) {- 19288
var ret, m;- 19288
if ((m = s.match(this._static.COLUMN_REF_RE1)) !== null) {- 156
ret = m.slice(1);}- 19132
else if ((m = s.match(this._static.COLUMN_REF_RE2)) !== null) {- 24
ret = [null, m[1], m[2]];}- 19108
else if ((m = s.match(this._static.COLUMN_REF_RE3)) !== null) {- 1782
ret = [m[1], m[2], null];}else {- 17326
ret = [null, s, null];}- 19288
return ret;},/*** @ignore**/getters:{rowCb:function () {- 21845
return this.__rowCb;},identifierInputMethod:function () {- 14971
return this.__identifierInputMethod;},identifierOutputMethod:function () {- 14971
return this.__identifierOutputMethod;},firstSourceAlias:function () {- 581
var source = this.__opts.from;- 581
if (isUndefinedOrNull(source) || !source.length) {- 2
throw new DatasetError("No source specified for the query");}- 579
source = source[0];- 579
if (isInstanceOf(source, AliasedExpression)) {- 20
return source.alias;- 559
} else if (isString(source)) {- 0
var parts = this._splitString(source);- 0
var alias = parts[2];- 0
return alias ? alias : source;} else {- 559
return source;}},firstSourceTable:function () {- 15
var source = this.__opts.from;- 15
if (isUndefinedOrNull(source) || !source.length) {- 1
throw new QueryError("No source specified for the query");}- 14
var source = source[0];- 14
if (isInstanceOf(source, AliasedExpression)) {- 3
return source.expression;- 11
} else if (isString(source)) {- 0
var parts = this._splitString(source);- 0
return source;} else {- 11
return source;}},sourceList:function () {- 0
return (this.__opts.from || []).map(this.stringToIdentifier, this);},joinSourceList:function () {- 0
return (this.__opts.join || []).map(function (join) {- 0
return this.stringToIdentifier(join.tableAlias || join.table);}, this);},hasSelectSource:function () {- 0
var select = this.__opts.select;- 0
return !(isUndefinedOrNull(select) || select.length === 0);}},/*** @ignore**/setters:{/**@lends patio.Dataset.prototype*/identifierInputMethod:function (meth) {- 15061
this.__identifierInputMethod = meth;},identifierOutputMethod:function (meth) {- 15061
this.__identifierOutputMethod = meth;},rowCb:function (cb) {- 18629
if (isFunction(cb) || isNull(cb)) {- 18624
this.__rowCb = cb;} else {- 5
throw new DatasetError("rowCb mus be a function");}}}},static:{COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,COLUMN_REF_RE2:/^(\w+)___(\w+)$/,COLUMN_REF_RE3:/^(\w+)__(\w+)$/}}).as(module);
|
model.js
|
Coverage87.95
SLOC1118
LOC332
Missed40
|
- 1
var comb = require("comb"),isFunction = comb.isFunction,isUndefined = comb.isUndefined,isDefined = comb.isDefined,isBoolean = comb.isBoolean,isString = comb.isString,argsToArray = comb.argsToArray,isInstanceOf = comb.isInstanceOf,serial = comb.serial,isHash = comb.isHash,when = comb.when,merge = comb.merge,toArray = comb.array.toArray,ModelError = require("./errors").ModelError,plugins = require("./plugins"),isUndefinedOrNull = comb.isUndefinedOrNull,AssociationPlugin = plugins.AssociationPlugin,QueryPlugin = plugins.QueryPlugin,Promise = comb.Promise,PromiseList = comb.PromiseList,HashTable = comb.collections.HashTable,hitch = comb.hitch,hitchIgnore = comb.hitchIgnore,Middleware = comb.plugins.Middleware,EventEmitter = require("events").EventEmitter,util = require("util"),define = comb.define,patio;- 1
var MODELS = new HashTable();- 1
var applyColumnTransformMethod = function (val, meth) {- 947
return !isUndefinedOrNull(meth) ? isFunction(val[meth]) ? val[meth] : isFunction(comb[meth]) ? comb[meth](val) : val : val;};- 1
var checkAndTransformName = function (name) {- 176
return isString(name) ? applyColumnTransformMethod(name, Model.camelize === true ? "camelize" : Model.underscore === true ? "underscore" : null) : name;};- 1
var Model = define([QueryPlugin, Middleware], {instance:{/*** @lends patio.Model.prototype*/__ignore:false,__changed:null,__values:null,/*** patio - read only** @type patio*/patio:null,/*** The database type such as mysql** @type String** */type:null,/*** Whether or not this model is new* */__isNew:true,/*** Signifies if the model has changed* */__isChanged:false,/*** Base class for all models.* <p>This is used through {@link patio.addModel}, <b>NOT directly.</b></p>** @constructs* @augments comb.plugins.Middleware** @param {Object} columnValues values of each column to be used by this Model.** @property {patio.Dataset} dataset a dataset to use to retrieve models from the database. The dataset* has the {@link patio.Dataset#rowCb} set to create instances of this model.* @property {String[]} columns a list of columns this models table contains.* @property {Object} schema the schema of this models table.* @property {String} tableName the table name of this models table.* @property {*} primaryKeyValue the value of this models primaryKey* @property {Boolean} isNew true if this model is new and does not exist in the database.* @property {Boolean} isChanged true if the model has been changed and not saved.** @borrows patio.Dataset#all as all* @borrows patio.Dataset#one as one* @borrows patio.Dataset#avg as avg* @borrows patio.Dataset#count as count* @borrows patio.Dataset#columns as columns* @borrows patio.Dataset#forEach as forEach* @borrows patio.Dataset#isEmpty as empty* @borrows patio.Dataset#first as first* @borrows patio.Dataset#get as get* @borrows patio.Dataset#import as import* @borrows patio.Dataset#insert as insert* @borrows patio.Dataset#insertMultiple as insertMultiple* @borrows patio.Dataset#saveMultiple as saveMultiple* @borrows patio.Dataset#interval as interval* @borrows patio.Dataset#last as last* @borrows patio.Dataset#map as map* @borrows patio.Dataset#max as max* @borrows patio.Dataset#min as min* @borrows patio.Dataset#multiInsert as multiInsert* @borrows patio.Dataset#range as range* @borrows patio.Dataset#selectHash as selectHash* @borrows patio.Dataset#selectMap as selectMap* @borrows patio.Dataset#selectOrderMap as selectOrderMap* @borrows patio.Dataset#set as set* @borrows patio.Dataset#singleRecord as singleRecord* @borrows patio.Dataset#singleValue as singleValue* @borrows patio.Dataset#sum as sum* @borrows patio.Dataset#toCsv as toCsv* @borrows patio.Dataset#toHash as toHash* @borrows patio.Dataset#truncate as truncate* @borrows patio.Dataset#addGraphAliases as addGraphAliases* @borrows patio.Dataset#and as and* @borrows patio.Dataset#distinct as distinct* @borrows patio.Dataset#except as except* @borrows patio.Dataset#exclude as exclude* @borrows patio.Dataset#is as is* @borrows patio.Dataset#isNot as isNot* @borrows patio.Dataset#eq as eq* @borrows patio.Dataset#neq as neq* @borrows patio.Dataset#lt as lt* @borrows patio.Dataset#lte as lte* @borrows patio.Dataset#gt as gt* @borrows patio.Dataset#gte as gte* @borrows patio.Dataset#forUpdate as forUpdate* @borrows patio.Dataset#from as from* @borrows patio.Dataset#fromSelf as fromSelf* @borrows patio.Dataset#graph as graph* @borrows patio.Dataset#grep as grep* @borrows patio.Dataset#group as group* @borrows patio.Dataset#groupAndCount as groupAndCount* @borrows patio.Dataset#groupBy as groupBy* @borrows patio.Dataset#having as having* @borrows patio.Dataset#intersect as intersect* @borrows patio.Dataset#invert as invert* @borrows patio.Dataset#limit as limit* @borrows patio.Dataset#lockStyle as lockStyle* @borrows patio.Dataset#naked as naked* @borrows patio.Dataset#or as or* @borrows patio.Dataset#order as order* @borrows patio.Dataset#orderAppend as orderAppend* @borrows patio.Dataset#orderBy as orderBy* @borrows patio.Dataset#orderMore as orderMore* @borrows patio.Dataset#orderPrepend as orderPrepend* @borrows patio.Dataset#qualify as qualify* @borrows patio.Dataset#reverse as reverse* @borrows patio.Dataset#reverseOrder as reverseOrder* @borrows patio.Dataset#select as select* @borrows patio.Dataset#selectAll as selectAll* @borrows patio.Dataset#selectAppend as selectAppend* @borrows patio.Dataset#selectMore as selectMore* @borrows patio.Dataset#setDefaults as setDefaults* @borrows patio.Dataset#setGraphAliases as setGraphAliases* @borrows patio.Dataset#setOverrides as setOverrides* @borrows patio.Dataset#unfiltered as unfiltered* @borrows patio.Dataset#ungraphed as ungraphed* @borrows patio.Dataset#ungrouped as ungrouped* @borrows patio.Dataset#union as union* @borrows patio.Dataset#unlimited as unlimited* @borrows patio.Dataset#unordered as unordered* @borrows patio.Dataset#where as where* @borrows patio.Dataset#with as with* @borrows patio.Dataset#withRecursive as withRecursive* @borrows patio.Dataset#withSql as withSql* @borrows patio.Dataset#naturalJoin as naturalJoin* @borrows patio.Dataset#naturalLeftJoin as naturalLeftJoin* @borrows patio.Dataset#naturalRightJoin as naturalRightJoin* @borrows patio.Dataset#naturalFullJoin as naturalFullJoin* @borrows patio.Dataset#crossJoin as crossJoin* @borrows patio.Dataset#innerJoin as innerJoin* @borrows patio.Dataset#fullOuterJoin as fullOuterJoin* @borrows patio.Dataset#rightOuterJoin as rightOuterJoin* @borrows patio.Dataset#leftOuterJoin as leftOuterJoin* @borrows patio.Dataset#fullJoin as fullJoin* @borrows patio.Dataset#rightJoin as rightJoin* @borrows patio.Dataset#leftJoin as leftJoin* */constructor:function (options, fromDb) {- 3189
if (this.synced) {- 3189
this.__emitter = new EventEmitter();- 3189
this._super(arguments);- 3189
this.patio = patio || require("./index");- 3189
fromDb = isBoolean(fromDb) ? fromDb : false;- 3189
this.__changed = {};- 3189
this.__values = {};- 3189
if (fromDb) {- 1782
this._hook("pre", "load");- 1782
this.__isNew = false;- 1782
this.__setFromDb(options, true);- 1782
if (this._static.emitOnLoad) {- 1782
this.emit("load", this);- 1782
this._static.emit("load", this);}} else {- 1407
this.__isNew = true;- 1407
this.__set(options);}} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},__set:function (values, ignore) {- 1531
values = values || {};- 1531
this.__ignore = ignore === true;- 1531
Object.keys(values).forEach(function (attribute) {- 6705
var value = values[attribute];//check if the column is a constrained value and is allowed to be set- 6705
!ignore && this._checkIfColumnIsConstrained(attribute);- 6705
this[attribute] = value;}, this);- 1531
this.__ignore = false;},__setFromDb:function (values, ignore) {- 3101
values = values || {};- 3101
this.__ignore = ignore === true;- 3101
var schema = this.schema;- 3101
Object.keys(values).forEach(function (column) {- 26154
var value = values[column];// Typecast value retrieved from db- 26154
if (schema.hasOwnProperty(column)) {- 25473
this.__values[column] = this._typeCastValue(column, value, ignore);} else {- 681
this[column] = value;}}, this);- 3101
this.__ignore = false;},/*** Set multiple values at once. Useful if you have a hash of properties that you want to set.** <b>NOTE:</b> This method will use the static restrictedColumns property of the model.** @example** myModel.setValues({firstName : "Bob", lastName : "yukon"});** //this will throw an error by default, assuming id is a pk.* myModel.setValues({id : 1, firstName : "Bob", lastName : "yukon"});** @param {Object} values value to set on the model.** @return {patio.Model} return this for chaining.*/setValues:function (values) {- 17
this.__set(values, false);- 17
return this;},_toObject:function () {- 1140
if (this.synced) {- 1140
var columns = this._static.columns, ret = {};- 1140
for (var i in columns) {- 10376
var col = columns[i];- 10376
var val = this.__values[col];- 10376
if (!isUndefined(val)) {- 6285
ret[col] = val;}}- 1140
return ret;} else {- 0
throw new ModelError("Model " + this.tableName + " has not been synced");}},_addColumnToIsChanged:function (name, val) {- 7652
if (!this.isNew && !this.__ignore) {- 174
this.__isChanged = true;- 174
this.__changed[name] = val;}},_checkIfColumnIsConstrained:function (name) {- 6705
if (this.synced && !this.__ignore) {- 6705
var col = this.schema[name], restrictedCols = this._static.restrictedColumns || [];- 6705
if (!isUndefined(col) && (col.primaryKey && this._static.isRestrictedPrimaryKey) || restrictedCols.indexOf(name) != -1) {- 0
throw new ModelError("Cannot set primary key of model " + this._static.tableName);}}},_getColumnValue:function (name) {- 6867
var val = this.__values[name];- 6867
var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)];- 6867
var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val;- 6867
return columnValue;},_setColumnValue:function (name, val) {- 7652
var ignore = this.__ignore;- 7652
val = this._typeCastValue(name, val, ignore);- 7652
this._addColumnToIsChanged(name, val);- 7652
var setterFunc = this["_set" + name.charAt(0).toUpperCase() + name.substr(1)];- 7652
var columnValue = isFunction(setterFunc) ? setterFunc.call(this, val, ignore) : val;- 7652
this.__values[name] = columnValue;- 7652
if (this._static.emitOnColumnSet) {- 7652
this.emit("column", name, columnValue, ignore);}},//Typecast the value to the column's type if typecasting. Calls the database's//typecast_value method, so database adapters can override/augment the handling//for database specific column types._typeCastValue:function (column, value, fromDatabase) {- 33125
var colSchema, clazz = this._static;- 33125
if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) {- 33125
var type = colSchema.type;- 33125
if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob"].indexOf(type) === -1) {- 3
value = null;}- 33125
var raiseOnError = clazz.raiseOnTypecastError;- 33125
if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) {- 0
throw new ModelError("null is not allowed for the " + column + " column.");}- 33125
try {- 33125
value = clazz.db.typecastValue(type, value);} catch (e) {- 0
if (raiseOnError === true) {- 0
throw e;}}}- 33125
return value;},/*** Convert this model to an object, containing column, value pairs.** @return {Object} the object version of this model.**/toObject:function () {- 0
return this._toObject(false);},/*** Convert this model to JSON, containing column, value pairs.** @return {JSON} the JSON version of this model.**/toJSON:function () {- 0
return this.toObject();},/*** Convert this model to a string, containing column, value pairs.** @return {String} the string version of this model.**/toString:function () {- 0
return JSON.stringify(this.toObject(), null, 4);},/*** Convert this model to a string, containing column, value pairs.** @return {String} the string version of this model.**/valueOf:function () {- 0
return this.toObject();},_checkTransaction:function (options, cb) {- 2519
return this._static._checkTransaction(options, cb);},addListener:function () {- 0
var emitter = this.__emitter;- 0
return emitter.addListener.apply(emitter, arguments);},on:function () {- 6
var emitter = this.__emitter;- 6
return emitter.on.apply(emitter, arguments);},once:function () {- 0
var emitter = this.__emitter;- 0
return emitter.once.apply(emitter, arguments);},removeListener:function () {- 6
var emitter = this.__emitter;- 6
return emitter.removeListener.apply(emitter, arguments);},removeAllListeners:function () {- 0
var emitter = this.__emitter;- 0
return emitter.removeAllListeners.apply(emitter, arguments);},setMaxListeners:function () {- 0
var emitter = this.__emitter;- 0
return emitter.setMaxListeners.apply(emitter, arguments);},listeners:function () {- 0
var emitter = this.__emitter;- 0
return emitter.listeners.apply(emitter, arguments);},emit:function () {- 11270
var emitter = this.__emitter;- 11270
return emitter.emit.apply(emitter, arguments);},getters:{/**@lends patio.Model.prototype*//*Returns my actual primary key value*/primaryKeyValue:function () {- 48
return this[this.primaryKey];},/*Return if Im a new object*/isNew:function () {- 9742
return this.__isNew;},/*Return if Im changed*/isChanged:function () {- 0
return this.__isChanged;},/**@lends patio.Model.prototype*/primaryKey:function () {- 2617
return this._static.primaryKey;},tableName:function () {- 41
return this._static.tableName;},dataset:function () {- 3125
return this.__dataset || (this.__dataset = this._static.dataset);},removeDataset:function () {- 2
return this.__removeDataset || (this.__removeDataset = this._static.removeDataset);},queryDataset:function () {- 0
return this.__queryDataset || (this.__queryDataset = this._static.queryDataset);},updateDataset:function () {- 4
return this.__updateDataset || (this.__updateDataset = this._static.updateDataset);},insertDataset:function () {- 4
return this.__insertDataset || (this.__insertDataset = this._static.insertDataset);},db:function () {- 30
return this._static.db;},schema:function () {- 76056
return this._static.schema;},columns:function () {- 0
return this._static.columns;},synced:function () {- 12977
return this._static.synced;}}},static:{/*** @lends patio.Model*/synced:false,/*** Set to false to prevent the emitting of an event on load* @default true*/emitOnLoad:true,/*** Set to false to prevent the emitting of an event on the setting of a column value* @default true*/emitOnColumnSet:true,/*** Set to false to prevent empty strings from being type casted to null* @default true*/typecastEmptyStringToNull:true,/*** Set to false to prevent properties from being type casted when loaded from the database.* See {@link patio.Database#typecastValue}* @default true*/typecastOnLoad:true,/*** Set to false to prevent properties from being type casted when manually set.* See {@link patio.Database#typecastValue}* @default true*/typecastOnAssignment:true,/*** Set to false to prevent errors thrown while type casting a value from being propogated.* @default true*/raiseOnTypecastError:true,/*** Set to false to allow the setting of primary keys.* @default false*/isRestrictedPrimaryKey:true,/*** Set to false to prevent models from using transactions when saving, deleting, or updating.* This applies to the model associations also.*/useTransactions:true,/*** See {@link patio.Dataset#identifierOutputMethod}* @default null*/identifierOutputMethod:null,/*** See {@link patio.Dataset#identifierInputMethod}* @default null*/identifierInputMethod:null,/*** Set to false to prevent the reload of a model after saving.* @default true*/reloadOnSave:true,/*** Columns that should be restriced when setting values through the {@link patio.Model#set} method.**/restrictedColumns:null,/*** Set to false to prevent the reload of a model after updating.* @default true*/reloadOnUpdate:true,__camelize:false,__underscore:false,__columns:null,__schema:null,__primaryKey:null,__dataset:null,__db:null,__tableName:null,/*** The table that this Model represents.* <b>READ ONLY</b>*/table:null,/*** patio - read only** @type patio*/patio:null,init:function () {- 92
var emitter = new EventEmitter();- 92
["addListener", "on", "once", "removeListener","removeAllListeners", "setMaxListeners", "listeners", "emit"].forEach(function (name) {- 736
this[name] = emitter[name].bind(emitter);}, this);- 92
if (this.__tableName) {- 91
this._setTableName(this.__tableName);}- 92
if (this.__db) {- 40
this._setDb(this.__db);}},sync:function (cb) {- 4026
var ret = new Promise();- 4026
if (!this.synced) {- 93
var db = this.db, tableName = this.tableName, supers = this.__supers;- 93
ret = db.schema(tableName).chain(hitch(this, function (schema) {- 93
if (!this.synced && schema) {- 93
this._setSchema(schema);- 93
if (supers && supers.length) {- 3
return when(supers.map(function (sup) {- 3
return sup.sync();})).chain(hitch(this, function () {- 3
this.synced = true;- 3
supers.forEach(this.inherits, this);- 3
return this;}));} else {- 90
this.synced = true;- 90
return this;}} else {- 0
var error = new ModelError("Unable to find schema for " + tableName);- 0
this.emit("error", error);- 0
throw error;}}));} else {- 3933
ret.callback(this);}- 4026
if (isFunction(cb)) {- 0
ret.classic(cb);}- 4026
return ret.promise();},/*** Stub for plugins to notified of model inheritance** @param {patio.Model} model a model class to inherit from*/inherits:function (model) {},/*** Create a new model initialized with the specified values.** @param {Object} values the values to initialize the model with.** @returns {Model} instantiated model initialized with the values passed in.*/create:function (values) {//load an object from an object- 0
return new this(values, false);},load:function (vals) {- 936
var ret = new Promise();//sync our model- 936
return this.sync().chain(function () {- 936
var m = new this(vals, true);//call the hooks!- 936
return m._hook("post", "load").chain(function () {- 936
return m;});}.bind(this));},_checkTransaction:function (opts, cb) {- 2938
if (isFunction(opts)) {- 639
cb = opts;- 639
opts = {};} else {- 2299
opts = opts || {};}- 2938
var ret = new Promise(), retVal = null, errored = false;- 2938
this.sync().then(hitch(this, function () {- 2938
if (this.useTransaction(opts)) {- 2938
this.db.transaction(opts, hitch(this, function () {- 2938
return when(cb()).then(function () {- 2894
retVal = argsToArray(arguments);}, function () {- 44
retVal = argsToArray(arguments);- 44
errored = true;});})).then(hitch(this, function () {- 2894
ret[errored ? "errback" : "callback"].apply(ret, retVal);}), hitch(this, function () {- 44
if (errored) {- 44
ret.errback.apply(ret, retVal);} else {- 0
ret.errback.apply(ret, arguments);}}));} else {- 0
when(cb()).then(ret);}}), ret);- 2938
return ret.promise();},/*** @private* Returns a boolean indicating whether or not to use a transaction.* @param {Object} [opts] set a transaction property to override the {@link patio.Model#useTransaction}.*/useTransaction:function (opts) {- 2938
opts = opts || {};- 2938
return isBoolean(opts.transaction) ? opts.transaction === true : this.useTransactions === true;},_setDataset:function (ds) {- 3
this.__dataset = ds;- 3
if (ds.db) {- 3
this._setDb(ds.db);}},_setDb:function (db) {- 43
this.__db = db;},_setTableName:function (name) {- 91
this.__tableName = name;},_setColumns:function (cols) {- 96
var proto = this.prototype;- 96
if (this.__columns) {- 6
this.__columns.forEach(function (name) {- 16
delete proto[name];});}- 96
this.__columns = cols;- 96
cols.forEach(function (name) {- 779
this._defineColumnSetter(name);- 779
this._defineColumnGetter(name);}, this);},_setPrimaryKey:function (pks) {- 99
this.__primaryKey = pks || [];},_setSchema:function (schema) {- 96
var columns = [];- 96
var pks = [];- 96
for (var i in schema) {- 779
var col = schema[i];- 779
var name = applyColumnTransformMethod(i, this.identifierOutputMethod);- 779
schema[name] = col;- 779
columns.push(name);- 779
col.primaryKey && pks.push(name);}- 96
this.__schema = schema;- 96
this._setPrimaryKey(pks);- 96
this._setColumns(columns);},_defineColumnSetter:function (name) {/*Adds a setter to an object*/- 779
this.prototype.__defineSetter__(name, function (val) {- 7652
this._setColumnValue(name, val);});},_defineColumnGetter:function (name) {- 779
this.prototype.__defineGetter__(name, function () {- 6867
return this._getColumnValue(name);});},_getDataset:function () {- 3596
var ds = this.__dataset;- 3596
if (!ds) {- 84
ds = this.db.from(this.tableName);- 84
ds.rowCb = hitch(this, function (vals) {- 885
return this.load(vals);});- 84
this.identifierInputMethod && (ds.identifierInputMethod = this.identifierInputMethod);- 84
this.identifierOutputMethod && (ds.identifierOutputMethod = this.identifierOutputMethod);- 84
this.__dataset = ds;- 3512
} else if (!ds.rowCb) {- 6
ds.rowCb = hitch(this, function rowCb(vals) {- 25
return this.load(vals);});}- 3596
return ds;},_getQueryDataset:function () {- 0
return this._getDataset();},_getUpdateDataset:function () {- 4
return this._getDataset();},_getRemoveDataset:function () {- 2
return this._getDataset();},_getInsertDataset:function () {- 4
return this._getDataset();},/*** @ignore*/getters:{/**@lends patio.Model*//*** Set to true if this models column names should be use the "underscore" method when sending* keys to the database and to "camelize" method on columns returned from the database. If set to false see* {@link patio.Model#underscore}.* @field* @default false* @type {Boolean}*/camelize:function (camelize) {- 169
return this.__camelize;},/*** Set to true if this models column names should be use the "camelize" method when sending* keys to the database and to "underscore" method on columns returned from the database. If set to false see* {@link patio.Model#underscore}.* @field* @default false* @type {Boolean}*/underscore:function (underscore) {- 3
return this.__underscore;},/**@lends patio.Model*//*** The name of the table all instances of the this {@link patio.Model} use.* @field* @ignoreCode* @type String*/tableName:function () {- 6375
return this.__tableName;},/*** The database all instances of this {@link patio.Model} use.* @field* @ignoreCode* @type patio.Database*/db:function () {- 36325
var db = this.__db;- 36325
if (!db) {- 53
db = this.__db = patio.defaultDatabase;}- 36325
if (!db) {- 0
throw new ModelError("patio has not been connected to a database");}- 36325
return db;},/*** A dataset to use to retrieve instances of this {@link patio.Model{ from the database. The dataset* has the {@link patio.Dataset#rowCb} set to create instances of this model.* @field* @ignoreCode* @type patio.Dataset*/dataset:function () {- 3586
return this._getDataset();},removeDataset:function () {- 2
return this._getRemoveDataset();},queryDataset:function () {- 0
return this._getQueryDataset();},updateDataset:function () {- 4
return this._getUpdateDataset();},insertDataset:function () {- 4
return this._getInsertDataset();},/*** A list of columns this models table contains.* @field* @ignoreCode* @type String[]*/columns:function () {- 1144
return this.__columns;},/*** The schema of this {@link patio.Model}'s table. See {@link patio.Database#schema} for details* on the schema object.* @field* @ignoreCode* @type Object*/schema:function () {- 76060
if (this.synced) {- 76060
return this.__schema;} else {- 0
throw new ModelError("Model has not been synced yet");}},/*** The primaryKey column/s of this {@link patio.Model}* @field* @ignoreCode*/primaryKey:function () {- 8340
if (this.synced) {- 8340
return this.__primaryKey.slice(0);} else {- 0
throw new ModelError("Model has not been synced yet");}},/*** A reference to the global {@link patio}.* @field* @ignoreCode*/patio:function () {- 81
return patio || require("./index");}},/**@ignore*/setters:{/**@lends patio.Model*//**@ignore*/camelize:function (camelize) {- 26
camelize = camelize === true;- 26
if (camelize) {- 26
this.identifierOutputMethod = "camelize";- 26
this.identifierInputMethod = "underscore";}- 26
this.__camelize = camelize;- 26
this.__underscore = !camelize;},/**@ignore*/underscore:function (underscore) {- 1
underscore = underscore === true;- 1
if (underscore) {- 1
this.identifierOutputMethod = "underscore";- 1
this.identifierInputMethod = "camelize";}- 1
this.__underscore = underscore;- 1
this.__camelize = !underscore;}}}}).as(exports, "Model");- 1
function checkAndAddDBToTable(db, table) {- 93
if (!table.contains(db)) {- 34
table.set(db, new HashTable());}}/**@ignore*/- 1
exports.create = function (name, supers, modelOptions) {- 93
if (!patio) {- 1
(patio = require("./index"));- 1
patio.on("disconnect", function () {- 39
MODELS.clear();});}- 93
var db, ds, tableName;- 93
var key, modelKey;- 93
if (isString(name)) {- 87
tableName = name;- 87
key = db = patio.defaultDatabase || "default";- 6
} else if (isInstanceOf(name, patio.Dataset)) {- 6
ds = name;- 6
tableName = ds.firstSourceAlias;- 6
key = db = ds.db;}- 93
var hasSuper = false;- 93
if (isHash(supers) || isUndefinedOrNull(supers)) {- 90
modelOptions = supers;- 90
supers = [Model];} else {- 3
supers = toArray(supers);- 3
supers = supers.map(function (sup) {- 3
return exports.getModel(sup, db);});- 3
hasSuper = true;}- 93
var model;- 93
checkAndAddDBToTable(key, MODELS);- 93
var DEFAULT_PROTO = {instance:{}, "static":{}};- 93
modelOptions = merge(DEFAULT_PROTO, modelOptions || {});- 93
modelOptions.instance._hooks = ["save", "update", "remove", "load"];- 93
modelOptions.instance.__hooks = {pre:{}, post:{}};//Mixin the column setter/getters- 93
modelOptions["static"].synced = false;- 93
modelOptions["static"].__tableName = tableName;- 93
modelOptions["static"].__db = (db === "default" ? null : db);- 93
modelOptions["static"].__supers = hasSuper ? supers : [];- 93
modelOptions["static"].__dataset = ds;- 93
model = define(supers.concat(modelOptions.plugins || []).concat([AssociationPlugin]), modelOptions);- 93
["pre", "post"].forEach(function (op) {- 186
var optionsOp = modelOptions[op];- 186
if (optionsOp) {- 0
for (var i in optionsOp) {- 0
model[op](i, optionsOp[i]);}}});- 93
if (!(MODELS.get(key).contains(checkAndTransformName(name)))) {- 61
MODELS.get(key).set(name, model);}- 93
return model;};- 1
exports.syncModels = function (cb) {- 35
var ret = new Promise();- 35
serial(MODELS.entrySet.map(function (entry) {- 34
return function () {- 34
var value = entry.value;- 34
return serial(value.entrySet.map(function (m) {- 61
return hitch(m.value, "sync");}));};})).then(hitchIgnore(ret, "callback", true), ret);- 35
if (isFunction(cb)) {- 0
ret.classic(cb);}- 35
return ret.promise();};- 1
var checkAndGetModel = function (db, name) {- 164
var ret;- 164
if (MODELS.contains(db)) {- 83
ret = MODELS.get(db).get(checkAndTransformName(name));}- 164
return ret;};- 1
exports.getModel = function (name, db) {- 86
var ret = null;- 86
if (isDefined(name)) {- 86
!patio && (patio = require("./index"));- 86
if (isFunction(name)) {- 3
ret = name;} else {- 83
if (!db && isInstanceOf(name, patio.Dataset)) {- 2
db = name.db;}- 83
var defaultDb = patio.defaultDatabase;- 83
if (db) {- 57
ret = checkAndGetModel(db, name);- 57
if (!ret && db === defaultDb) {- 55
ret = checkAndGetModel("default", name);}} else {- 26
db = patio.defaultDatabase;- 26
ret = checkAndGetModel(db, name);- 26
if (!ret) {- 26
ret = checkAndGetModel("default", name);}}}} else {- 0
ret = name;}- 86
if (isUndefinedOrNull(ret)) {- 0
throw new ModelError("Model " + name + " has not been registered with patio");}- 86
return ret;};
|
errors.js
|
Coverage89.47
SLOC81
LOC19
Missed2
|
- 1
var patio = exports;/*** @class Thrown if a function is not impltemened** @param {String} message the message to show.*/- 1
patio.NotImplemented = function(message) {- 4
return new Error("Not Implemented : " + message);};/*** @class Thrown if there is an Expression Error.** @param {String} message the message to show.*/- 1
patio.ExpressionError = function(message) {- 0
return new Error("Expression Error :" + message);};/*** @class Thrown if there is a Query Error.** @param {String} message the message to show.*/- 1
patio.QueryError = function(message) {- 122
return new Error("QueryError : " + message);};/*** @class Thrown if there is a Dataset Error.** @param {String} message the message to show.*/- 1
patio.DatasetError = function(message) {- 7
return new Error("DatasetError : " + message);};/*** @class Thrown if there is a Database Error.** @param {String} message the message to show.*/- 1
patio.DatabaseError = function(message) {- 5
return new Error("Database error : " + message);};/*** @class Thrown if there is a unexpected Error.** @param {String} message the message to show.*/- 1
patio.PatioError = function(message) {- 31
return new Error("Patio error : " + message);};/*** @class Thrown if there is a error thrown within a model.* @param {String} message the message to show.*/- 1
patio.ModelError = function(message) {- 2
return new Error("Model error : " + message);};/*** @class Thrown if there is an error when loading/creating/deleteing an association.* @param {String} message the message to show.*/- 1
patio.AssociationError = function(message) {- 0
return new Error("Association error : " + message);};/*** Thrown if there is an error when performing a migration.* @param {String} message the message to show.*/- 1
patio.MigrationError = function(message) {- 2
return new Error("Migration error : " + message);};
|
database/schema.js
|
Coverage89.49
SLOC1204
LOC257
Missed27
|
- 1
"use strict";- 1
var comb = require("comb"),serial = comb.serial,asyncArray = comb.async.array,isFunction = comb.isFunction,argsToArray = comb.argsToArray,array = comb.array,isArray = comb.isArray,isString = comb.isString,isUndefined = comb.isUndefined,isNumber = comb.isNumber,toArray = comb.array.toArray,hitch = comb.hitch,format = comb.string.format,Dataset = require("../dataset"),Promise = comb.Promise,PromiseList = comb.PromiseList,errors = require("../errors"),DatabaseError = errors.DatabaseError,generators = require("./schemaGenerators"),SchemaGenerator = generators.SchemaGenerator,AlterTableGenerator = generators.AlterTableGenerator,sql = require("../sql").sql,Time = sql.Time,TimeStamp = sql.TimeStamp,DateTime = sql.DateTime,Year = sql.Year,Float = sql.Float,Decimal = sql.Decimal,isInstanceOf = comb.isInstanceOf,Identifier = sql.Identifier,QualifiedIdentifier = sql.QualifiedIdentifier,define = comb.define;- 1
define(null, {instance:{/**@lends patio.Database.prototype*//**@ignore*/constructor:function () {- 122
this._super(arguments);- 122
this.schemas = {};},/*** Adds a column to the specified table. This method expects a column name,* a datatype and optionally a hash with additional constraints and options:** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* addColumn call.* </p>*** @example* //Outside of a table* //ALTER TABLE test ADD COLUMN name text UNIQUE'* DB.addColumn("test", "name", "text", {unique : true});** @param {String} table the table to add the column to.* @param {String} column the name of the column to add.* @param type datatype of the column* @param {Object} [opts] additional options that can be used when adding a column.* @param {Boolean} [opts.primaryKey] set to true if this column is a primary key.* @param {Boolean} [opts.allowNull] whether or not this column should allow null.* @param {Boolean} [opts.unique] set to true to add a UNIQUE constraint to a column,** @return {Promise} a promise that is resolved when the ADD COLUMN action is complete.**/addColumn:function (table, column, type, opts) {- 9
var args = argsToArray(arguments).slice(1);- 9
return this.alterTable(table, function () {- 9
this.addColumn.apply(this, args);});},/*** Adds an index to a table for the given columns** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* addIndex call.* </p>* @example* DB.addIndex("test", "name", {unique : true});* //=> 'CREATE UNIQUE INDEX test_name_index ON test (name)'* DB.addIndex("test", ["one", "two"]);* //=> ''CREATE INDEX test_one_two_index ON test (one, two)''** @param {String} table the table to add the index to.* @param {String|String[]} columns the name of the column/s to create an index for.* @param {Object} [options] additional options that can be used when adding an index.* @param {Boolean} [options.unique] set to true if this this index should have a UNIQUE constraint.* @param {Boolean} [options.ignoreErrors] set to true to ignore errors.** @return {Promise} a promise that is resolved when the CREATE INDEX action is complete.* */addIndex:function (table, columns, options) {- 4
options = options || {};- 4
var ignoreErrors = options.ignoreErrors === true;- 4
var ret = new Promise();- 4
this.alterTable(table,function () {- 4
this.addIndex(columns, options);}).then(ret, function (err) {- 0
if (!ignoreErrors) {- 0
ret.errback(err);} else {- 0
ret.callback();}});- 4
return ret.promise();},/*** Removes a column from the specified table.* <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* dropColumn call.* </p>** @example* DB.dropColumn("items", "category");* //=> 'ALTER TABLE items DROP COLUMN category',** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the column to drop.** @return {Promise} a promise that is resolved once the DROP COLUMN action is complete.* */dropColumn:function (table, column) {- 3
column = argsToArray(arguments).slice(1);- 3
return this.alterTable(table, function () {- 3
this.dropColumn.apply(this, column);});},/*** Removes an index for the given table and column/s.** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* dropIndex call.* </p>** @example* DB.dropIndex("posts", "title");* //=>'DROP INDEX posts_title_index* DB.dropIndex("posts", ["author", "title"]);* //'DROP INDEX posts_author_title_index'** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the name of the column/s the index was created from.** @return {Promise} a promise that is resolved once the DROP INDEX action is complete.* */dropIndex:function (table, columns, options) {- 1
var args = argsToArray(arguments).slice(1);- 1
return this.alterTable(table, function () {- 1
this.dropIndex.apply(this, args);});},/*** Renames a column in the specified table.** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* renameColumn call.* </p>** @example* DB.renameColumn("items", "cntr", "counter");* //=> ALTER TABLE items RENAME COLUMN cntr TO counter** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the name of the column to rename.* @param {String|patio.sql.Identifier} newColumn the new name of the column.** @return {Promise} a promise that is resolved once the RENAME COLUMN action is complete.* */renameColumn:function (table, column, newColumn) {- 4
var args = argsToArray(arguments).slice(1);- 4
return this.alterTable(table, function () {- 4
this.renameColumn.apply(this, args);});},/***Sets the default value for the given column in the given table:** <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* setColumnDefault call.* </p>** @example* DB.setColumnDefault("items", "category", "misc");* //=> ALTER TABLE items ALTER COLUMN category SET DEFAULT 'misc'** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the name of the column to set the DEFAULT on.* @param def the new default value of the column.** @return {Promise} a promise that is resolved once the SET DEFAULT action is complete.* */setColumnDefault:function (table, column, def) {- 1
var args = argsToArray(arguments).slice(1);- 1
return this.alterTable(table, function () {- 1
this.setColumnDefault.apply(this, args);});},/*** Set the data type for the given column in the given table:* <p>* This method is a shortcut to {@link patio.Database#alterTable} with an* setColumnType call.* </p>** @example* DB.setColumnType("items", "category", String);* //=> ALTER TABLE items ALTER COLUMN category TYPE varchar(255)** @param {String|patio.sql.Identifier} table the table to alter.* @param {String|patio.sql.Identifier} column the name of the column to set the TYPE on.* @param type the datatype of the column.** @return {Promise} a promise that is resolved once the SET TYPE action is complete.* */setColumnType:function (table, column, type) {- 3
var args = argsToArray(arguments).slice(1);- 3
return this.alterTable(table, function () {- 3
this.setColumnType.apply(this, args);});},/*** Alters the given table with the specified block.* <p>* <b>NOTE:</b> The block is invoked in the scope of the table that is being altered. The block* is also called with the table as the first argument. Within the block you must use* <b>this</b>(If the block has not been bound to a different scope), or the table object* that is passed in for all alter table operations. See {@link patio.AlterTableGenerator} for* avaiable operations.* </p>** <p>* <b>Note</b> that addColumn accepts all the options available for column* definitions using createTable, and addIndex accepts all the options* available for index definition.* </p>** @example* //using the table object* DB.alterTable("items", function(table){* //you must use the passed in table object.* table.addColumn("category", "text", {default : 'javascript'});* table.dropColumn("category");* table.renameColumn("cntr", "counter");* table.setColumnType("value", "float");* table.setColumnDefault("value", "float");* table.addIndex(["group", "category"]);* table.dropIndex [:group, :category]* });** //using this* DB.alterTable("items", function(){* this.addColumn("category", "text", {default : 'javascript'});* this.dropColumn("category");* this.renameColumn("cntr", "counter");* this.setColumnType("value", "float");* this.setColumnDefault("value", "float");* this.addIndex(["group", "category"]);* this.dropIndex [:group, :category]* });** //This will not work* DB.alterTable("items", comb.hitch(someObject, function(){* //This is called in the scope of someObject so this* //will not work and will throw an error* this.addColumn("category", "text", {default : 'javascript'});* }));** //This will work* DB.alterTable("items", comb.hitch(someObject, function(table){* //This is called in the scope of someObject so you must* //use the table argument* table.category("text", {default : 'javascript'});* }));*** @param {String|patio.sql.Identifier} table to the table to perform the ALTER TABLE operations on.* @param {Function} block the block to invoke for the ALTER TABLE operations** @return {Promise} a promise that is resolved once all ALTER TABLE operations have completed.* */alterTable:function (name, generator, block) {- 80
if (isFunction(generator)) {- 80
block = generator;- 80
generator = new AlterTableGenerator(this, block);}- 80
var ret = new Promise();- 80
return this.__alterTableSqlList(name, generator.operations).chain(function (res) {- 80
return asyncArray(comb(res).pluck("1").flatten()).forEach(function (sql) {- 91
return this.executeDdl(sql);}, this).chain(comb("removeCachedSchema").bindIgnore(this, name));}.bind(this));},/*** Creates a table with the columns given in the provided block:** <p>* <b>NOTE:</b> The block is invoked in the scope of the table that is being created. The block* is also called with the table as the first argument. Within the block you must use* <b>this</b>(If the block has not been bound to a different scope), or the table object* that is passed in for all create table operations. See {@link patio.SchemaGenerator} for* available operations.* </p>*** @example** //using the table to create the table* DB.createTable("posts", function(table){* table.primaryKey("id");* table.column('title", "text");* //you may also invoke the column name as* //function on the table* table.content(String);* table.index(title);* });** //using this to create the table* DB.createTable("posts", function(){* this.primaryKey("id");* this.column('title", "text");* //you may also invoke the column name as* //function on the table* this.content(String);* this.index(title);* });** @param {String|patio.sql.Identifier} name the name of the table to create.* @param {Object} [options] an optional options object* @param {Boolean} [options.temp] set to true if this table is a TEMPORARY table.* @param {Boolean} [options.ignoreIndexErrors] Ignore any errors when creating indexes.* @param {Function} block the block to invoke when creating the table.** @return {Promise} a promise that is resolved when the CREATE TABLE action is completed.**/createTable:function (name, options, block) {- 218
if (isFunction(options)) {- 208
block = options;- 208
options = {};}- 218
this.removeCachedSchema(name);- 218
if (isInstanceOf(options, SchemaGenerator)) {- 0
options = {generator:options};}- 218
var generator = options.generator || new SchemaGenerator(this, block);- 218
return this.__createTableFromGenerator(name, generator, options).chain(comb("__createTableIndexesFromGenerator").bindIgnore(this, name, generator, options));},/*** Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.* <p>* See {@link patio.Database#createTable} for parameter types.* </p>** @example* // DROP TABLE a* // CREATE TABLE a (a integer)* DB.forceCreateTable("a", function(){* this.a("integer");* });***/forceCreateTable:function (name, options, block) {- 18
return this.dropTable(name).chainBoth(comb("createTable").bindIgnore(this, name, options, block));},/*** Creates the table unless the table already exists.* <p>* See {@link patio.Database#createTable} for parameter types.* </p>*/createTableUnlessExists:function (name, options, block) {- 0
var ret = new Promise();- 0
return this.tableExists(name).chain(function (exists) {- 0
if (!exists) {- 0
return this.createTable(name, options, block);}}.bind(this));},/*** Creates a view, replacing it if it already exists:* @example* DB.createOrReplaceView("cheapItems", "SELECT * FROM items WHERE price < 100");* //=> CREATE OR REPLACE VIEW cheapItems AS SELECT * FROM items WHERE price < 100* DB.createOrReplaceView("miscItems", DB[:items].filter({category : 'misc'}));* //=> CREATE OR REPLACE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'** @param {String|patio.sql.Identifier} name the name of the view to create.* @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the* view.** @return {Promise} a promise that is resolved when the CREATE OR REPLACE VIEW action is complete.**/createOrReplaceView:function (name, source) {- 4
if (isInstanceOf(source, Dataset)) {- 2
source = source.sql;}- 4
return this.executeDdl(format("CREATE OR REPLACE VIEW %s AS %s", this.__quoteSchemaTable(name), source)).chain(function () {- 4
this.removeCachedSchema(name);}.bind(this));},/*** Creates a view based on a dataset or an SQL string:* @example* DB.createView("cheapItems", "SELECT * FROM items WHERE price < 100");* //=> CREATE VIEW cheapItems AS SELECT * FROM items WHERE price < 100* DB.createView("miscItems", DB[:items].filter({category : 'misc'}));* //=> CREATE VIEW miscItems AS SELECT * FROM items WHERE category = 'misc'** @param {String|patio.sql.Identifier} name the name of the view to create.* @param {String|patio.Dataset} source the SQL or {@link patio.Dataset} to use as the source of the* view.**/createView:function (name, source) {- 4
if (isInstanceOf(source, Dataset)) {- 2
source = source.sql;}- 4
return this.executeDdl(format("CREATE VIEW %s AS %s", this.__quoteSchemaTable(name), source));},/*** Drops one or more tables corresponding to the given names.** @example* DB.dropTable("test");* //=>'DROP TABLE test'* DB.dropTable("a", "bb", "ccc");* //=>'DROP TABLE a',* //=>'DROP TABLE bb',* //=>'DROP TABLE ccc'** @param {String|String[]|patio.sql.Identifier|patio.sql.Identifier[]} names the names of the tables* to drop.** @return {Promise} a promise that is resolved once all tables have been dropped.**/dropTable:function (names) {- 48
if (!isArray(names)) {- 33
names = comb(arguments).toArray();}- 48
return serial(names.filter(function (t) {- 67
return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);}).map(function (name) {- 65
return function () {- 65
return this.executeDdl(this.__dropTableSql(name)).chain(function () {- 55
return this.removeCachedSchema(name);}.bind(this));}.bind(this);}.bind(this)));},/*** Forcible drops one or more tables corresponding to the given names, ignoring errors.** @example* DB.dropTable("test");* //=>'DROP TABLE test'* DB.dropTable("a", "bb", "ccc");* //=>'DROP TABLE a',* //=>'DROP TABLE bb',* //=>'DROP TABLE ccc'** @param {String|String[]|patio.sql.Identifier|patio.sql.Identifier[]} names the names of the tables* to drop.** @return {Promise} a promise that is resolved once all tables have been dropped.**/forceDropTable:function (names) {- 97
var ret = new Promise();- 97
if (!isArray(names)) {- 63
names = comb(arguments).toArray();}- 97
names = names.filter(function (t) {- 146
return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);});- 97
var l = names.length;- 97
var drop = function (i) {- 243
if (i < l) {- 146
var name = names[i++];- 146
this.executeDdl(this.__dropTableSql(name)).both(hitch(this, function () {- 146
this.removeCachedSchema(name);- 146
drop(i);}));} else {- 97
ret.callback();}}.bind(this);- 97
drop(0);- 97
return ret.promise();},/*** Drops one or more views corresponding to the given names.** @example* DB.dropView("test_view");* //=>'DROP VIEW test_view'* DB.dropTable("test_view_1", "test_view_2", "test_view_3");* //=>'DROP VIEW test_view_1',* //=>'DROP VIEW test_view_2',* //=>'DROP VIEW test_view_3'** @param {String|String[]|patio.sql.Identifier|patio.sql.Identifier[]} names the names of the views* to drop.** @return {Promise} a promise that is resolved once the view/s have been dropped.**/dropView:function (names) {- 8
var ret = new Promise(), l = names.length;- 8
if (isArray(names)) {- 4
var drop = hitch(this, function (i) {- 8
if (i < l) {- 4
var name = names[i++];- 4
this.executeDdl(format("DROP VIEW %s", this.__quoteSchemaTable(name))).then(hitch(this, function () {- 4
this.removeCachedSchema(name);- 4
drop(i);}), ret);} else {- 4
ret.callback();}});- 4
drop(0);- 4
return ret.promise();} else {- 4
return this.dropView(argsToArray(arguments).filter(function (t) {- 4
return isString(t) || isInstanceOf(t, Identifier, QualifiedIdentifier);}));}},/*** Renames a table.** @example* comb.executeInOrder(DB, function(DB){* DB.tables(); //=> ["items"]* DB.renameTable("items", "old_items");* //=>'ALTER TABLE items RENAME TO old_items'* DB.tables; //=> ["old_items"]*});** @param {String|patio.sql.Identifier} name the name of the table to rename* @param {String|patio.sql.Identifier} newName the new name of the table* @return {Promise} a promise that is resolved once the table is renamed.**/renameTable:function (name, newName) {- 2
return this.executeDdl(this.__renameTableSql(name, newName)).chain(hitch(this, function () {- 2
this.removeCachedSchema(name);})).promise();},/*** @private* The SQL to execute to modify the DDL for the given table name. op* should be one of the operations returned by the AlterTableGenerator.* */__alterTableSql:function (table, op) {- 79
var ret = new Promise();- 79
var quotedName = op.name ? this.__quoteIdentifier(op.name) : null;- 79
var alterTableOp = null;- 79
switch (op.op) {case "addColumn":- 13
alterTableOp = format("ADD COLUMN %s", this.__columnDefinitionSql(op));- 13
break;case "dropColumn":- 4
alterTableOp = format("DROP COLUMN %s", quotedName);- 4
break;case "renameColumn":- 48
alterTableOp = format("RENAME COLUMN %s TO %s", quotedName, this.__quoteIdentifier(op.newName));- 48
break;case "setColumnType":- 3
alterTableOp = format("ALTER COLUMN %s TYPE %s", quotedName, this.typeLiteral(op));- 3
break;case "setColumnDefault":- 2
alterTableOp = format("ALTER COLUMN %s SET DEFAULT %s", quotedName, this.literal(op["default"]));- 2
break;case "setColumnNull":- 0
alterTableOp = format("ALTER COLUMN %s %s NOT NULL", quotedName, op["null"] ? "DROP" : "SET");- 0
break;case "addIndex":- 5
return ret.callback(this.__indexDefinitionSql(table, op)).promise();case "dropIndex":- 2
return ret.callback(this.__dropIndexSql(table, op)).promise();case "addConstraint":- 1
alterTableOp = format("ADD %s", this.__constraintDefinitionSql(op));- 1
break;case "dropConstraint":- 0
alterTableOp = format("DROP CONSTRAINT %s", quotedName);- 0
break;default :- 1
throw new DatabaseError("Invalid altertable operator");}- 71
return ret.callback(format("ALTER TABLE %s %s", this.__quoteSchemaTable(table), alterTableOp)).promise();},/*** @private* Array of SQL DDL modification statements for the given table,* corresponding to the DDL changes specified by the operations.* */__alterTableSqlList:function (table, operations) {- 80
return new PromiseList(operations.map(hitch(this, "__alterTableSql", table)));},/*** @private* SQL DDL fragment containing the column creation SQL for the given column.** @param column*/__columnDefinitionSql:function (column) {- 643
var sql = [format("%s %s", this.__quoteIdentifier(column.name), this.typeLiteral(column))];- 643
column.unique && sql.push(this._static.UNIQUE);- 643
(column.allowNull === false || column["null"] === false) && sql.push(this._static.NOT_NULL);- 643
(column.allowNull === true || column["null"] === true) && sql.push(this._static.NULL);- 643
!isUndefined(column["default"]) && sql.push(format(" DEFAULT %s", this.literal(column["default"])));- 643
column.primaryKey && sql.push(this._static.PRIMARY_KEY);- 643
column.autoIncrement && sql.push(" " + this.autoIncrementSql);- 643
column.table && sql.push(this.__columnReferencesColumnConstraintSql(column));- 643
return sql.join("");},/*** @private* SQL DDL fragment containing the column creation* SQL for all given columns, used inside a CREATE TABLE block.*/__columnListSql:function (generator) {- 218
return generator.columns.map(hitch(this, "__columnDefinitionSql")).concat(generator.constraints.map(hitch(this, "__constraintDefinitionSql"))).join(this._static.COMMA_SEPARATOR);},/*** @private*SQL DDL fragment for column foreign key references (column constraints)*/__columnReferencesColumnConstraintSql:function (column) {- 30
return this.__columnReferencesSql(column);},/*** @private* SQL DDL fragment for column foreign key references*/__columnReferencesSql:function (column) {- 38
var sql = format(" REFERENCES %s", this.__quoteSchemaTable(column.table));- 38
column.key && (sql += format("(%s)", array.toArray(column.key).map(this.__quoteIdentifier, this).join(this._static.COMMA_SEPARATOR)));- 38
column.onDelete && (sql += format(" ON DELETE %s", this.__onDeleteClause(column.onDelete)));- 38
column.onUpdate && (sql += format(" ON UPDATE %s", this.__onUpdateClause(column.onUpdate)));- 38
column.deferrable && (sql += " DEFERRABLE INITIALLY DEFERRED");- 38
return sql;},/*** @private* SQL DDL fragment for table foreign key references (table constraints)* */__columnReferencesTableConstraintSql:function (constraint) {- 6
return format("FOREIGN KEY %s%s", this.literal(constraint.columns.map(function (c) {- 6
return isString(c) ? sql.stringToIdentifier(c) : c;})), this.__columnReferencesSql(constraint));},/*** @private* SQL DDL fragment specifying a constraint on a table.*/__constraintDefinitionSql:function (constraint) {- 6
var ret = [constraint.name ? format("CONSTRAINT %s ", this.__quoteIdentifier(constraint.name)) : ""];- 6
switch (constraint.type) {case "check":- 0
var check = constraint.check;- 0
ret.push(format("CHECK %s", this.__filterExpr(isArray(check) && check.length === 1 ? check[0] : check)));- 0
break;case "primaryKey":- 0
ret.push(format("PRIMARY KEY %s", this.literal(constraint.columns.map(function (c) {- 0
return isString(c) ? sql.stringToIdentifier(c) : c;}))));- 0
break;case "foreignKey":- 6
ret.push(this.__columnReferencesTableConstraintSql(constraint));- 6
break;case "unique":- 0
ret.push(format("UNIQUE %s", this.literal(constraint.columns.map(function (c) {- 0
return isString(c) ? sql.stringToIdentifier(c) : c;}))));- 0
break;default:- 0
throw new DatabaseError(format("Invalid constriant type %s, should be 'check', 'primaryKey', foreignKey', or 'unique'", constraint.type));}- 6
return ret.join("");},/*** @private* Execute the create table statements using the generator.* */__createTableFromGenerator:function (name, generator, options) {- 218
return this.executeDdl(this.__createTableSql(name, generator, options));},/*** @private* Execute the create index statements using the generator.* */__createTableIndexesFromGenerator:function (name, generator, options) {- 218
var e = options.ignoreIndexErrors;- 218
var ret = new Promise();- 218
var promises = generator.indexes.map(function (index) {- 17
var ps = this.__indexSqlList(name, [index]).map(this.executeDdl, this);- 17
return new PromiseList(ps);}, this);- 218
if (promises.length) {- 15
new PromiseList(promises).then(ret, hitch(ret, e ? "callback" : "errback"));} else {- 203
ret.callback();}- 218
return ret.promise();},/*** @private* DDL statement for creating a table with the given name, columns, and options* */__createTableSql:function (name, generator, options) {- 218
return format("CREATE %sTABLE %s (%s)", options.temp ? this.temporaryTableSql : "", this.__quoteSchemaTable(name), this.__columnListSql(generator));},/*** @private* Default index name for the table and columns, may be too long* for certain databases.*/__defaultIndexName:function (tableName, columns) {- 24
var parts = this.__schemaAndTable(tableName);- 24
var schema = parts[0], table = parts[1];- 24
var index = [];- 24
if (schema && schema !== this.defaultSchema) {- 0
index.push(schema);}- 24
index.push(table);- 24
index = index.concat(columns.map(function (c) {- 27
return isString(c) ? c : this.literal(c).replace(/\W/g, "");}, this));- 24
index.push("index");- 24
return index.join(this._static.UNDERSCORE);},/*** @private* The SQL to drop an index for the table.* */__dropIndexSql:function (table, op) {- 2
return format("DROP INDEX %s", this.__quoteIdentifier(op.name || this.__defaultIndexName(table, op.columns)));},/*** @private** SQL DDL statement to drop the table with the given name.**/__dropTableSql:function (name) {- 211
return format("DROP TABLE %s", this.__quoteSchemaTable(name));},/*** @private* Proxy the filterExpr call to the dataset, used for creating constraints.* */__filterExpr:function (args, block) {- 2
var ds = this.__schemaUtiltyDataset;- 2
return ds.literal(ds._filterExpr.apply(ds, arguments));},/*** @private* SQL DDL statement for creating an index for the table with the given name* and index specifications.*/__indexDefinitionSql:function (tableName, index) {- 6
var indexName = index.name || this.__defaultIndexName(tableName, index.columns);- 6
if (index.type) {- 0
throw new DatabaseError("Index types are not supported for this database");- 6
} else if (index.where) {- 0
throw new DatabaseError("Partial indexes are not supported for this database");} else {- 6
return format("CREATE %sINDEX %s ON %s %s", index.unique ? "UNIQUE " : "", this.__quoteIdentifier(indexName), this.__quoteSchemaTable(tableName), this.literal(index.columns.map(function (c) {- 7
return isString(c) ? new Identifier(c) : c;})));}},/*** Array of SQL DDL statements, one for each index specification,* for the given table.*/__indexSqlList:function (tableName, indexes) {- 17
return indexes.map(hitch(this, this.__indexDefinitionSql, tableName));},/*** @private* SQL DDL ON DELETE fragment to use, based on the given action.*The following actions are recognized:* <ul>* <li>cascade - Delete rows referencing this row.</li>* <li>noAction (default) - Raise an error if other rows reference this* row, allow deferring of the integrity check.* </li>* <li>restrict - Raise an error if other rows reference this row,* but do not allow deferring the integrity check.</li>* <li> setDefault - Set columns referencing this row to their default value.</li>* <li>setNull - Set columns referencing this row to NULL.</li>* </ul>*/__onDeleteClause:function (action) {- 23
return this._static[action.toUpperCase()] || this._static.NO_ACTION;},/*** @private* SQL DDL ON UPDATE fragment to use, based on the given action.*The following actions are recognized:* <ul>* <li>cascade - Delete rows referencing this row.</li>* <li>noAction (default) - Raise an error if other rows reference this* row, allow deferring of the integrity check.* </li>* <li>restrict - Raise an error if other rows reference this row,* but do not allow deferring the integrity check.</li>* <li> setDefault - Set columns referencing this row to their default value.</li>* <li>setNull - Set columns referencing this row to NULL.</li>* </ul>*/__onUpdateClause:function (action) {- 1
return this._static[action.toUpperCase()] || this._static.NO_ACTION;},/*** @private* Proxy the quoteSchemaTable method to the dataset* */__quoteSchemaTable:function (table) {- 2221
return this.__schemaUtiltyDataset.quoteSchemaTable(table);},/*** @private* Proxy the quoteIdentifier method to the dataset, used for quoting tables and columns.* */__quoteIdentifier:function (v) {- 836
return this.__schemaUtiltyDataset.quoteIdentifier(v);},/*** @private* SQL DDL statement for renaming a table.* */__renameTableSql:function (name, newName) {- 1
return format("ALTER TABLE %s RENAME TO %s", this.__quoteSchemaTable(name), this.__quoteSchemaTable(newName));},/*** @private* Remove the cached schemaUtilityDataset, because the identifier* quoting has changed.*/__resetSchemaUtilityDataset:function () {- 0
this.__schemaUtiltyDs = null;},/*** @private* Split the schema information from the table* */__schemaAndTable:function (tableName) {- 210
return this.__schemaUtiltyDataset.schemaAndTable(tableName);},/*** @private* Return true if the given column schema represents an autoincrementing primary key.**/_schemaAutoincrementingPrimaryKey:function (schema) {- 0
return !!schema.primaryKey;},/*** @private* SQL fragment specifying the type of a given column.* */typeLiteral:function (column) {- 689
return this.__typeLiteralGeneric(column);},/*** @private* SQL fragment specifying the full type of a column,* consider the type with possible modifiers.*/__typeLiteralGeneric:function (column) {- 689
var type = column.type;- 689
var meth = "__typeLiteralGeneric";- 689
var isStr = isString(type);- 689
var proper = isStr ? type.charAt(0).toUpperCase() + type.substr(1) : null;- 689
if (type === String || (isStr && type.match(/string/i))) {- 198
meth += "String";- 491
} else if ((isStr && type.match(/number/i)) || type === Number) {- 12
meth += "Numeric";- 479
} else if ((isStr && type.match(/datetime/i)) || type === DateTime) {- 8
meth += "DateTime";- 471
} else if ((isStr && type.match(/date/i)) || type === Date) {- 11
meth += "Date";- 460
} else if ((isStr && type.match(/year/i)) || type === Year) {- 2
meth += "Year";- 458
} else if ((isStr && type.match(/timestamp/i)) || type === TimeStamp) {- 4
meth += "Timestamp";- 454
} else if ((isStr && type.match(/time/i)) || type === Time) {- 2
meth += "Time";- 452
} else if ((isStr && type.match(/decimal/i)) || type === Decimal) {- 2
meth += "Decimal";- 450
} else if ((isStr && type.match(/float/i)) || type === Float) {- 15
meth += "Float";- 435
} else if ((isStr && type.match(/boolean/i)) || type === Boolean) {- 5
meth += "Boolean";- 430
} else if ((isStr && type.match(/buffer/i)) || type === Buffer) {- 29
meth += "Blob";- 401
} else if (isStr && isFunction(this[meth + proper])) {- 138
meth += proper;} else {- 263
return this.__typeLiteralSpecific(column);}- 426
return this[meth](column);},/*** @private* patio uses the date type by default for Dates.* <ul>* <li>if onlyTime is present then time is used</li>* <li>if timeStamp is present then timestamp is used,</li>* <li>if dateTime is present then datetime is used</li>* <li>if yearOnly is present then year is used</li>* <li>else date is used</li>* </ul>*/__typeLiteralGenericDate:function (column) {- 11
var type = column.type, ret = "date";- 11
if (column.onlyTime) {- 2
ret = "time";- 9
} else if (column.timeStamp) {- 2
ret = "timestamp";- 7
} else if (column.dateTime) {- 2
ret = "datetime";- 5
} else if (column.yearOnly) {- 2
ret = "year";}- 11
return ret;},/*** @private* * patio uses the blob type by default for Buffers.*/__typeLiteralGenericBlob:function (column) {- 25
return "blob";},/*** @private* * patio uses the year type by default for {@link patio.sql.DateTime}.*/__typeLiteralGenericDateTime:function (column) {- 2
return "datetime";},/*** @private* patio uses the timestamp type by default for {@link patio.sql.TimeStamp}.*/__typeLiteralGenericTimestamp:function (column) {- 4
return "timestamp";},/*** @private* patio uses the time type by default for {@link patio.sql.Time}.*/__typeLiteralGenericTime:function (column) {- 2
return "time";},/*** @private* patio uses the year type by default for {@link patio.sql.Year}.*/__typeLiteralGenericYear:function (column) {- 2
return "year";},/*** @private* patio uses the boolean type by default for Boolean class* */__typeLiteralGenericBoolean:function (column) {- 3
return "boolean";},/*** @private* patio uses the numeric type by default for NumericTypes* If a size is given, it is used, otherwise, it will default to whatever* the database default is for an unsized value.* <ul>* <li> if isInt is present the int is used</li>* <li> if isDouble is present then double precision is used</li>* </ul>*/__typeLiteralGenericNumeric:function (column) {- 10
return column.size ? format("numeric(%s)", array.toArray(column.size).join(', ')) : column.isInt ? "integer" : column.isDouble ? "double precision" : "numeric";},/*** @private*/__typeLiteralGenericFloat:function (column) {- 15
return "double precision";},/*** @private*/__typeLiteralGenericDecimal:function (column) {- 2
return "double precision";},/*** @private* patio uses the varchar type by default for Strings. If a* size isn't present, patio assumes a size of 255. If the* fixed option is used, patio uses the char type. If the* text option is used, patio uses the :text type.*/__typeLiteralGenericString:function (column) {- 41
return column.text ? "text" : format("%s(%s)", column.fixed ? "char" : "varchar", column.size || 255);},/*** @private* SQL fragment for the given type of a column if the column is not one of the* generic types specified with a native javascript type class.*/__typeLiteralSpecific:function (column) {- 335
var type = column.type;- 335
type = type === "double" ? "double precision" : type;- 335
if (type === "varchar") {- 7
column.size = isNumber(column.size) ? column.size : 255;}- 335
var elements = column.size || column.elements;- 335
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 () {- 9
return this._static.AUTOINCREMENT;},/*** @private* @field* */temporaryTableSql:function () {- 3
return this._static.TEMPORARY;},/*** @private* @field* */__schemaUtiltyDataset:function () {- 3269
this.__schemaUtiltyDs = this.__schemaUtiltyDs || this.dataset;- 3269
return this.__schemaUtiltyDs;}}},"static":{/**@lends patio.Database*//***Default AUTO INCREMENT SQL*/AUTOINCREMENT:'AUTOINCREMENT',/***Default CASCACDE SQL*/CASCADE:'CASCADE',/***Default comma*/COMMA_SEPARATOR:', ',/***Default NO ACTION SQL*/NO_ACTION:'NO ACTION',/***Default NOT NULL SQL*/NOT_NULL:' NOT NULL',/*** Default NULL SQL*/NULL:' NULL',/***Default PRIMARY KEY SQL*/PRIMARY_KEY:' PRIMARY KEY',/***Default RESTRICT SQL*/RESTRICT:'RESTRICT',/***Default SET DEFAULT SQL*/SET_DEFAULT:'SET DEFAULT',/***Default SET NULL SQL*/SET_NULL:'SET NULL',/***Default TEMPORARY SQL*/TEMPORARY:'TEMPORARY ',/***Default UNDERSCORE SQL, used in index creation.*/UNDERSCORE:'_',/***Default UNIQUE SQL*/UNIQUE:' UNIQUE',/*** Default UNSIGNED SQL*/UNSIGNED:' UNSIGNED'}}).as(module);
|
sql.js
|
Coverage90.99
SLOC2745
LOC466
Missed42
|
- 1
var comb = require("comb-proxy"),array = comb.array,flatten = array.flatten,ExpressionError = require("./errors").ExpressionError,methodMissing = comb.methodMissing,createFunctionWrapper = comb.createFunctionWrapper,isUndefined = comb.isUndefined,isNull = comb.isNull,isInstanceOf = comb.isInstanceOf,argsToArray = comb.argsToArray,isDate = comb.isDate,isHash = comb.isHash,merge = comb.merge,isArray = comb.isArray,toArray = array.toArray,format = comb.string.format,isBoolean = comb.isBoolean,isNumber = comb.isNumber,isObject = comb.isObject,isString = comb.isString,define = comb.define,isRegExp = comb.isRegExp,Dataset, patio;- 1
var virtualRow = function (name) {- 1016
var WILDCARD = new LiteralString('*');- 1016
var QUESTION_MARK = new LiteralString('?');- 1016
var COMMA_SEPARATOR = new LiteralString(', ');- 1016
var DOUBLE_UNDERSCORE = '__';- 1016
var parts = name.split(DOUBLE_UNDERSCORE);- 1016
var table = parts[0], column = parts[1];- 1016
var ident = column ? QualifiedIdentifier.fromArgs([table, column]) : Identifier.fromArgs([name]);- 1016
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);- 1016
var ret = createFunctionWrapper(prox, function (m) {- 548
var args = argsToArray(arguments);- 548
if (args.length) {- 542
return SQLFunction.fromArgs([name].concat(args));} else {- 6
return prox;}}, function () {- 0
return SQLFunction.fromArgs(arguments);});- 1016
ret.__proto__ = ident;- 1016
return ret;};- 1
var DATE_METHODS = ["getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds","getTime", "getTimezoneOffset", "getUTCDate", "getUTCDay", "getUTCFullYear", "getUTCHours", "getUTCMilliseconds","getUTCMinutes", "getUTCMonth", "getUTCSeconds", "getYear", "parse", "setDate", "setFullYear", "setHours", "setMilliseconds","setMinutes", "setMonth", "setSeconds", "setTime", "setUTCDate", "setUTCFullYear", "setUTCHours", "setUTCMilliseconds","setUTCMinutes", "setUTCMonth", "setUTCSeconds", "setYear", "toDateString", "toGMTString", "toLocaleDateString","toLocaleTimeString", "toLocaleString", "toTimeString", "toUTCString", "UTC", "valueOf"];- 1
var addDateMethod = function (op) {- 180
return function () {- 4
return this.date[op].apply(this.date, arguments);}};/*** @constructor* Creates a Year type to be used in queries that require a SQL year datatype.* All <i>Date</i> methods ar included in the prototype of the Year type. toString and toJSON* are overridden to return a year format instead of the default <i>Date</i> formatting.* See {@link patioTime#yearToString} for formatting information.** @example** var year = patio.Year(2009); //=> 2009* JSON.stringify(year)l //=> 2009** @memberOf patio.sql* @param {Number} y the year this year represents.*/- 1
var Year = function (y) {- 10
this.date = isUndefined(y) ? new Date() : isDate(y) ? y : new Date(y, 0, 1, 0, 0, 0);};- 1
Year.prototype.toJSON = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
Year.prototype.toString = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DATE_METHODS.forEach(function (op) {- 45
Year.prototype[op] = addDateMethod(op);}, this);/*** @constructor* Creates a Time type to be used in queries that require a SQL time datatype.* All <i>Date</i> methods ar included in the prototype of the Time type. toString and toJSON* are overridden to return a time format instead of the default <i>Date</i> formatting.* See {@link patioTime#timeToString} for formatting information.** @example** var time = patio.Time(12, 12, 12); //=> 12:12:12* JSON.stringify(time); //=> 12:12:12** @memberOf patio.sql* @param {Number} [h=0] the hour* @param {Number} [min=0] the minute/s* @param {Number} [s=0] the second/s* @param {Number} [ms=0] the millisecond/s, this paramater is not be used, but may depending on the adapter.*/- 1
var Time = function (h, min, s, ms) {- 14
var args = argsToArray(arguments);- 14
if (args.length === 0) {- 0
this.date = new Date();- 14
} else if (isDate(h)) {- 9
this.date = h;} else {- 5
var date = new Date(1970, 0, 1, 0, 0, 0);- 5
isNumber(h) && date.setHours(h);- 5
isNumber(min) && date.setMinutes(min);- 5
isNumber(s) && date.setSeconds(s);- 5
isNumber(ms) && date.setMilliseconds(ms);- 5
this.date = date;}};- 1
Time.prototype.toJSON = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
Time.prototype.toString = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DATE_METHODS.forEach(function (op) {- 45
Time.prototype[op] = addDateMethod(op);}, this);/*** @constructor* Creates a TimeStamp type to be used in queries that require a SQL timestamp datatype.* All <i>Date</i> methods ar included in the prototype of the TimeStamp type. toString and toJSON* are overridden to return a ISO8601 format instead of the default <i>Date</i> formatting.* See {@link patioTime#timeStampToString} for formatting information.** @example** var timeStamp = patio.TimeStamp(2009, 10, 10, 10, 10, 10); //=> '2009-11-10 10:10:10'* JSON.stringify(timeStamp); //=> '2009-11-10 10:10:10'** @memberOf patio.sql* @param {Number} [y=1970] the year* @param {Number} [m=0] the month* @param {Number} [d=1] the day* @param {Number} [h=0] the hour* @param {Number} [min=0] the minute/s* @param {Number} [s=0] the second/s* @param {Number} [ms=0] the millisecond/s, this paramater is not be used, but may depending on the adapter.*/- 1
var TimeStamp = function (y, m, d, h, min, s, ms) {- 54
var args = argsToArray(arguments);- 54
if (args.length === 0) {- 1
this.date = new Date();- 53
} else if (isDate(y)) {- 49
this.date = y;} else {- 4
var date = new Date(1970, 0, 1, 0, 0, 0);- 4
isNumber(y) && date.setYear(y);- 4
isNumber(m) && date.setMonth(m);- 4
isNumber(d) && date.setDate(d);- 4
isNumber(h) && date.setHours(h);- 4
isNumber(min) && date.setMinutes(min);- 4
isNumber(s) && date.setSeconds(s);- 4
isNumber(ms) && date.setMilliseconds(ms);- 4
this.date = date;}};- 1
TimeStamp.prototype.toJSON = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
TimeStamp.prototype.toString = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DATE_METHODS.forEach(function (op) {- 45
TimeStamp.prototype[op] = addDateMethod(op);}, this);/*** @constructor* Creates a DateTime type to be used in queries that require a SQL datetime datatype.* All <i>Date</i> methods ar included in the prototype of the DateTime type. toString and toJSON* are overridden to return a ISO8601 formatted date string instead of the default <i>Date</i> formatting.* See {@link patioTime#dateTimeToString} for formatting information.** @example** var dateTime = patio.DateTime(2009, 10, 10, 10, 10, 10); //=> '2009-11-10 10:10:10'* JSON.stringify(dateTime); //=> '2009-11-10 10:10:10'** @memberOf patio.sql* @param {Number} [y=1970] the year* @param {Number} [m=0] the month* @param {Number} [d=1] the day* @param {Number} [h=0] the hour* @param {Number} [min=0] the minute/s* @param {Number} [s=0] the second/s* @param {Number} [ms=0] the millisecond/s, this paramater is not be used, but may depending on the adapter.*/- 1
var DateTime = function (y, m, d, h, min, s, ms) {- 83
var args = argsToArray(arguments);- 83
if(args.length === 0){- 0
this.date = new Date();- 83
}else if (isDate(y)) {- 77
this.date = y;} else {- 6
var date = new Date(1970, 0, 1, 0, 0, 0);- 6
isNumber(y) && date.setYear(y);- 6
isNumber(m) && date.setMonth(m);- 6
isNumber(d) && date.setDate(d);- 6
isNumber(h) && date.setHours(h);- 6
isNumber(min) && date.setMinutes(min);- 6
isNumber(s) && date.setSeconds(s);- 6
isNumber(ms) && date.setMilliseconds(ms);- 6
this.date = date;}};- 1
DateTime.prototype.toJSON = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DateTime.prototype.toString = function () {- 0
return isUndefined(this.date) ? this.date : sql.patio.dateToString(this);};- 1
DATE_METHODS.forEach(function (op) {- 45
DateTime.prototype[op] = addDateMethod(op);}, this);/*** @class Represents a SQL Float type, by default is converted to double precision* @param {Number} number the number to be represented as a float* @memberOf patio.sql*/- 1
var Float = function (number) {- 0
this.number = number;};- 1
Float.prototype.toJSON = function () {- 0
return this.number;};/*** @class* Represents a SQL Decimal type, by default is converted to double precision* @param {Number} number the number to be represented as a decimal* @memberOf patio.sql*/- 1
var Decimal = function (number) {- 0
this.number = number;};- 1
Decimal.prototype.toJSON = function () {- 0
return this.number;};- 1
var hashToArray = function (hash) {- 3
var ret = [];- 3
if (isHash(hash)) {- 3
for (var i in hash) {- 3
var k = sql.stringToIdentifier(i), v = hash[i];- 3
v = isHash(v) ? hashToArray(v) : v;- 3
ret.push([k , v]);}}- 3
return ret;};/*** @namespace Collection of SQL related types, and expressions.** <p>* The {@link patio.sql} object* can be used directly to create {@link patio.sql.Expression}s, {@link patio.sql.Identifier}s, {@link patio.sql.SQLFunction}s,* and {@link patio.sql.QualifiedIdentifier}s.* <pre class='code'>* var sql = patio.sql;* //creating an identifier* sql.a; //=> a;** //creating a qualified identifier* sql.table__column; //table.column;** //BooleanExpression* sql.a.lt(sql.b); //=> a < 'b';** //SQL Functions* sql.sum(sql.a); //=> sum(a)* sql.avg(sql.b); //=> avg(b)* sql.a("b", 1); //=> a(b, 1)* sql.myDatabasesObjectFunction(sql.a, sql.b, sql.c); //=> myDatabasesObjectFunction(a, b, c);** //combined* sql.a.cast("boolean"); //=> 'CAST(a AS boolean)'* sql.a.plus(sql.b).lt(sql.c.minus(3) //=> ((a + b) < (c - 3))** </pre>** This is useful when combined with dataset filtering** <pre class="code">* var ds = DB.from("t");** ds.filter({a:[sql.b, sql.c]}).sql;* //=> SELECT * FROM t WHERE (a IN (b, c))** ds.select(sql["case"]({b:{c:1}}, false)).sql;* //=> SELECT (CASE WHEN b THEN (c = 1) ELSE 'f' END) FROM t** ds.select(sql.a).qualifyToFirstSource().sql;* //=> SELECT a FROM t** ds.order(sql.a.desc(), sql.b.asc()).sql;* //=> SELECT * FROM t ORDER BY a DESC, b ASC** ds.select(sql.a.as("b")).sql;* //=> SELECT a AS b FROM t** ds.filter(sql["case"]({a:sql.b}, sql.c, sql.d)).sql* //=> SELECT * FROM t WHERE (CASE d WHEN a THEN b ELSE c END)** ds.filter(sql.a.cast("boolean")).sql;* //=> SELECT * FROM t WHERE CAST(a AS boolean)** ds.filter(sql.a("b", 1)).sql* //=> SELECT * FROM t WHERE a(b, 1)* ds.filter(sql.a.plus(sql.b).lt(sql.c.minus(3)).sql;* //=> SELECT * FROM t WHERE ((a + b) < (c - 3))** ds.filter(sql.a.sqlSubscript(sql.b, 3)).sql;* //=> SELECT * FROM t WHERE a[b, 3]** ds.filter('? > ?', sql.a, 1).sql;* //=> SELECT * FROM t WHERE (a > 1);** ds.filter('{a} > {b}', {a:sql.c, b:1}).sql;* //=>SELECT * FROM t WHERE (c > 1)** ds.select(sql.literal("'a'"))* .filter(sql.a(3))* .filter('blah')* .order(sql.literal(true))* .group(sql.literal('a > ?', [1]))* .having(false).sql;* //=>"SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true");</pre>** </p>* @name sql* @memberOf patio*/- 1
var sql = {/**@lends patio.sql*//*** Returns a {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},* or {@link patio.sql.ALiasedExpression} depending on the format of the string* passed in.** <ul>* <li>For columns : table__column___alias.</li>* <li>For tables : schema__table___alias.</li>* </ul>* each portion of the identifier is optional. See example below** @example** patio.sql.identifier("a") //= > new patio.sql.Identifier("a");* patio.sql.identifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);* patio.sql.identifier("table__column___alias");* //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);** @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},* or {@link patio.sql.AliasedExpression}.** @return {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.*/identifier:function (s) {- 1575
return sql.stringToIdentifier(s);},/*** @see patio.sql.identifier*/stringToIdentifier:function (name) {- 11853
!Dataset && (Dataset = require("./dataset"));- 11853
return new Dataset().stringToIdentifier(name);},/*** Creates a {@link patio.sql.LiteralString} or {@link patio.sql.PlaceHolderLiteralString}* depending on the arguments passed in. If a single string is passed in then* it is assumed to be a {@link patio.sql.LiteralString}. If more than one argument is* passed in then it is assumed to be a {@link patio.sql.PlaceHolderLiteralString}.** @example** //a literal string that will be placed in an SQL query with out quoting.* patio.sql.literal("a"); //=> new patio.sql.LiteralString('a');** //a placeholder string that will have ? replaced with the {@link patio.Dataset#literal} version of* //the arugment and replaced in the string.* patio.sql.literal("a = ?", 1) //=> a = 1* patio.sql.literal("a = ?", "b"); //=> a = 'b'* patio.sql.literal("a = {a} AND b = {b}", {a : 1, b : 2}); //=> a = 1 AND b = 2** @param {String ...} s variable number of arguments where the first argument* is a string. If multiple arguments are passed it is a assumed to be a {@link patio.sql.PlaceHolderLiteralString}** @return {patio.sql.LiteralString|patio.sql.PlaceHolderLiteralString} an expression that can be used as an argument* for {@link patio.Dataset} query methods.*/literal:function (s) {- 458
var args = argsToArray(arguments);- 458
return args.length > 1 ? PlaceHolderLiteralString.fromArgs(args) : new LiteralString(s);},/*** Returns a {@link patio.sql.CaseExpression}. See {@link patio.sql.CaseExpression} for argument types.** @example** sql["case"]({a:sql.b}, sql.c, sql.d); //=> (CASE t.d WHEN t.a THEN t.b ELSE t.c END)**/"case":function (hash, /*args**/opts) {- 2
var args = argsToArray(arguments, 1);- 2
return CaseExpression.fromArgs([hashToArray(hash)].concat(args));},/*** Creates a {@link patio.sql.StringExpression}** Return a {@link patio.sql.StringExpression} representing an SQL string made up of the* concatenation of this array's elements. If an joiner is passed* it is used in between each element of the array in the SQL* concatenation.** @example* patio.sql.sqlStringJoin(["a"]); //=> a* //you can use sql.* as a shortcut to get an identifier* patio.sql.sqlStringJoin([sql.identifier("a"), sql.b]);//=> a || b* patio.sql.sqlStringJoin([sql.a, 'b']) # SQL: a || 'b'* patio.sql.sqlStringJoin(['a', sql.b], ' '); //=> 'a' || ' ' || b*/sqlStringJoin:function (arr, joiner) {- 6
joiner = joiner || null;- 6
var args;- 6
arr = arr.map(function (a) {- 12
return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : sql.stringToIdentifier(a)});- 6
if (joiner) {- 4
var newJoiner = [];- 4
for (var i = 0; i < arr.length; i++) {- 9
newJoiner.push(joiner);}- 4
args = array.flatten(array.zip(arr, newJoiner));- 4
args.pop();} else {- 2
args = arr;}- 6
args = args.map(function (a) {- 17
return isInstanceOf(a, Expression, LiteralString, Boolean) || isNull(a) ? a : "" + a;});- 6
return StringExpression.fromArgs(["||"].concat(args));},Year:Year,TimeStamp:TimeStamp,Time:Time,DateTime:DateTime,Float:Float,Decimal:Decimal};- 1
sql.__defineGetter__("patio", function () {- 0
!patio && (patio = require("./index"));- 0
return patio;});- 1
exports.sql = methodMissing(sql, function (name) {- 1016
return virtualRow(name);});- 1
var OPERTATOR_INVERSIONS = {AND:"OR",OR:"AND",GT:"lte",GTE:"lt",LT:"gte",LTE:"gt",EQ:"neq",NEQ:"eq",LIKE:'NOT LIKE',"NOT LIKE":"LIKE",'!~*':'~*','~*':'!~*',"~":'!~',"IN":'NOTIN',"NOTIN":"IN","IS":'IS NOT',"ISNOT":"IS",NOT:"NOOP",NOOP:"NOT",ILIKE:'NOT ILIKE',NOTILIKE:"ILIKE"};// Standard mathematical operators used in +NumericMethods+- 1
var MATHEMATICAL_OPERATORS = {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"};// Bitwise mathematical operators used in +NumericMethods+- 1
var BITWISE_OPERATORS = {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"};- 1
var INEQUALITY_OPERATORS = {GT:">", GTE:">=", LT:"<", LTE:"<="};//Hash of ruby operator symbols to SQL operators, used in +BooleanMethods+- 1
var BOOLEAN_OPERATORS = {AND:"AND", OR:"OR"};//Operators that use IN/NOT IN for inclusion/exclusion- 1
var IN_OPERATORS = {IN:"IN", NOTIN:'NOT IN'};//Operators that use IS, used for special casing to override literal true/false values- 1
var IS_OPERATORS = {IS:"IS", ISNOT:'IS NOT'};//Operator symbols that take exactly two arguments- 1
var TWO_ARITY_OPERATORS = merge({EQ:'=',NEQ:'!=', LIKE:"LIKE","NOT LIKE":'NOT LIKE',ILIKE:"ILIKE","NOT ILIKE":'NOT ILIKE',"~":"~",'!~':"!~",'~*':"~*",'!~*':"!~*"}, INEQUALITY_OPERATORS, BITWISE_OPERATORS, IS_OPERATORS, IN_OPERATORS);//Operator symbols that take one or more arguments- 1
var N_ARITY_OPERATORS = merge({"||":"||"}, BOOLEAN_OPERATORS, MATHEMATICAL_OPERATORS);//Operator symbols that take only a single argument- 1
var ONE_ARITY_OPERATORS = {"NOT":"NOT", "NOOP":"NOOP"};/*** @class Mixin to provide alias methods to an expression.** @name AliasMethods* @memberOf patio.sql*/- 1
var AliasMethods = define(null, {instance:{/**@lends patio.sql.AliasMethods.prototype*//*** Create an SQL alias {@link patio.sql.AliasedExpression} of the receiving column or expression* to the given alias.** @example** sql.identifier("column").as("alias");* //=> "column" AS "alias"** @param {String} alias the alias to assign to the expression.** @return {patio.sql.AliasedExpression} the aliased expression.*/as:function (alias) {- 552
return new AliasedExpression(this, alias);}}}).as(sql, "AliasMethods");- 1
var bitWiseMethod = function (op) {- 5
return function (expression) {- 0
if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, BooleanExpression)) {- 0
throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");}else {- 0
return new BooleanExpression(op, this, expression);}}};/*** @class Defines the bitwise methods: bitWiseAnd, bitWiseOr, exclusiveOr, leftShift, and rightShift. These* methods are only on {@link patio.sql.NumericExpression}** @example* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"* sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"* sql.a.sqlNumber.leftShift("b") // "a" << "b"* sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"** @name BitWiseMethods* @memberOf patio.sql*/- 1
var BitWiseMethods = define(null, {instance:{/**@lends patio.sql.BitWiseMethods.prototype*//*** Bitwise and** @example* sql.a.sqlNumber.bitWiseAnd("b"); //=> "a" & "b"*/bitWiseAnd:bitWiseMethod("bitWiseAnd"),/*** Bitwise or** @example* sql.a.sqlNumber.bitWiseOr("b") //=> "a" | "b"*/bitWiseOr:bitWiseMethod("bitWiseOr"),/*** Exclusive Or** @example** sql.a.sqlNumber.exclusiveOr("b") //=> "a" ^ "b"*/exclusiveOr:bitWiseMethod("exclusiveOr"),/*** Bitwise shift left** @example** sql.a.sqlNumber.leftShift("b") // "a" << "b"*/leftShift:bitWiseMethod("leftShift"),/*** Bitwise shift right** @example** sql.a.sqlNumber.rightShift("b") //=> "a" >> "b"*/rightShift:bitWiseMethod("rightShift")}}).as(sql, "BitWiseMethods");- 1
var booleanMethod = function (op) {- 2
return function (expression) {- 7
if (isInstanceOf(expression, StringExpression) || isInstanceOf(expression, NumericExpression)) {- 0
throw new ExpressionError("Cannot apply " + op + " to a non boolean expression");}else {- 7
return new BooleanExpression(op, this, expression);}}};/*** @class Defines boolean/logical AND (&), OR (|) and NOT (~) operators* that are defined on objects that can be used in a boolean context in SQL* ({@link patio.sql.LiteralString}, and {@link patio.sql.GenericExpression}).** @example* sql.a.and(sql.b) //=> "a" AND "b"* sql.a.or(sql.b) //=> "a" OR "b"* sql.a.not() //=> NOT "a"** @name BooleanMethods* @memberOf patio.sql*/- 1
var BooleanMethods = define(null, {instance:{/**@lends patio.sql.BooleanMethods.prototype*//**** @function* Logical AND** @example** sql.a.and(sql.b) //=> "a" AND "b"** @return {patio.sql.BooleanExpression} a ANDed boolean expression.*/and:booleanMethod("and"),/*** @function* Logical OR** @example** sql.a.or(sql.b) //=> "a" OR "b"** @return {patio.sql.BooleanExpression} a ORed boolean expression*/or:booleanMethod("or"),/*** Logical NOT** @example** sql.a.not() //=> NOT "a"** @return {patio.sql.BooleanExpression} a inverted boolean expression.*/not:function () {- 5
return BooleanExpression.invert(this);}}}).as(sql, "BooleanMethods");/*** @class Defines case methods** @name CastMethods* @memberOf patio.sql*/- 1
var CastMethods = define(null, {instance:{/**@lends patio.sql.CastMethods.prototype*//*** Cast the reciever to the given SQL type.** @example** sql.a.cast("integer") //=> CAST(a AS integer)* sql.a.cast(String) //=> CAST(a AS varchar(255))** @return {patio.sql.Cast} the casted expression*/cast:function (type) {- 2
return new Cast(this, type);},/*** Cast the reciever to the given SQL type (or the database's default Number type if none given.** @example** sql.a.castNumeric() //=> CAST(a AS integer)* sql.a.castNumeric("double") //=> CAST(a AS double precision)** @param type the numeric type to cast to** @return {patio.sql.NumericExpression} a casted numberic expression*/castNumeric:function (type) {- 0
return this.cast(type || "integer").sqlNumber;},/*** Cast the reciever to the given SQL type (or the database's default String type if none given),* and return the result as a {@link patio.sql.StringExpression}.** @example** sql.a.castString() //=> CAST(a AS varchar(255))* sql.a.castString("text") //=> CAST(a AS text)* @param type the string type to cast to** @return {patio.sql.StringExpression} the casted string expression*/castString:function (type) {- 0
return this.cast(type || String).sqlString;}}}).as(sql, "CastMethods");/*** @class Provides methods to assist in assigning a SQL type to* particular types, i.e. Boolean, Function, Number or String.** @name ComplexExpressionMethods* @memberOf patio.sql* @property {patio.sql.BooleanExpression} sqlBoolean Return a {@link patio.sql.BooleanExpression} representation of this expression type.* @property {patio.sql.BooleanExpression} sqlFunction Return a {@link patio.sql.SQLFunction} representation of this expression type.* @property {patio.sql.BooleanExpression} sqlNumber Return a {@link patio.sql.NumericExpression} representation of this expression type.* <pre class="code">* sql.a.not("a") //=> NOT "a"* sql.a.sqlNumber.not() //=> ~"a"* </pre>* @property {patio.sql.BooleanExpression} sqlString Return a {@link patio.sql.StringExpression} representation of this expression type.* <pre class="code">* sql.a.plus(sql.b); //=> "a" + "b"* sql.a.sqlString.concat(sql.b) //=> "a" || "b"* </pre>*/- 1
var ComplexExpressionMethods = define(null, {instance:{/**@ignore*/getters:{/*** @ignore*/sqlBoolean:function () {- 0
return new BooleanExpression("noop", this);},/*** @ignore*/sqlFunction:function () {- 13
return new SQLFunction(this);},/*** @ignore*/sqlNumber:function () {- 50
return new NumericExpression("noop", this);},/*** @ignore*/sqlString:function () {- 0
return new StringExpression("noop", this);}}}}).as(sql, "ComplexExpressionMethods");- 1
var inequalityMethod = function (op) {- 6
return function (expression) {- 88
if (isInstanceOf(expression, BooleanExpression)|| isBoolean(expression)|| isNull(expression)|| (isHash(expression))|| isArray(expression)) {- 0
throw new ExpressionError("Cannot apply " + op + " to a boolean expression");} else {- 88
return new BooleanExpression(op, this, expression);}}};/*** @class This mixin includes the inequality methods (>, <, >=, <=) that are defined on objects that can be* used in a numeric or string context in SQL.** @example* sql.a.gt("b") //=> a > "b"* sql.a.lt("b") //=> a > "b"* sql.a.gte("b") //=> a >= "b"* sql.a.lte("b") //=> a <= "b"* sql.a.eq("b") //=> a = "b"** @name InequalityMethods* @memberOf patio.sql*/- 1
var InequalityMethods = define(null, {instance:{/**@lends patio.sql.InequalityMethods.prototype*//*** @function Creates a gt {@link patio.sql.BooleanExpression} compared to this expression.* @example** sql.a.gt("b") //=> a > "b"** @return {patio.sql.BooleanExpression}*/gt:inequalityMethod("gt"),/*** @function Creates a gte {@link patio.sql.BooleanExpression} compared to this expression.** @example** sql.a.gte("b") //=> a >= "b"** @return {patio.sql.BooleanExpression}*/gte:inequalityMethod("gte"),/*** @function Creates a lt {@link patio.sql.BooleanExpression} compared to this expression.** @example** sql.a.lt("b") //=> a < "b"** @return {patio.sql.BooleanExpression}*/lt:inequalityMethod("lt"),/*** @function Creates a lte {@link patio.sql.BooleanExpression} compared to this expression.** @example** sql.a.lte("b") //=> a <= "b"** @return {patio.sql.BooleanExpression}*/lte:inequalityMethod("lte"),/*** @function Creates a eq {@link patio.sql.BooleanExpression} compared to this expression.** @example** sql.a.eq("b") //=> a = "b"** @return {patio.sql.BooleanExpression}*/eq:inequalityMethod("eq"),neq:inequalityMethod("neq"),/*** @private** Creates a boolean expression where the key is '>=' value 1 and '<=' value two.** @example** sql.x.between([1,2]) => //=> WHERE ((x >= 1) AND (x <= 10))* sql.x.between([1,2]).invert() => //=> WHERE ((x < 1) OR (x > 10))** @param {Object} items a two element array where the first element it the item to be gte and the second item lte.** @return {patio.sql.BooleanExpression} a boolean expression containing the between expression.*/between:function (items) {- 6
return new BooleanExpression("AND", new BooleanExpression("gte", this, items[0]), new BooleanExpression("lte", this, items[1]))}}}).as(sql, "InequalityMethods");/*** @class This mixin augments the default constructor for {@link patio.sql.ComplexExpression},* so that attempting to use boolean input when initializing a {@link patio.sql.NumericExpression}* or {@link patio.sql.StringExpression} results in an error. <b>It is not expected to be used directly.</b>** @name NoBooleanInputMethods* @memberOf patio.sql*/- 1
var NoBooleanInputMethods = define(null, {instance:{constructor:function (op) {- 22
var args = argsToArray(arguments, 1);- 22
args.forEach(function (expression) {- 26
if ((isInstanceOf(expression, BooleanExpression))|| isBoolean(expression)|| isNull(expression)|| (isObject(expression) && !isInstanceOf(expression, Expression, Dataset, LiteralString))|| isArray(expression)) {- 0
throw new ExpressionError("Cannot apply " + op + " to a boolean expression");}});- 22
this._super(arguments);}}}).as(sql, "NoBooleanInputMethods");- 1
var numericMethod = function (op) {- 4
return function (expression) {- 12
if (isInstanceOf(expression, BooleanExpression, StringExpression)) {- 0
throw new ExpressionError("Cannot apply " + op + " to a non numeric expression");} else {- 12
return new NumericExpression(op, this, expression);}}};/*** @class This mixin includes the standard mathematical methods (+, -, *, and /)* that are defined on objects that can be used in a numeric context in SQL.** @example* sql.a.plus(sql.b) //=> "a" + "b"* sql.a.minus(sql.b) //=> "a" - "b"* sql.a.multiply(sql.b) //=> "a" * "b"* sql.a.divide(sql.b) //=> "a" / "b"** @name NumericMethods* @memberOf patio.sql*/- 1
var NumericMethods = define(null, {instance:{/**@lends patio.sql.NumericMethods.prototype*//*** @function Adds the provided expression to this expression and returns a {@link patio.sql.NumericExpression}.** @example** sql.a.plus(sql.b) //=> "a" + "b"** @return {patio.sql.NumericExpression}*/plus:numericMethod("plus"),/*** @function Subtracts the provided expression from this expression and returns a {@link patio.sql.NumericExpression}.** @example** sql.a.minus(sql.b) //=> "a" - "b"** @return {patio.sql.NumericExpression}*/minus:numericMethod("minus"),/*** @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.** @example** sql.a.divide(sql.b) //=> "a" / "b"** @return {patio.sql.NumericExpression}*/divide:numericMethod("divide"),/*** @function Divides this expression by the provided expression and returns a {@link patio.sql.NumericExpression}.** @example** sql.a.multiply(sql.b) //=> "a" * "b"** @return {patio.sql.NumericExpression}*/multiply:numericMethod("multiply")}}).as(sql, "NumericMethods");/*** @class This mixin provides ordering methods ("asc", "desc") to expression.** @example** sql.name.asc(); //=> name ASC* sql.price.desc(); //=> price DESC* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST** @name OrderedMethods* @memberOf patio.sql*/- 1
var OrderedMethods = define(null, {instance:{/**@lends patio.sql.OrderedMethods.prototype*//*** Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).** @example* sql.name.asc(); //=> name ASC* sql.name.asc({nulls:"last"}); //=> name ASC NULLS LAST** @param {Object} [options] options to use when sorting* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).* @return {patio.sql.OrderedExpression}*/asc:function (options) {- 7
return new OrderedExpression(this, false, options);},/*** Mark the receiving SQL column as sorting in a descending fashion.* @example** sql.price.desc(); //=> price DESC* sql.price.desc({nulls:"first"}); //=> price DESC NULLS FIRST** @param {Object} [options] options to use when sorting* @param {String} [options.nulls = null] Set to "first" to use NULLS FIRST (so NULL values are ordered* before other values), or "last" to use NULLS LAST (so NULL values are ordered after other values).* @return {patio.sql.OrderedExpression}*/desc:function (options) {- 26
return new OrderedExpression(this, true, options);}}}).as(sql, "OrderedMethods");/*** @class This mixin provides methods related to qualifying expression.** @example** sql.column.qualify("table") //=> "table"."column"* sql.table.qualify("schema") //=> "schema"."table"* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"** @name QualifyingMethods* @memberOf patio.sql*/- 1
var QualifyingMethods = define(null, {instance:{/**@lends patio.sql.QualifyingMethods.prototype*//*** Qualify the receiver with the given qualifier (table for column/schema for table).** @example* sql.column.qualify("table") //=> "table"."column"* sql.table.qualify("schema") //=> "schema"."table"* sql.column.qualify("table").qualify("schema") //=> "schema"."table"."column"** @param {String|patio.sql.Identifier} qualifier table/schema to qualify this expression to.** @return {patio.sql.QualifiedIdentifier}*/qualify:function (qualifier) {- 578
return new QualifiedIdentifier(qualifier, this);},/*** Use to create a .* expression.** @example* sql.table.all() //=> "table".** sql.table.qualify("schema").all() //=> "schema"."table".**** @return {patio.sql.ColumnAll}*/all:function () {- 3
return new ColumnAll(this);}}}).as(sql, "QualifyingMethods");/*** @class This mixin provides SQL string methods such as (like and iLike).** @example** sql.a.like("A%"); //=> "a" LIKE 'A%'* sql.a.iLike("A%"); //=> "a" LIKE 'A%'* sql.a.like(/^a/); //=> "a" ~* '^a'** @name StringMethods* @memberOf patio.sql*/- 1
var StringMethods = define(null, {instance:{/**@lends patio.sql.StringMethods.prototype*//*** Create a {@link patio.sql.BooleanExpression} case insensitive pattern match of the receiver* with the given patterns. See {@link patio.sql.StringExpression#like}.** @example* sql.a.iLike("A%"); //=> "a" LIKE 'A%'** @return {patio.sql.BooleanExpression}*/ilike:function (expression) {- 310
expression = argsToArray(arguments);- 310
return StringExpression.like.apply(StringExpression, [this].concat(expression).concat([{caseInsensitive:true}]));},/*** Create a {@link patio.sql.BooleanExpression} case sensitive (if the database supports it) pattern match of the receiver with* the given patterns. See {@link patio.sql.StringExpression#like}.** @example* sql.a.like(/^a/); //=> "a" ~* '^a'* sql.a.like("A%"); //=> "a" LIKE 'A%'** @param expression*/like:function (expression) {- 11
expression = argsToArray(arguments);- 11
return StringExpression.like.apply(StringExpression, [this].concat(expression));}}}).as(sql, "StringMethods");/*** @class This mixin provides string concatenation methods ("concat");** @example** sql.x.sqlString.concat("y"); //=> "x" || "y"** @name StringConcatenationMethods* @memberOf patio.sql*/- 1
var StringConcatenationMethods = define(null, {instance:{/**@lends patio.sql.StringConcatenationMethods.prototype*//*** Return a {@link patio.sql.StringExpression} representing the concatenation of this expression* with the given argument.** @example** sql.x.sqlString.concat("y"); //=> "x" || "y"** @param expression expression to concatenate this expression with.*/concat:function (expression) {- 0
return new StringExpression("||", this, expression);}}}).as(sql, "StringConcatenationMethods");/*** @class This mixin provides the ability to access elements within a SQL array.** @example* sql.array.sqlSubscript(1) //=> array[1]* sql.array.sqlSubscript(1, 2) //=> array[1, 2]* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]** @name SubscriptMethods* @memberOf patio.sql*/- 1
var SubscriptMethods = define(null, {instance:{/*** Return a {@link patio.sql.Subscript} with the given arguments, representing an* SQL array access.** @example* sql.array.sqlSubscript(1) //=> array[1]* sql.array.sqlSubscript(1, 2) //=> array[1, 2]* sql.array.sqlSubscript([1, 2]) //=> array[1, 2]** @param subscript*/sqlSubscript:function (subscript) {- 75
var args = argsToArray(arguments);- 75
return new SubScript(this, flatten(args));}}}).as(sql, "SubScriptMethods");/*** @class This is the parent of all expressions.** @name Expression* @memberOf patio.sql*/- 1
var Expression = define(null, {instance:{/**@lends patio.sql.Expression.prototype*//*** Returns the string representation of this expression** @param {patio.Dataset} ds the dataset that will be used to SQL-ify this expression.* @return {String} a string literal version of this expression.*/sqlLiteral:function (ds) {- 0
return this.toString(ds);}},static:{/**@lends patio.sql.Expression*//*** This is a helper method that will take in an array of arguments and return an expression.** @example** QualifiedIdentifier.fromArgs(["table", "column"]);** @param {*[]} args array of arguments to pass into the constructor of the function.** @return {patio.sql.Expression} an expression.*/fromArgs:function (args) {- 2327
var ret;- 2327
try {- 2327
ret = new this();} catch (ignore) {}- 2327
this.apply(ret, args);- 2327
return ret;},/*** Helper to determine if something is a condition specifier. Returns true if the object* is a Hash or is an array of two element arrays.** @example* Expression.isConditionSpecifier({a : "b"}); //=> true* Expression.isConditionSpecifier("a"); //=> false* Expression.isConditionSpecifier([["a", "b"], ["c", "d"]]); //=> true* Expression.isConditionSpecifier([["a", "b", "e"], ["c", "d"]]); //=> false** @param {*} obj object to test if it is a condition specifier* @return {Boolean} true if the object is a Hash or is an array of two element arrays.*/isConditionSpecifier:function (obj) {- 22237
return isHash(obj) || (isArray(obj) && obj.length && obj.every(function (i) {- 9576
return isArray(i) && i.length === 2;}));}}}).as(sql, "Expression");/*** @class Base class for all GenericExpressions** @augments patio.sql.Expression* @augments patio.sql.AliasMethods* @augments patio.sql.BooleanMethods* @augments patio.sql.CastMethods* @augments patio.sql.ComplexExpressionMethods* @augments patio.sql.InequalityMethods* @augments patio.sql.NumericMethods* @augments patio.sql.OrderedMethods* @augments patio.sql.StringMethods* @augments patio.sql.SubscriptMethods** @name GenericExpression* @memberOf patio.sql*/- 1
var GenericExpression = define([Expression, AliasMethods, BooleanMethods, CastMethods, ComplexExpressionMethods, InequalityMethods, NumericMethods, OrderedMethods, StringMethods, SubscriptMethods]).as(sql, "GenericExpression");- 1
var AliasedExpression = define(Expression, {instance:{/**@lends patio.sql.AliasedExpression.prototype*//*** This class reperesents an Aliased Expression** @constructs* @augments patio.sql.Expression** @param expression the expression to alias.* @param alias the alias to alias the expression to.** @property expression the expression being aliased* @property alias the alias of the expression**/constructor:function (expression, alias) {- 910
this.expression = expression;- 910
this.alias = alias;},/*** Converts the aliased expression to a string* @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if* the dataset is ommited then the default {@link patio.Dataset} implementation is used.** @return String the SQL alias fragment.*/toString:function (ds) {- 842
!Dataset && (Dataset = require("./dataset"));- 842
ds = ds || new Dataset();- 842
return ds.aliasedExpressionSql(this);}}}).as(sql, "AliasedExpression");- 1
var CaseExpression = define(GenericExpression, {instance:{/**@lends patio.sql.CaseExpression.prototype*//*** Create an object with the given conditions and* default value. An expression can be provided to* test each condition against, instead of having* all conditions represent their own boolean expression.** @constructs* @augments patio.sql.GenericExpression* @param {Array|Object} conditions conditions to create the case expression from* @param def default value* @param expression expression to create the CASE expression from** @property {Boolean} hasExpression returns true if this case expression has a expression* @property conditions the conditions of the {@link patio.sql.CaseExpression}.* @property def the default value of the {@link patio.sql.CaseExpression}.* @property expression the expression of the {@link patio.sql.CaseExpression}.* @property {Boolean} noExpression true if this {@link patio.sql.CaseExpression}'s expression is undefined.*/constructor:function (conditions, def, expression) {- 8
if (Expression.isConditionSpecifier(conditions)) {- 4
this.conditions = toArray(conditions);- 4
this.def = def;- 4
this.expression = expression;- 4
this.noExpression = isUndefined(expression);}},/*** Converts the case expression to a string** @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if* the dataset is ommited then the default {@link patio.Dataset} implementation is used.** @return String the SQL case expression fragment.*/toString:function (ds) {- 2
!Dataset && (Dataset = require("./dataset"));- 2
ds = ds || new Dataset();- 2
return ds.caseExpressionSql(this);},/**@ignore*/getters:{/**@ignore*/hasExpression:function () {- 2
return !this.noExpression;}}}}).as(sql, "CaseExpression");- 1
var Cast = define(GenericExpression, {instance:{/**@lends patio.sql.Cast*//*** Represents a cast of an SQL expression to a specific type.* @constructs* @augments patio.sql.GenericExpression** @param expr the expression to CAST.* @param type the type to CAST the expression to.** @property expr the expression to CAST.* @property type the type to CAST the expression to.*/constructor:function (expr, type) {- 3
this.expr = expr;- 3
this.type = type;},/*** Converts the cast expression to a string** @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if* the dataset is ommited then the default {@link patio.Dataset} implementation is used.** @return String the SQL cast expression fragment.*/toString:function (ds) {- 2
!Dataset && (Dataset = require("./dataset"));- 2
ds = ds || new Dataset();- 2
return ds.castSql(this.expr, this.type);}}}).as(sql, "Cast");- 1
var ColumnAll = define(Expression, {instance:{/**@lends patio.sql.ColumnAll.prototype*//*** Represents all columns in a given table, table.* in SQL* @constructs** @augments patio.sql.Expression** @param table the table this expression is for.** @property table the table this all column expression represents.*/constructor:function (table) {- 20
this.table = table;},/*** Converts the ColumnAll expression to a string** @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if* the dataset is ommited then the default {@link patio.Dataset} implementation is used.** @return String the SQL columnAll expression fragment.*/toString:function (ds) {- 19
!Dataset && (Dataset = require("./dataset"));- 19
ds = ds || new Dataset();- 19
return ds.columnAllSql(this);}}}).as(sql, "ColumnAll");- 1
var ComplexExpression = define([Expression, AliasMethods, CastMethods, OrderedMethods, SubscriptMethods], {instance:{/**@lends patio.sql.ComplexExpression.prototype*//*** Represents a complex SQL expression, with a given operator and one* or more attributes (which may also be ComplexExpressions, forming* a tree).** This is an abstract class that is not that useful by itself. The* subclasses @link patio.sql.BooleanExpression},* {@link patio.sql.NumericExpression} and {@link patio.sql.StringExpression} should* be used instead of this class directly.** @constructs* @augments patio.sql.Expression* @augments patio.sql.AliasMethods* @augments patio.sql.CastMethods* @augments patio.sql.OrderedMethods* @augments patio.sql.SubscriptMethods** @throws {patio.sql.ExpressionError} if the operator doesn't allow boolean input and a boolean argument is given.* @throws {patio.sql.ExpressionError} if the wrong number of arguments for a given operator is used.** @param {...} op The operator and arguments for this object to the ones given.* <p>* Convert all args that are hashes or arrays of two element arrays to {@link patio.sql.BooleanExpression}s,* other than the second arg for an IN/NOT IN operator.</li>* </p>*/constructor:function (op) {- 7746
if (op) {- 6989
var args = argsToArray(arguments,1 );//make a copy of the args- 6989
var origArgs = args.slice(0);- 6989
args.forEach(function (a, i) {- 14194
if (Expression.isConditionSpecifier(a)) {- 6
args[i] = BooleanExpression.fromValuePairs(a);}});- 6989
op = op.toUpperCase();- 6989
if (N_ARITY_OPERATORS.hasOwnProperty(op)) {- 1134
if (args.length < 1) {- 0
throw new ExpressionError("The " + op + " operator requires at least 1 argument")}- 1134
var oldArgs = args.slice(0);- 1134
args = [];- 1134
oldArgs.forEach(function (a) {- 2547
a instanceof ComplexExpression && a.op == op ? args = args.concat(a.args) : args.push(a);});- 5855
} else if (TWO_ARITY_OPERATORS.hasOwnProperty(op)) {- 5792
if (args.length != 2) {- 0
throw new ExpressionError("The " + op + " operator requires precisely 2 arguments");}//With IN/NOT IN, even if the second argument is an array of two element arrays,//don't convert it into a boolean expression, since it's definitely being used//as a value list.- 5792
if (IN_OPERATORS[op]) {- 23
args[1] = origArgs[1]}- 63
} else if (ONE_ARITY_OPERATORS.hasOwnProperty(op)) {- 63
if (args.length != 1) {- 0
throw new ExpressionError("The " + op + " operator requires only one argument");}} else {- 0
throw new ExpressionError("Invalid operator " + op);}- 6989
this.op = op;- 6989
this.args = args;}},/*** Converts the ComplexExpression to a string.** @param {patio.Dataset} [ds] dataset used to created the SQL fragment, if* the dataset is ommited then the default {@link patio.Dataset} implementation is used.** @return String the SQL version of the {@link patio.sql.ComplexExpression}.*/toString:function (ds) {- 6586
!Dataset && (Dataset = require("./dataset"));- 6586
ds = ds || new Dataset();- 6586
return ds.complexExpressionSql(this.op, this.args);}},static:{/**@lends patio.sql.ComplexExpression*//*** Hash of operator inversions* @type Object* @default {* AND:"OR",* OR:"AND",* GT:"lte",* GTE:"lt",* LT:"gte",* LTE:"gt",* EQ:"neq",* NEQ:"eq",* LIKE:'NOT LIKE',* "NOT LIKE":"LIKE",* '!~*':'~*',* '~*':'!~*',* "~":'!~',* "IN":'NOTIN',* "NOTIN":"IN",* "IS":'IS NOT',* "ISNOT":"IS",* NOT:"NOOP",* NOOP:"NOT",* ILIKE:'NOT ILIKE',* NOTILIKE:"ILIKE"* }*/OPERATOR_INVERSIONS:OPERTATOR_INVERSIONS,/*** Default mathematical operators.** @type Object* @default {PLUS:"+", MINUS:"-", DIVIDE:"/", MULTIPLY:"*"}*/MATHEMATICAL_OPERATORS:MATHEMATICAL_OPERATORS,/*** Default bitwise operators.** @type Object* @default {bitWiseAnd:"&", bitWiseOr:"|", exclusiveOr:"^", leftShift:"<<", rightShift:">>"}*/BITWISE_OPERATORS:BITWISE_OPERATORS,/*** Default inequality operators.** @type Object* @default {GT:">",GTE:">=",LT:"<",LTE:"<="}*/INEQUALITY_OPERATORS:INEQUALITY_OPERATORS,/*** Default boolean operators.** @type Object* @default {AND:"AND",OR:"OR"}*/BOOLEAN_OPERATORS:BOOLEAN_OPERATORS,/*** Default IN operators.** @type Object* @default {IN:"IN",NOTIN:'NOT IN'}*/IN_OPERATORS:IN_OPERATORS,/*** Default IS operators.** @type Object* @default {IS:"IS", ISNOT:'IS NOT'}*/