sequelize
Advanced tools
Comparing version 1.7.0-alpha2 to 2.0.0-alpha1
@@ -0,1 +1,4 @@ | ||
# v2.0.0 (alpha1) # | ||
- [FEATURE] async validations. [#580](https://github.com/sequelize/sequelize/pull/580). thanks to Interlock | ||
# v1.7.0 # | ||
@@ -11,2 +14,3 @@ - [DEPENDENCIES] Upgraded validator for IPv6 support. [#603](https://github.com/sequelize/sequelize/pull/603). thanks to durango | ||
- [BUG] Allow overriding of default columns. [#635](https://github.com/sequelize/sequelize/pull/635). Thanks to sevastos | ||
- [BUG] Fix where params for belongsTo [#658](https://github.com/sequelize/sequelize/pull/658). Thanks to mweibel | ||
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango | ||
@@ -22,2 +26,3 @@ - [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango | ||
- [FEATURE] Added model wide validations. [#640](https://github.com/sequelize/sequelize/pull/640). Thanks to tremby | ||
- [FEATURE] `findOrCreate` now returns an additional flag (`created`), that is true if a model was created, and false if it was found [#648](https://github.com/sequelize/sequelize/pull/648). janmeier | ||
@@ -24,0 +29,0 @@ # v1.6.0 # |
@@ -45,4 +45,6 @@ var Utils = require("./../utils") | ||
if (!Utils._.isUndefined(params)) { | ||
if (!Utils._.isUndefined(params.attributes)) { | ||
params = Utils._.extend({where: {id:id}}, params) | ||
if (!Utils._.isUndefined(params.where)) { | ||
params.where = Utils._.extend({id:id}, params.where) | ||
} else { | ||
params.where = {id: id} | ||
} | ||
@@ -49,0 +51,0 @@ } else { |
@@ -216,3 +216,3 @@ var Utils = require("./utils") | ||
var optcpy = Utils._.clone(options) | ||
optcpy.attributes = optcpy.attributes || [Utils.addTicks(this.tableName)+".*"] | ||
optcpy.attributes = optcpy.attributes || [this.QueryInterface.quoteIdentifier(this.tableName)+".*"] | ||
@@ -346,3 +346,3 @@ // whereCollection is used for non-primary key updates | ||
.success(function (instance) { | ||
emitter.emit('success', instance) | ||
emitter.emit('success', instance, true) | ||
}) | ||
@@ -353,3 +353,3 @@ .error( function (error) { | ||
} else { | ||
emitter.emit('success', instance) | ||
emitter.emit('success', instance, false) | ||
} | ||
@@ -438,5 +438,8 @@ }).error(function (error) { | ||
* @param {Object} where Options to describe the scope of the search. | ||
* @param {Object} options Possible options are: | ||
- limit: How many rows to delete | ||
- truncate: If set to true, dialects that support it will use TRUNCATE instead of DELETE FROM. If a table is truncated the where and limit options are ignored | ||
* @return {Object} A promise which fires `success`, `error`, `complete` and `sql`. | ||
*/ | ||
DAOFactory.prototype.destroy = function(where) { | ||
DAOFactory.prototype.destroy = function(where, options) { | ||
if (this.options.timestamps && this.options.paranoid) { | ||
@@ -448,3 +451,3 @@ var attr = this.options.underscored ? 'deleted_at' : 'deletedAt' | ||
} else { | ||
return this.QueryInterface.bulkDelete(this.tableName, where) | ||
return this.QueryInterface.bulkDelete(this.tableName, where, options) | ||
} | ||
@@ -451,0 +454,0 @@ } |
122
lib/dao.js
@@ -1,10 +0,8 @@ | ||
var Utils = require("./utils") | ||
, Mixin = require("./associations/mixin") | ||
, Validator = require("validator") | ||
, DataTypes = require("./data-types") | ||
var Utils = require("./utils") | ||
, Mixin = require("./associations/mixin") | ||
, DaoValidator = require("./dao-validator") | ||
, DataTypes = require("./data-types") | ||
module.exports = (function() { | ||
var DAO = function(values, options, isNewRecord) { | ||
var self = this | ||
this.dataValues = {} | ||
@@ -141,3 +139,3 @@ this.__options = options | ||
text.push(this.QueryInterface.QueryGenerator.addQuotes(key) + '=>' + (typeof value === "string" ? this.QueryInterface.QueryGenerator.addQuotes(value) : value)) | ||
text.push(this.QueryInterface.quoteIdentifier(key) + '=>' + (typeof value === "string" ? this.QueryInterface.quoteIdentifier(value) : value)) | ||
}.bind(this)) | ||
@@ -154,23 +152,25 @@ values[attrName] = text.join(',') | ||
var errors = this.validate() | ||
return new Utils.CustomEventEmitter(function(emitter) { | ||
this.validate().success(function(errors) { | ||
if (!!errors) { | ||
emitter.emit('error', errors) | ||
} else if (this.isNewRecord) { | ||
this | ||
.QueryInterface | ||
.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values) | ||
.proxy(emitter) | ||
} else { | ||
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id }; | ||
if (!!errors) { | ||
return new Utils.CustomEventEmitter(function(emitter) { | ||
emitter.emit('error', errors) | ||
}).run() | ||
} | ||
else if (this.isNewRecord) { | ||
return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values) | ||
} else { | ||
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id; | ||
if (identifier === null && this.__options.whereCollection !== null) { | ||
identifier = this.__options.whereCollection; | ||
} | ||
if (identifier === null && this.__options.whereCollection !== null) { | ||
identifier = this.__options.whereCollection; | ||
} | ||
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory) | ||
, query = this.QueryInterface.update(this, tableName, values, identifier) | ||
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory) | ||
, query = this.QueryInterface.update(this, tableName, values, identifier) | ||
return query | ||
} | ||
query.proxy(emitter) | ||
} | ||
}.bind(this)) | ||
}.bind(this)).run() | ||
} | ||
@@ -187,3 +187,3 @@ | ||
var where = [ | ||
this.QueryInterface.QueryGenerator.addQuotes(this.__factory.tableName) + '.' + this.QueryInterface.QueryGenerator.addQuotes('id')+'=?', | ||
this.QueryInterface.quoteIdentifier(this.__factory.tableName) + '.' + this.QueryInterface.quoteIdentifier('id')+'=?', | ||
this.id | ||
@@ -217,73 +217,5 @@ ] | ||
DAO.prototype.validate = function() { | ||
var self = this | ||
var failures = {} | ||
// for each field and value | ||
Utils._.each(self.values, function(value, field) { | ||
// if field has validators | ||
var hasAllowedNull = (self.rawAttributes[field].allowNull && self.rawAttributes[field].allowNull === true && (value === null || value === undefined)); | ||
if (self.validators.hasOwnProperty(field) && !hasAllowedNull) { | ||
// for each validator | ||
Utils._.each(self.validators[field], function(details, validatorType) { | ||
var is_custom_fn = false // if true then it's a custom validation method | ||
var fn_method = null // the validation function to call | ||
var fn_args = [] // extra arguments to pass to validation function | ||
var fn_msg = "" // the error message to return if validation fails | ||
// is it a custom validator function? | ||
if (Utils._.isFunction(details)) { | ||
is_custom_fn = true | ||
fn_method = Utils._.bind(details, self, value) | ||
} | ||
// is it a validator module function? | ||
else { | ||
// extra args | ||
fn_args = details.hasOwnProperty("args") ? details.args : details | ||
if (!Array.isArray(fn_args)) | ||
fn_args = [fn_args] | ||
// error msg | ||
fn_msg = details.hasOwnProperty("msg") ? details.msg : false | ||
// check method exists | ||
var v = Validator.check(value, fn_msg) | ||
if (!Utils._.isFunction(v[validatorType])) | ||
throw new Error("Invalid validator function: " + validatorType) | ||
// bind to validator obj | ||
fn_method = Utils._.bind(v[validatorType], v) | ||
} | ||
try { | ||
fn_method.apply(null, fn_args) | ||
} catch (err) { | ||
err = err.message | ||
// if we didn't provide a custom error message then augment the default one returned by the validator | ||
if (!fn_msg && !is_custom_fn) | ||
err += ": " + field | ||
// each field can have multiple validation failures stored against it | ||
if (failures.hasOwnProperty(field)) { | ||
failures[field].push(err) | ||
} else { | ||
failures[field] = [err] | ||
} | ||
} | ||
}) // for each validator for this field | ||
} // if field has validator set | ||
}) // for each field | ||
// for each model validator for this DAO | ||
Utils._.each(self.__options.validate, function(validator, validatorType) { | ||
try { | ||
validator.apply(self) | ||
} catch (err) { | ||
failures[validatorType] = [err.message] // TODO: data structure needs to change for 2.0 | ||
} | ||
}) | ||
return (Utils._.isEmpty(failures) ? null : failures) | ||
return new DaoValidator(this).validate() | ||
} | ||
DAO.prototype.updateAttributes = function(updates, fields) { | ||
@@ -290,0 +222,0 @@ this.setAttributes(updates) |
@@ -5,12 +5,2 @@ var Utils = require("../../utils") | ||
var processAndEscapeValue = function(value) { | ||
var processedValue = value | ||
if (value instanceof Date) { | ||
processedValue = Utils.toSqlDate(value) | ||
} else if (typeof value === 'boolean') { | ||
processedValue = value ? 1 : 0 | ||
} | ||
return Utils.escape(processedValue) | ||
} | ||
module.exports = (function() { | ||
@@ -34,3 +24,3 @@ var QueryGenerator = { | ||
return QueryGenerator.addQuotes(schema + (!schemaDelimiter ? '.' : schemaDelimiter) + tableName) | ||
return this.quoteIdentifier(schema + (!schemaDelimiter ? '.' : schemaDelimiter) + tableName, false) | ||
}, | ||
@@ -69,10 +59,10 @@ | ||
primaryKeys.push(attr) | ||
attrStr.push(QueryGenerator.addQuotes(attr) + " " + dataType.replace(/PRIMARY KEY/, '')) | ||
attrStr.push(this.quoteIdentifier(attr) + " " + dataType.replace(/PRIMARY KEY/, '')) | ||
} else if (Utils._.includes(dataType, 'REFERENCES')) { | ||
// MySQL doesn't support inline REFERENCES declarations: move to the end | ||
var m = dataType.match(/^(.+) (REFERENCES.*)$/) | ||
attrStr.push(QueryGenerator.addQuotes(attr) + " " + m[1]) | ||
attrStr.push(this.quoteIdentifier(attr) + " " + m[1]) | ||
foreignKeys[attr] = m[2] | ||
} else { | ||
attrStr.push(QueryGenerator.addQuotes(attr) + " " + dataType) | ||
attrStr.push(this.quoteIdentifier(attr) + " " + dataType) | ||
} | ||
@@ -83,3 +73,3 @@ } | ||
var values = { | ||
table: QueryGenerator.addQuotes(tableName), | ||
table: this.quoteIdentifier(tableName), | ||
attributes: attrStr.join(", "), | ||
@@ -89,3 +79,3 @@ engine: options.engine, | ||
} | ||
, pkString = primaryKeys.map(function(pk) { return QueryGenerator.addQuotes(pk) }).join(", ") | ||
, pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk) }.bind(this)).join(", ") | ||
@@ -98,3 +88,3 @@ if (pkString.length > 0) { | ||
if(foreignKeys.hasOwnProperty(fkey)) { | ||
values.attributes += ", FOREIGN KEY (" + QueryGenerator.addQuotes(fkey) + ") " + foreignKeys[fkey] | ||
values.attributes += ", FOREIGN KEY (" + this.quoteIdentifier(fkey) + ") " + foreignKeys[fkey] | ||
} | ||
@@ -112,3 +102,3 @@ } | ||
return Utils._.template(query)({ | ||
table: QueryGenerator.addQuotes(tableName) | ||
table: this.quoteIdentifier(tableName) | ||
}) | ||
@@ -185,10 +175,10 @@ }, | ||
options = options || {} | ||
options.table = table = Array.isArray(tableName) ? tableName.map(function(tbl){ return QueryGenerator.addQuotes(tbl) }).join(", ") : QueryGenerator.addQuotes(tableName) | ||
options.table = table = Array.isArray(tableName) ? tableName.map(function(t) { return this.quoteIdentifier(t)}.bind(this)).join(", ") : this.quoteIdentifier(tableName) | ||
options.attributes = options.attributes && options.attributes.map(function(attr){ | ||
if(Array.isArray(attr) && attr.length == 2) { | ||
return [attr[0], QueryGenerator.addQuotes(attr[1])].join(' as ') | ||
return [attr[0], this.quoteIdentifier(attr[1])].join(' as ') | ||
} else { | ||
return attr.indexOf(Utils.TICK_CHAR) < 0 ? QueryGenerator.addQuotes(attr) : attr | ||
return attr.indexOf(Utils.TICK_CHAR) < 0 ? this.quoteIdentifiers(attr) : attr | ||
} | ||
}).join(", ") | ||
}.bind(this)).join(", ") | ||
options.attributes = options.attributes || '*' | ||
@@ -201,4 +191,4 @@ | ||
var attributes = Object.keys(include.daoFactory.attributes).map(function(attr) { | ||
return "`" + include.as + "`.`" + attr + "` AS `" + include.as + "." + attr + "`" | ||
}) | ||
return this.quoteIdentifier(include.as) + "." + this.quoteIdentifier(attr) + " AS " + this.quoteIdentifier(include.as + "." + attr) | ||
}.bind(this)) | ||
@@ -213,5 +203,5 @@ optAttributes = optAttributes.concat(attributes) | ||
var attrRight = include.association.identifier | ||
joinQuery += " LEFT OUTER JOIN `" + table + "` AS `" + as + "` ON `" + tableLeft + "`.`" + attrLeft + "` = `" + tableRight + "`.`" + attrRight + "`" | ||
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) | ||
}) | ||
}.bind(this)) | ||
@@ -230,3 +220,3 @@ options.attributes = optAttributes.join(', ') | ||
if (options.group) { | ||
options.group = Array.isArray(options.group) ? options.group.map(function(grp){return QueryGenerator.addQuotes(grp)}).join(', ') : QueryGenerator.addQuotes(options.group) | ||
options.group = Array.isArray(options.group) ? options.group.map(function(t) { return this.quoteIdentifiers(t)}.bind(this)).join(', ') : this.quoteIdentifiers(options.group) | ||
query += " GROUP BY " + options.group | ||
@@ -256,5 +246,5 @@ } | ||
var table = QueryGenerator.addQuotes(tableName) | ||
var attributes = Object.keys(attrValueHash).map(function(attr){return QueryGenerator.addQuotes(attr)}).join(",") | ||
var values = Utils._.values(attrValueHash).map(processAndEscapeValue).join(",") | ||
var table = this.quoteIdentifier(tableName) | ||
var attributes = Object.keys(attrValueHash).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(",") | ||
var values = Utils._.values(attrValueHash).map(function(v) { return this.escape(v) }.bind(this)).join(",") | ||
@@ -271,8 +261,8 @@ var query = "INSERT INTO " + table + " (" + attributes + ") VALUES (" + values + ");" | ||
tuples.push("(" + | ||
Utils._.values(attrValueHash).map(processAndEscapeValue).join(",") + | ||
Utils._.values(attrValueHash).map(function(v) { return this.escape(v) }.bind(this)).join(",") + | ||
")") | ||
}) | ||
}.bind(this)) | ||
var table = QueryGenerator.addQuotes(tableName) | ||
var attributes = Object.keys(attrValueHashes[0]).map(function(attr){return QueryGenerator.addQuotes(attr)}).join(",") | ||
var table = this.quoteIdentifier(tableName) | ||
var attributes = Object.keys(attrValueHashes[0]).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(",") | ||
@@ -291,10 +281,10 @@ var query = "INSERT INTO " + table + " (" + attributes + ") VALUES " + tuples.join(",") + ";" | ||
var value = attrValueHash[key] | ||
, _value = processAndEscapeValue(value) | ||
, _value = this.escape(value) | ||
values.push(QueryGenerator.addQuotes(key) + "=" + _value) | ||
values.push(this.quoteIdentifier(key) + "=" + _value) | ||
} | ||
var query = "UPDATE " + QueryGenerator.addQuotes(tableName) + | ||
var query = "UPDATE " + this.quoteIdentifier(tableName) + | ||
" SET " + values.join(",") + | ||
" WHERE " + QueryGenerator.getWhereConditions(where) | ||
" WHERE " + this.getWhereConditions(where) | ||
@@ -307,4 +297,9 @@ return query | ||
var table = QueryGenerator.addQuotes(tableName) | ||
where = QueryGenerator.getWhereConditions(where) | ||
var table = this.quoteIdentifier(tableName) | ||
if (options.truncate === true) { | ||
// Truncate does not allow LIMIT and WHERE | ||
return "TRUNCATE " + table | ||
} | ||
where = this.getWhereConditions(where) | ||
var limit = "" | ||
@@ -317,8 +312,6 @@ | ||
if(!!options.limit) { | ||
limit = " LIMIT " + Utils.escape(options.limit) | ||
limit = " LIMIT " + this.escape(options.limit) | ||
} | ||
var query = "DELETE FROM " + table + " WHERE " + where + limit | ||
return query | ||
return "DELETE FROM " + table + " WHERE " + where + limit | ||
}, | ||
@@ -329,4 +322,4 @@ | ||
var table = QueryGenerator.addQuotes(tableName) | ||
where = QueryGenerator.getWhereConditions(where) | ||
var table = this.quoteIdentifier(tableName) | ||
where = this.getWhereConditions(where) | ||
@@ -345,10 +338,10 @@ var query = "DELETE FROM " + table + " WHERE " + where | ||
var value = attrValueHash[key] | ||
, _value = processAndEscapeValue(value) | ||
, _value = this.escape(value) | ||
values.push(QueryGenerator.addQuotes(key) + "=" + QueryGenerator.addQuotes(key) + " + " + _value) | ||
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + _value) | ||
} | ||
var table = QueryGenerator.addQuotes(tableName) | ||
var table = this.quoteIdentifier(tableName) | ||
values = values.join(",") | ||
where = QueryGenerator.getWhereConditions(where) | ||
where = this.getWhereConditions(where) | ||
@@ -383,7 +376,7 @@ var query = "UPDATE " + table + " SET " + values + " WHERE " + where | ||
} | ||
}) | ||
}.bind(this)) | ||
var onlyAttributeNames = attributes.map(function(attribute) { | ||
return (typeof attribute === 'string') ? attribute : attribute.attribute | ||
}) | ||
}.bind(this)) | ||
@@ -448,3 +441,3 @@ options = Utils._.extend({ | ||
//handle qualified key names | ||
var _key = key.split('.').map(function(col){return QueryGenerator.addQuotes(col)}).join(".") | ||
var _key = this.quoteIdentifiers(key) | ||
, _value = null | ||
@@ -455,3 +448,3 @@ | ||
if (value.length === 0) { value = [null] } | ||
_value = "(" + value.map(processAndEscapeValue).join(',') + ")" | ||
_value = "(" + value.map(function(v) { return this.escape(v) }.bind(this)).join(',') + ")" | ||
@@ -463,6 +456,6 @@ result.push([_key, _value].join(" IN ")) | ||
//using as sentinel for join column => value | ||
_value = value.join.split('.').map(function(col){ return QueryGenerator.addQuotes(col) }).join(".") | ||
_value = this.quoteIdentifiers(value.join) | ||
result.push([_key, _value].join("=")) | ||
} else { | ||
_value = processAndEscapeValue(value) | ||
_value = this.escape(value) | ||
result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) | ||
@@ -487,4 +480,4 @@ } | ||
template = "ENUM(" + Utils._.map(dataType.values, function(value) { | ||
return Utils.escape(value) | ||
}).join(", ") + ")" | ||
return this.escape(value) | ||
}.bind(this)).join(", ") + ")" | ||
} else { | ||
@@ -506,3 +499,3 @@ throw new Error('Values for ENUM haven\'t been defined.') | ||
if ((dataType.defaultValue !== undefined) && (dataType.defaultValue != DataTypes.NOW)) { | ||
template += " DEFAULT " + Utils.escape(dataType.defaultValue) | ||
template += " DEFAULT " + this.escape(dataType.defaultValue) | ||
} | ||
@@ -519,9 +512,9 @@ | ||
if(dataType.references) { | ||
template += " REFERENCES " + Utils.addTicks(dataType.references) | ||
template += " REFERENCES " + this.quoteIdentifier(dataType.references) | ||
if(dataType.referencesKey) { | ||
template += " (" + Utils.addTicks(dataType.referencesKey) + ")" | ||
template += " (" + this.quoteIdentifier(dataType.referencesKey) + ")" | ||
} else { | ||
template += " (" + Utils.addTicks('id') + ")" | ||
template += " (" + this.quoteIdentifier('id') + ")" | ||
} | ||
@@ -574,9 +567,19 @@ | ||
addQuotes: function(s, quoteChar) { | ||
return Utils.addTicks(s, quoteChar) | ||
quoteIdentifier: function(identifier, force) { | ||
return Utils.addTicks(identifier, "`") | ||
}, | ||
removeQuotes: function(s, quoteChar) { | ||
return Utils.removeTicks(s, quoteChar) | ||
quoteIdentifiers: function(identifiers, force) { | ||
return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.') | ||
}, | ||
escape: function(value) { | ||
if (value instanceof Date) { | ||
value = Utils.toSqlDate(value) | ||
} else if (typeof value === 'boolean') { | ||
value = value ? 1 : 0 | ||
} | ||
return Utils.escape(value) | ||
} | ||
} | ||
@@ -583,0 +586,0 @@ |
@@ -27,3 +27,3 @@ var Utils = require("../../utils") | ||
return QueryGenerator.addQuotes(schema) + '.' + QueryGenerator.addQuotes(tableName) | ||
return this.quoteIdentifier(schema) + '.' + this.quoteIdentifier(tableName) | ||
}, | ||
@@ -56,7 +56,7 @@ | ||
for (var attr in attributes) { | ||
var dataType = QueryGenerator.pgDataTypeMapping(tableName, attr, attributes[attr]) | ||
attrStr.push(QueryGenerator.addQuotes(attr) + " " + dataType) | ||
var dataType = this.pgDataTypeMapping(tableName, attr, attributes[attr]) | ||
attrStr.push(this.quoteIdentifier(attr) + " " + dataType) | ||
if (attributes[attr].match(/^ENUM\(/)) { | ||
query = QueryGenerator.pgEnum(tableName, attr, attributes[attr]) + query | ||
query = this.pgEnum(tableName, attr, attributes[attr]) + query | ||
} | ||
@@ -66,3 +66,3 @@ } | ||
var values = { | ||
table: QueryGenerator.addQuotes(tableName), | ||
table: this.quoteIdentifiers(tableName), | ||
attributes: attrStr.join(", ") | ||
@@ -72,4 +72,4 @@ } | ||
var pks = primaryKeys[tableName].map(function(pk){ | ||
return QueryGenerator.addQuotes(pk) | ||
}).join(",") | ||
return this.quoteIdentifier(pk) | ||
}.bind(this)).join(",") | ||
@@ -87,3 +87,3 @@ if (pks.length > 0) { | ||
return Utils._.template(query)({ | ||
table: QueryGenerator.addQuotes(tableName), | ||
table: this.quoteIdentifiers(tableName), | ||
cascade: options.cascade? " CASCADE" : "" | ||
@@ -96,4 +96,4 @@ }) | ||
return Utils._.template(query)({ | ||
before: QueryGenerator.addQuotes(before), | ||
after: QueryGenerator.addQuotes(after) | ||
before: this.quoteIdentifier(before), | ||
after: this.quoteIdentifier(after) | ||
}) | ||
@@ -107,5 +107,5 @@ }, | ||
describeTableQuery: function(tableName) { | ||
var query = 'SELECT column_name as "Field", column_default as "Default", is_nullable as "Null", data_type as "Type" FROM information_schema.columns WHERE table_name = <%= table %>;' | ||
var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", c.data_type as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS special FROM information_schema.columns c WHERE table_name = <%= table %>;' | ||
return Utils._.template(query)({ | ||
table: QueryGenerator.addQuotes(tableName, "'") | ||
table: this.escape(tableName) | ||
}) | ||
@@ -122,8 +122,8 @@ }, | ||
attrString.push(Utils._.template('<%= attrName %> <%= definition %>')({ | ||
attrName: QueryGenerator.addQuotes(attrName), | ||
definition: QueryGenerator.pgDataTypeMapping(tableName, attrName, definition) | ||
attrName: this.quoteIdentifier(attrName), | ||
definition: this.pgDataTypeMapping(tableName, attrName, definition) | ||
})) | ||
if (definition.match(/^ENUM\(/)) { | ||
query = QueryGenerator.pgEnum(tableName, attrName, definition) + query | ||
query = this.pgEnum(tableName, attrName, definition) + query | ||
} | ||
@@ -133,3 +133,3 @@ } | ||
return Utils._.template(query)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
tableName: this.quoteIdentifiers(tableName), | ||
attributes: attrString.join(', ') }) | ||
@@ -141,4 +141,4 @@ }, | ||
return Utils._.template(query)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
attributeName: QueryGenerator.addQuotes(attributeName) | ||
tableName: this.quoteIdentifiers(tableName), | ||
attributeName: this.quoteIdentifier(attributeName) | ||
}) | ||
@@ -157,4 +157,4 @@ }, | ||
attrSql += Utils._.template(query)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
query: QueryGenerator.addQuotes(attributeName) + ' SET NOT NULL' | ||
tableName: this.quoteIdentifiers(tableName), | ||
query: this.quoteIdentifier(attributeName) + ' SET NOT NULL' | ||
}) | ||
@@ -165,4 +165,4 @@ | ||
attrSql += Utils._.template(query)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
query: QueryGenerator.addQuotes(attributeName) + ' DROP NOT NULL' | ||
tableName: this.quoteIdentifiers(tableName), | ||
query: this.quoteIdentifier(attributeName) + ' DROP NOT NULL' | ||
}) | ||
@@ -173,4 +173,4 @@ } | ||
attrSql += Utils._.template(query)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
query: QueryGenerator.addQuotes(attributeName) + ' SET DEFAULT' + definition.match(/DEFAULT ([^;]+)/)[1] | ||
tableName: this.quoteIdentifiers(tableName), | ||
query: this.quoteIdentifier(attributeName) + ' SET DEFAULT' + definition.match(/DEFAULT ([^;]+)/)[1] | ||
}) | ||
@@ -181,4 +181,4 @@ | ||
attrSql += Utils._.template(query)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
query: QueryGenerator.addQuotes(attributeName) + ' DROP DEFAULT' | ||
tableName: this.quoteIdentifiers(tableName), | ||
query: this.quoteIdentifier(attributeName) + ' DROP DEFAULT' | ||
}) | ||
@@ -188,9 +188,9 @@ } | ||
if (definition.match(/^ENUM\(/)) { | ||
query = QueryGenerator.pgEnum(tableName, attributeName, definition) + query | ||
definition = definition.replace(/^ENUM\(.+\)/, Utils.escape("enum_" + tableName + "_" + attributeName)) | ||
query = this.pgEnum(tableName, attributeName, definition) + query | ||
definition = definition.replace(/^ENUM\(.+\)/, this.quoteIdentifier("enum_" + tableName + "_" + attributeName)) | ||
} | ||
attrSql += Utils._.template(query)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
query: QueryGenerator.addQuotes(attributeName) + ' TYPE ' + definition | ||
tableName: this.quoteIdentifiers(tableName), | ||
query: this.quoteIdentifier(attributeName) + ' TYPE ' + definition | ||
}) | ||
@@ -210,4 +210,4 @@ | ||
attrString.push(Utils._.template('<%= before %> TO <%= after %>')({ | ||
before: QueryGenerator.addQuotes(attrBefore), | ||
after: QueryGenerator.addQuotes(attributeName) | ||
before: this.quoteIdentifier(attrBefore), | ||
after: this.quoteIdentifier(attributeName) | ||
})) | ||
@@ -217,3 +217,3 @@ } | ||
return Utils._.template(query)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
tableName: this.quoteIdentifiers(tableName), | ||
attributes: attrString.join(', ') | ||
@@ -224,28 +224,14 @@ }) | ||
selectQuery: function(tableName, options) { | ||
var query = "SELECT <%= attributes %> FROM <%= table %>" | ||
, table = null | ||
var query = "SELECT <%= attributes %> FROM <%= table %>", | ||
table = null | ||
options = options || {} | ||
if (Array.isArray(tableName)) { | ||
options.table = table = tableName.map(function(t){ | ||
return QueryGenerator.addQuotes(t) | ||
}).join(", ") | ||
} else { | ||
options.table = table = QueryGenerator.addQuotes(tableName) | ||
} | ||
options = options || {} | ||
options.table = table = Array.isArray(tableName) ? tableName.map(function(t) { return this.quoteIdentifiers(t) }.bind(this)).join(", ") : this.quoteIdentifiers(tableName) | ||
options.attributes = options.attributes && options.attributes.map(function(attr) { | ||
if (Array.isArray(attr) && attr.length === 2) { | ||
return [ | ||
attr[0], | ||
QueryGenerator.addQuotes(QueryGenerator.removeQuotes(attr[1], '`')) | ||
].join(' as ') | ||
} else if (attr.indexOf('`') >= 0) { | ||
return attr.replace(/`/g, '"') | ||
return [attr[0], this.quoteIdentifier(attr[1])].join(' as ') | ||
} else { | ||
return QueryGenerator.addQuotes(attr) | ||
return attr.indexOf('"') < 0 ? this.quoteIdentifiers(attr) : attr | ||
} | ||
}).join(", ") | ||
}.bind(this)).join(", ") | ||
options.attributes = options.attributes || '*' | ||
@@ -258,19 +244,18 @@ | ||
var attributes = Object.keys(include.daoFactory.attributes).map(function(attr) { | ||
var template = Utils._.template('"<%= as %>"."<%= attr %>" AS "<%= as %>.<%= attr %>"') | ||
return template({ as: include.as, attr: attr }) | ||
}) | ||
return this.quoteIdentifier(include.as) + "." + this.quoteIdentifier(attr) + " AS " + this.quoteIdentifier(include.as + "." + attr, true) | ||
}.bind(this)) | ||
optAttributes = optAttributes.concat(attributes) | ||
var joinQuery = ' LEFT OUTER JOIN "<%= table %>" AS "<%= as %>" ON "<%= tableLeft %>"."<%= attrLeft %>" = "<%= tableRight %>"."<%= attrRight %>"' | ||
var joinQuery = ' LEFT OUTER JOIN <%= table %> AS <%= as %> ON <%= tableLeft %>.<%= attrLeft %> = <%= tableRight %>.<%= attrRight %>' | ||
query += Utils._.template(joinQuery)({ | ||
table: include.daoFactory.tableName, | ||
as: include.as, | ||
tableLeft: ((include.association.associationType === 'BelongsTo') ? include.as : tableName), | ||
attrLeft: 'id', | ||
tableRight: ((include.association.associationType === 'BelongsTo') ? tableName : include.as), | ||
attrRight: include.association.identifier | ||
table: this.quoteIdentifiers(include.daoFactory.tableName), | ||
as: this.quoteIdentifier(include.as), | ||
tableLeft: this.quoteIdentifiers((include.association.associationType === 'BelongsTo') ? include.as : tableName), | ||
attrLeft: this.quoteIdentifier('id'), | ||
tableRight: this.quoteIdentifiers((include.association.associationType === 'BelongsTo') ? tableName : include.as), | ||
attrRight: this.quoteIdentifier(include.association.identifier) | ||
}) | ||
}) | ||
}.bind(this)) | ||
@@ -281,3 +266,3 @@ options.attributes = optAttributes.join(', ') | ||
if(options.hasOwnProperty('where')) { | ||
options.where = QueryGenerator.getWhereConditions(options.where, tableName) | ||
options.where = this.getWhereConditions(options.where, tableName) | ||
query += " WHERE <%= where %>" | ||
@@ -287,10 +272,3 @@ } | ||
if(options.group) { | ||
if (Array.isArray(options.group)) { | ||
options.group = options.group.map(function(grp){ | ||
return QueryGenerator.addQuotes(grp) | ||
}).join(', ') | ||
} else { | ||
options.group = QueryGenerator.addQuotes(options.group) | ||
} | ||
options.group = Array.isArray(options.group) ? options.group.map(function(t) { return this.quoteIdentifiers(t) }.bind(this)).join(', ') : this.quoteIdentifiers(options.group) | ||
query += " GROUP BY <%= group %>" | ||
@@ -301,4 +279,4 @@ } | ||
options.order = options.order.replace(/([^ ]+)(.*)/, function(m, g1, g2) { | ||
return QueryGenerator.addQuotes(g1) + g2 | ||
}) | ||
return this.quoteIdentifiers(g1) + g2 | ||
}.bind(this)) | ||
query += " ORDER BY <%= order %>" | ||
@@ -329,9 +307,9 @@ } | ||
var replacements = { | ||
table: QueryGenerator.addQuotes(tableName) | ||
table: this.quoteIdentifiers(tableName) | ||
, attributes: Object.keys(attrValueHash).map(function(attr){ | ||
return QueryGenerator.addQuotes(attr) | ||
}).join(",") | ||
return this.quoteIdentifier(attr) | ||
}.bind(this)).join(",") | ||
, values: Utils._.values(attrValueHash).map(function(value){ | ||
return QueryGenerator.pgEscape(value) | ||
}).join(",") | ||
return this.escape(value) | ||
}.bind(this)).join(",") | ||
} | ||
@@ -350,12 +328,12 @@ | ||
Utils._.values(attrValueHash).map(function(value){ | ||
return QueryGenerator.pgEscape(value) | ||
}).join(",") + | ||
return this.escape(value) | ||
}.bind(this)).join(",") + | ||
")") | ||
}) | ||
}.bind(this)) | ||
var replacements = { | ||
table: QueryGenerator.addQuotes(tableName) | ||
table: this.quoteIdentifiers(tableName) | ||
, attributes: Object.keys(attrValueHashes[0]).map(function(attr){ | ||
return QueryGenerator.addQuotes(attr) | ||
}).join(",") | ||
return this.quoteIdentifier(attr) | ||
}.bind(this)).join(",") | ||
, tuples: tuples.join(",") | ||
@@ -375,9 +353,9 @@ } | ||
var value = attrValueHash[key] | ||
values.push(QueryGenerator.addQuotes(key) + "=" + QueryGenerator.pgEscape(value)) | ||
values.push(this.quoteIdentifier(key) + "=" + this.escape(value)) | ||
} | ||
var replacements = { | ||
table: QueryGenerator.addQuotes(tableName), | ||
table: this.quoteIdentifiers(tableName), | ||
values: values.join(","), | ||
where: QueryGenerator.getWhereConditions(where) | ||
where: this.getWhereConditions(where) | ||
} | ||
@@ -391,2 +369,6 @@ | ||
if (options.truncate === true) { | ||
return "TRUNCATE " + QueryGenerator.quoteIdentifier(tableName) | ||
} | ||
if(Utils._.isUndefined(options.limit)) { | ||
@@ -403,12 +385,12 @@ options.limit = 1; | ||
pks = primaryKeys[tableName].map(function(pk) { | ||
return QueryGenerator.addQuotes(pk) | ||
}).join(',') | ||
return this.quoteIdentifier(pk) | ||
}.bind(this)).join(',') | ||
} else { | ||
pks = QueryGenerator.addQuotes('id') | ||
pks = this.quoteIdentifier('id') | ||
} | ||
var replacements = { | ||
table: QueryGenerator.addQuotes(tableName), | ||
where: QueryGenerator.getWhereConditions(where), | ||
limit: !!options.limit? " LIMIT " + QueryGenerator.pgEscape(options.limit) : "", | ||
table: this.quoteIdentifiers(tableName), | ||
where: this.getWhereConditions(where), | ||
limit: !!options.limit? " LIMIT " + this.escape(options.limit) : "", | ||
primaryKeys: primaryKeys[tableName].length > 1 ? '(' + pks + ')' : pks, | ||
@@ -429,9 +411,9 @@ primaryKeysSelection: pks | ||
var value = attrValueHash[key] | ||
values.push(QueryGenerator.addQuotes(key) + "=" + QueryGenerator.addQuotes(key) + " + " + QueryGenerator.pgEscape(value)) | ||
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + this.escape(value)) | ||
} | ||
var replacements = { | ||
table: QueryGenerator.addQuotes(tableName), | ||
table: this.quoteIdentifiers(tableName), | ||
values: values.join(","), | ||
where: QueryGenerator.getWhereConditions(where) | ||
where: this.getWhereConditions(where) | ||
} | ||
@@ -446,3 +428,3 @@ | ||
if (typeof attribute === 'string') { | ||
return QueryGenerator.addQuotes(attribute) | ||
return this.quoteIdentifier(attribute) | ||
} else { | ||
@@ -455,3 +437,3 @@ var result = "" | ||
result += QueryGenerator.addQuotes(attribute.attribute) | ||
result += this.quoteIdentifier(attribute.attribute) | ||
@@ -468,7 +450,7 @@ if (attribute.length) { | ||
} | ||
}) | ||
}.bind(this)) | ||
var onlyAttributeNames = attributes.map(function(attribute) { | ||
return (typeof attribute === "string") ? attribute : attribute.attribute | ||
}) | ||
}.bind(this)) | ||
@@ -483,5 +465,5 @@ var indexTable = tableName.split('.') | ||
return Utils._.compact([ | ||
"CREATE", options.indicesType, "INDEX", QueryGenerator.addQuotes(options.indexName), | ||
"CREATE", options.indicesType, "INDEX", this.quoteIdentifiers(options.indexName), | ||
(options.indexType ? ('USING ' + options.indexType) : undefined), | ||
"ON", QueryGenerator.addQuotes(tableName), '(' + transformedAttributes.join(', ') + ')' | ||
"ON", this.quoteIdentifiers(tableName), '(' + transformedAttributes.join(', ') + ')' | ||
]).join(' ') | ||
@@ -504,4 +486,4 @@ }, | ||
return Utils._.template(sql)({ | ||
tableName: QueryGenerator.addQuotes(tableName), | ||
indexName: QueryGenerator.addQuotes(indexName) | ||
tableName: this.quoteIdentifiers(tableName), | ||
indexName: this.quoteIdentifiers(indexName) | ||
}) | ||
@@ -515,7 +497,7 @@ }, | ||
smth = Utils.prependTableNameToHash(tableName, smth) | ||
result = QueryGenerator.hashToWhereConditions(smth) | ||
result = this.hashToWhereConditions(smth) | ||
} | ||
else if (typeof smth === "number") { | ||
smth = Utils.prependTableNameToHash(tableName, { id: smth }) | ||
result = QueryGenerator.hashToWhereConditions(smth) | ||
result = this.hashToWhereConditions(smth) | ||
} | ||
@@ -539,10 +521,8 @@ else if (typeof smth === "string") { | ||
//handle qualified key names | ||
var _key = key.split('.').map(function(col){return QueryGenerator.addQuotes(col)}).join(".") | ||
var _key = this.quoteIdentifiers(key) | ||
, _value = null | ||
if (Array.isArray(value)) { | ||
if (value.length == 0) { value = [null] } | ||
_value = "(" + value.map(function(subValue) { | ||
return QueryGenerator.pgEscape(subValue); | ||
}).join(',') + ")" | ||
if (value.length === 0) { value = [null] } | ||
_value = "(" + value.map(this.escape).join(',') + ")" | ||
@@ -553,6 +533,6 @@ result.push([_key, _value].join(" IN ")) | ||
//using as sentinel for join column => value | ||
_value = value.join.split('.').map(function(col){return QueryGenerator.addQuotes(col)}).join(".") | ||
_value = this.quoteIdentifiers(value.join) | ||
result.push([_key, _value].join("=")) | ||
} else { | ||
_value = QueryGenerator.pgEscape(value) | ||
_value = this.escape(value) | ||
result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) | ||
@@ -578,4 +558,4 @@ } | ||
replacements.type = "ENUM(" + Utils._.map(dataType.values, function(value) { | ||
return Utils.escape(value) | ||
}).join(", ") + ")" | ||
return this.escape(value) | ||
}.bind(this)).join(", ") + ")" | ||
} else { | ||
@@ -604,3 +584,3 @@ throw new Error('Values for ENUM haven\'t been defined.') | ||
template += " DEFAULT <%= defaultValue %>" | ||
replacements.defaultValue = QueryGenerator.pgEscape(dataType.defaultValue) | ||
replacements.defaultValue = this.escape(dataType.defaultValue) | ||
} | ||
@@ -618,8 +598,8 @@ | ||
template += " REFERENCES <%= referencesTable %> (<%= referencesKey %>)" | ||
replacements.referencesTable = QueryGenerator.addQuotes(dataType.references) | ||
replacements.referencesTable = this.quoteIdentifier(dataType.references) | ||
if(dataType.referencesKey) { | ||
replacements.referencesKey = QueryGenerator.addQuotes(dataType.referencesKey) | ||
replacements.referencesKey = this.quoteIdentifier(dataType.referencesKey) | ||
} else { | ||
replacements.referencesKey = QueryGenerator.addQuotes('id') | ||
replacements.referencesKey = this.quoteIdentifier('id') | ||
} | ||
@@ -683,47 +663,26 @@ | ||
removeQuotes: function (s, quoteChar) { | ||
quoteChar = quoteChar || '"' | ||
return s.replace(new RegExp(quoteChar, 'g'), '') | ||
pgEscapeAndQuote: function (val) { | ||
return this.quoteIdentifier(Utils.removeTicks(this.escape(val), "'")) | ||
}, | ||
addQuotes: function (s, quoteChar) { | ||
quoteChar = quoteChar || '"' | ||
return QueryGenerator.removeQuotes(s, quoteChar) | ||
.split('.') | ||
.map(function(e) { return quoteChar + String(e) + quoteChar }) | ||
.join('.') | ||
pgEnum: function (tableName, attr, dataType) { | ||
var enumName = this.pgEscapeAndQuote("enum_" + tableName + "_" + attr) | ||
return "DROP TYPE IF EXISTS " + enumName + "; CREATE TYPE " + enumName + " AS " + dataType.match(/^ENUM\(.+\)/)[0] + "; " | ||
}, | ||
pgEscape: function (val) { | ||
if (val === undefined || val === null) { | ||
return 'NULL'; | ||
} | ||
fromArray: function(text) { | ||
text = text.replace(/^{/, '').replace(/}$/, '') | ||
var matches = text.match(/("(?:\\.|[^"\\\\])*"|[^,]*)(?:\s*,\s*|\s*$)/ig) | ||
switch (typeof val) { | ||
case 'boolean': return (val) ? 'true' : 'false'; | ||
case 'number': return val+''; | ||
case 'object': | ||
if (Array.isArray(val)) { | ||
return 'ARRAY['+ val.map(function(it) { return QueryGenerator.pgEscape(it) }).join(',') +']'; | ||
} | ||
if (matches.length < 1) { | ||
return [] | ||
} | ||
if (val instanceof Date) { | ||
val = QueryGenerator.pgSqlDate(val); | ||
} | ||
matches = matches.map(function(m){ | ||
return m.replace(/",$/, '').replace(/,$/, '').replace(/(^"|"$)/, '') | ||
}) | ||
// http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS | ||
val = val.replace(/'/g, "''"); | ||
return "'"+val+"'"; | ||
return matches.slice(0, -1) | ||
}, | ||
pgEscapeAndQuote: function (val) { | ||
return QueryGenerator.addQuotes(QueryGenerator.removeQuotes(QueryGenerator.pgEscape(val), "'")) | ||
}, | ||
pgEnum: function (tableName, attr, dataType) { | ||
var enumName = QueryGenerator.pgEscapeAndQuote("enum_" + tableName + "_" + attr) | ||
return "DROP TYPE IF EXISTS " + enumName + "; CREATE TYPE " + enumName + " AS " + dataType.match(/^ENUM\(.+\)/)[0] + "; " | ||
}, | ||
toHstore: function(text) { | ||
@@ -749,4 +708,4 @@ var obj = {} | ||
pgSqlDate: function (dt) { | ||
var date = [ dt.getUTCFullYear(), QueryGenerator.padInt(dt.getUTCMonth()+1), QueryGenerator.padInt(dt.getUTCDate()) ].join('-') | ||
var time = [ dt.getUTCHours(), QueryGenerator.padInt(dt.getUTCMinutes()), QueryGenerator.padInt(dt.getUTCSeconds())].join(':') | ||
var date = [ dt.getUTCFullYear(), this.padInt(dt.getUTCMonth()+1), this.padInt(dt.getUTCDate()) ].join('-') | ||
var time = [ dt.getUTCHours(), this.padInt(dt.getUTCMinutes()), this.padInt(dt.getUTCSeconds())].join(':') | ||
return date + ' ' + time + '.' + ((dt.getTime() % 1000) * 1000) + 'Z' | ||
@@ -782,7 +741,50 @@ }, | ||
if (dataType.match(/^ENUM\(/)) { | ||
dataType = dataType.replace(/^ENUM\(.+\)/, QueryGenerator.pgEscapeAndQuote("enum_" + tableName + "_" + attr)) | ||
dataType = dataType.replace(/^ENUM\(.+\)/, this.pgEscapeAndQuote("enum_" + tableName + "_" + attr)) | ||
} | ||
return dataType | ||
}, | ||
quoteIdentifier: function(identifier, force) { | ||
if(!force && this.options && this.options.quoteIdentifiers === false) { // default is `true` | ||
// In Postgres, if tables or attributes are created double-quoted, | ||
// they are also case sensitive. If they contain any uppercase | ||
// characters, they must always be double-quoted. This makes it | ||
// impossible to write queries in portable SQL if tables are created in | ||
// this way. Hence, we strip quotes if we don't want case sensitivity. | ||
return Utils.removeTicks(identifier, '"') | ||
} else { | ||
return Utils.addTicks(identifier, '"') | ||
} | ||
}, | ||
quoteIdentifiers: function(identifiers, force) { | ||
return identifiers.split('.').map(function(t) { return this.quoteIdentifier(t, force) }.bind(this)).join('.') | ||
}, | ||
escape: function (val) { | ||
if (val === undefined || val === null) { | ||
return 'NULL'; | ||
} | ||
switch (typeof val) { | ||
case 'boolean': | ||
return (val) ? 'true' : 'false'; | ||
case 'number': | ||
return val + ''; | ||
case 'object': | ||
if (Array.isArray(val)) { | ||
return 'ARRAY['+ val.map(function(it) { return this.escape(it) }.bind(this)).join(',') + ']'; | ||
} | ||
} | ||
if (val instanceof Date) { | ||
val = this.pgSqlDate(val); | ||
} | ||
// http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS | ||
val = val.replace(/'/g, "''"); | ||
return "'" + val + "'"; | ||
} | ||
} | ||
@@ -789,0 +791,0 @@ |
@@ -59,2 +59,3 @@ var Utils = require("../../utils") | ||
var results = [] | ||
, self = this | ||
, isTableNameQuery = (this.sql.indexOf('SELECT table_name FROM information_schema.tables') === 0) | ||
@@ -78,3 +79,3 @@ , isRelNameQuery = (this.sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0) | ||
if (this.send('isSelectQuery')) { | ||
if (this.sql.toLowerCase().indexOf('select column_name') === 0) { | ||
if (this.sql.toLowerCase().indexOf('select c.column_name') === 0) { | ||
var result = {} | ||
@@ -86,3 +87,4 @@ | ||
allowNull: (_result.Null === 'YES'), | ||
defaultValue: _result.Default | ||
defaultValue: _result.Default, | ||
special: (!!_result.special ? self.sequelize.queryInterface.QueryGenerator.fromArray(_result.special) : []) | ||
} | ||
@@ -102,3 +104,6 @@ | ||
if (result[_result.Field].defaultValue.indexOf('::') > -1) { | ||
result[_result.Field].defaultValue = result[_result.Field].defaultValue.split('::')[0] | ||
var split = result[_result.Field].defaultValue.split('::'); | ||
if (split[1].toLowerCase() !== "regclass)") { | ||
result[_result.Field].defaultValue = split[0] | ||
} | ||
} | ||
@@ -110,2 +115,16 @@ } | ||
} else { | ||
// Postgres will treat tables as case-insensitive, so fix the case | ||
// of the returned values to match attributes | ||
if(this.sequelize.options.quoteIdentifiers == false) { | ||
var attrsMap = Utils._.reduce(this.callee.attributes, function(m, v, k) { m[k.toLowerCase()] = k; return m}, {}) | ||
rows.forEach(function(row) { | ||
Utils._.keys(row).forEach(function(key) { | ||
var targetAttr = attrsMap[key] | ||
if(targetAttr != key) { | ||
row[targetAttr] = row[key] | ||
delete row[key] | ||
} | ||
}) | ||
}) | ||
} | ||
this.emit('success', this.send('handleSelectQuery', rows)) | ||
@@ -112,0 +131,0 @@ } |
@@ -153,2 +153,7 @@ module.exports = (function() { | ||
- limit -> Maximaum count of lines to delete | ||
- truncate -> boolean - whether to use an 'optimized' mechanism (i.e. TRUNCATE) if available, | ||
note that this should not be the default behaviour because TRUNCATE does not | ||
always play nicely (e.g. InnoDB tables with FK constraints) | ||
(@see http://dev.mysql.com/doc/refman/5.6/en/truncate-table.html). | ||
Note that truncate must ignore limit and where | ||
*/ | ||
@@ -267,3 +272,24 @@ deleteQuery: function(tableName, where, options) { | ||
disableForeignKeyConstraintsQuery: function() { | ||
throwMethodUndefined('disableForeignKeyConstraintsQuery') | ||
throwMethodUndefined('disableForeignKeyConstraintsQuery') | ||
}, | ||
/* | ||
Escape an identifier (e.g. a table or attribute name) | ||
*/ | ||
quoteIdentifier: function(identifier, force) { | ||
throwMethodUndefined('quoteIdentifier') | ||
}, | ||
/* | ||
Split an identifier into .-separated tokens and quote each part | ||
*/ | ||
quoteIdentifiers: function(identifiers, force) { | ||
throwMethodUndefined('quoteIdentifiers') | ||
}, | ||
/* | ||
Escape a value (e.g. a string, number or date) | ||
*/ | ||
escape: function(value) { | ||
throwMethodUndefined('quoteIdentifier') | ||
} | ||
@@ -270,0 +296,0 @@ |
@@ -11,14 +11,2 @@ var Utils = require("../../utils") | ||
var escape = function(str) { | ||
if (typeof str === 'string') { | ||
return "'" + str.replace(/'/g, "''") + "'"; | ||
} else if (typeof str === 'boolean') { | ||
return str ? 1 : 0; // SQLite has no type boolean | ||
} else if (str === null || str === undefined) { | ||
return 'NULL'; | ||
} else { | ||
return str; | ||
} | ||
}; | ||
module.exports = (function() { | ||
@@ -28,15 +16,2 @@ var QueryGenerator = { | ||
removeQuotes: function (s, quoteChar) { | ||
quoteChar = quoteChar || '`' | ||
return s.replace(new RegExp(quoteChar, 'g'), '') | ||
}, | ||
addQuotes: function (s, quoteChar) { | ||
quoteChar = quoteChar || '`' | ||
return QueryGenerator.removeQuotes(s, quoteChar) | ||
.split('.') | ||
.map(function(e) { return quoteChar + String(e) + quoteChar }) | ||
.join('.') | ||
}, | ||
addSchema: function(opts) { | ||
@@ -58,3 +33,3 @@ var tableName = undefined | ||
return QueryGenerator.addQuotes(schema + (!schemaPrefix ? '.' : schemaPrefix) + tableName) | ||
return this.quoteIdentifier(schema) + (!schemaPrefix ? '.' : schemaPrefix) + this.quoteIdentifier(tableName) | ||
}, | ||
@@ -97,5 +72,5 @@ | ||
primaryKeys.push(attr) | ||
attrStr.push(Utils.addTicks(attr) + " " + dataType.replace(/PRIMARY KEY/, 'NOT NULL')) | ||
attrStr.push(this.quoteIdentifier(attr) + " " + dataType.replace(/PRIMARY KEY/, 'NOT NULL')) | ||
} else { | ||
attrStr.push(Utils.addTicks(attr) + " " + dataType) | ||
attrStr.push(this.quoteIdentifier(attr) + " " + dataType) | ||
} | ||
@@ -106,7 +81,7 @@ } | ||
var values = { | ||
table: Utils.addTicks(tableName), | ||
table: this.quoteIdentifier(tableName), | ||
attributes: attrStr.join(", "), | ||
charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "") | ||
} | ||
, pkString = primaryKeys.map(function(pk) { return Utils.addTicks(pk) }).join(", ") | ||
, pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk) }.bind(this)).join(", ") | ||
@@ -118,8 +93,8 @@ if (pkString.length > 0) { | ||
var sql = Utils._.template(query, values).trim() + ";" | ||
return QueryGenerator.replaceBooleanDefaults(sql) | ||
return this.replaceBooleanDefaults(sql) | ||
}, | ||
addColumnQuery: function() { | ||
var sql = MySqlQueryGenerator.addColumnQuery.apply(null, arguments) | ||
return QueryGenerator.replaceBooleanDefaults(sql) | ||
var sql = MySqlQueryGenerator.addColumnQuery.apply(this, arguments) | ||
return this.replaceBooleanDefaults(sql) | ||
}, | ||
@@ -137,7 +112,7 @@ | ||
var replacements = { | ||
table: Utils.addTicks(tableName), | ||
attributes: Object.keys(attrValueHash).map(function(attr){return Utils.addTicks(attr)}).join(","), | ||
table: this.quoteIdentifier(tableName), | ||
attributes: Object.keys(attrValueHash).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(","), | ||
values: Utils._.values(attrValueHash).map(function(value){ | ||
return escape((value instanceof Date) ? Utils.toSqlDate(value) : value) | ||
}).join(",") | ||
return this.escape(value) | ||
}.bind(this)).join(",") | ||
} | ||
@@ -155,10 +130,10 @@ | ||
Utils._.values(attrValueHash).map(function(value){ | ||
return escape((value instanceof Date) ? Utils.toSqlDate(value) : value) | ||
}).join(",") + | ||
return this.escape(value) | ||
}.bind(this)).join(",") + | ||
")") | ||
}) | ||
}.bind(this)) | ||
var replacements = { | ||
table: Utils.addTicks(tableName), | ||
attributes: Object.keys(attrValueHashes[0]).map(function(attr){return Utils.addTicks(attr)}).join(","), | ||
table: this.quoteIdentifier(tableName), | ||
attributes: Object.keys(attrValueHashes[0]).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(","), | ||
tuples: tuples | ||
@@ -174,10 +149,10 @@ } | ||
options = options || {} | ||
options.table = table = Array.isArray(tableName) ? tableName.map(function(tbl){ return QueryGenerator.addQuotes(tbl) }).join(", ") : QueryGenerator.addQuotes(tableName) | ||
options.table = table = Array.isArray(tableName) ? tableName.map(function(t) { return this.quoteIdentifier(t)}.bind(this)).join(", ") : this.quoteIdentifier(tableName) | ||
options.attributes = options.attributes && options.attributes.map(function(attr){ | ||
if(Array.isArray(attr) && attr.length == 2) { | ||
return [attr[0], QueryGenerator.addQuotes(attr[1])].join(' as ') | ||
return [attr[0], this.quoteIdentifier(attr[1])].join(' as ') | ||
} else { | ||
return attr.indexOf(Utils.TICK_CHAR) < 0 ? QueryGenerator.addQuotes(attr) : attr | ||
return attr.indexOf(Utils.TICK_CHAR) < 0 ? this.quoteIdentifiers(attr) : attr | ||
} | ||
}).join(", ") | ||
}.bind(this)).join(", ") | ||
options.attributes = options.attributes || '*' | ||
@@ -190,4 +165,4 @@ | ||
var attributes = Object.keys(include.daoFactory.attributes).map(function(attr) { | ||
return "`" + include.as + "`.`" + attr + "` AS `" + include.as + "." + attr + "`" | ||
}) | ||
return this.quoteIdentifier(include.as) + "." + this.quoteIdentifier(attr) + " AS " + this.quoteIdentifier(include.as + "." + attr) | ||
}.bind(this)) | ||
@@ -202,5 +177,5 @@ optAttributes = optAttributes.concat(attributes) | ||
var attrRight = include.association.identifier | ||
joinQuery += " LEFT OUTER JOIN `" + table + "` AS `" + as + "` ON `" + tableLeft + "`.`" + attrLeft + "` = `" + tableRight + "`.`" + attrRight + "`" | ||
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight) + "" | ||
}) | ||
}.bind(this)) | ||
@@ -219,3 +194,3 @@ options.attributes = optAttributes.join(', ') | ||
if (options.group) { | ||
options.group = Array.isArray(options.group) ? options.group.map(function(grp){return QueryGenerator.addQuotes(grp)}).join(', ') : QueryGenerator.addQuotes(options.group) | ||
options.group = Array.isArray(options.group) ? options.group.map(function(t) { return this.quoteIdentifiers(t)}.bind(this)).join(', ') : qa(options.group) | ||
query += " GROUP BY " + options.group | ||
@@ -250,9 +225,9 @@ } | ||
var value = attrValueHash[key] | ||
values.push(Utils.addTicks(key) + "=" + escape((value instanceof Date) ? Utils.toSqlDate(value) : value)) | ||
values.push(this.quoteIdentifier(key) + "=" + this.escape(value)) | ||
} | ||
var replacements = { | ||
table: Utils.addTicks(tableName), | ||
table: this.quoteIdentifier(tableName), | ||
values: values.join(","), | ||
where: MySqlQueryGenerator.getWhereConditions(where) | ||
where: this.getWhereConditions(where) | ||
} | ||
@@ -268,4 +243,4 @@ | ||
var replacements = { | ||
table: Utils.addTicks(tableName), | ||
where: MySqlQueryGenerator.getWhereConditions(where) | ||
table: this.quoteIdentifier(tableName), | ||
where: this.getWhereConditions(where) | ||
} | ||
@@ -284,9 +259,9 @@ | ||
var value = attrValueHash[key] | ||
values.push(Utils.addTicks(key) + "=" + Utils.addTicks(key) + "+ " + escape((value instanceof Date) ? Utils.toSqlDate(value) : value)) | ||
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + "+ " + this.escape(value)) | ||
} | ||
var replacements = { | ||
table: Utils.addTicks(tableName), | ||
table: this.quoteIdentifier(tableName), | ||
values: values.join(","), | ||
where: MySqlQueryGenerator.getWhereConditions(where) | ||
where: this.getWhereConditions(where) | ||
} | ||
@@ -321,3 +296,3 @@ | ||
template += " DEFAULT <%= defaultValue %>" | ||
replacements.defaultValue = Utils.escape(dataType.defaultValue) | ||
replacements.defaultValue = this.escape(dataType.defaultValue) | ||
} | ||
@@ -339,8 +314,8 @@ | ||
template += " REFERENCES <%= referencesTable %> (<%= referencesKey %>)" | ||
replacements.referencesTable = Utils.addTicks(dataType.references) | ||
replacements.referencesTable = this.quoteIdentifier(dataType.references) | ||
if(dataType.referencesKey) { | ||
replacements.referencesKey = Utils.addTicks(dataType.referencesKey) | ||
replacements.referencesKey = this.quoteIdentifier(dataType.referencesKey) | ||
} else { | ||
replacements.referencesKey = Utils.addTicks('id') | ||
replacements.referencesKey = this.quoteIdentifier('id') | ||
} | ||
@@ -408,3 +383,3 @@ | ||
return hashToWhereConditions(hash).replace(/\\'/g, "''"); | ||
return hashToWhereConditions.call(this, hash).replace(/\\'/g, "''"); | ||
}, | ||
@@ -439,10 +414,10 @@ | ||
removeColumnQuery: function(tableName, attributes) { | ||
attributes = QueryGenerator.attributesToSQL(attributes) | ||
attributes = this.attributesToSQL(attributes) | ||
var backupTableName = tableName + "_backup" | ||
var query = [ | ||
QueryGenerator.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'), | ||
this.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'), | ||
"INSERT INTO <%= tableName %>_backup SELECT <%= attributeNames %> FROM <%= tableName %>;", | ||
"DROP TABLE <%= tableName %>;", | ||
QueryGenerator.createTableQuery(tableName, attributes), | ||
this.createTableQuery(tableName, attributes), | ||
"INSERT INTO <%= tableName %> SELECT <%= attributeNames %> FROM <%= tableName %>_backup;", | ||
@@ -459,10 +434,10 @@ "DROP TABLE <%= tableName %>_backup;" | ||
renameColumnQuery: function(tableName, attrNameBefore, attrNameAfter, attributes) { | ||
attributes = QueryGenerator.attributesToSQL(attributes) | ||
attributes = this.attributesToSQL(attributes) | ||
var backupTableName = tableName + "_backup" | ||
var query = [ | ||
QueryGenerator.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'), | ||
this.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'), | ||
"INSERT INTO <%= tableName %>_backup SELECT <%= attributeNamesImport %> FROM <%= tableName %>;", | ||
"DROP TABLE <%= tableName %>;", | ||
QueryGenerator.createTableQuery(tableName, attributes), | ||
this.createTableQuery(tableName, attributes), | ||
"INSERT INTO <%= tableName %> SELECT <%= attributeNamesExport %> FROM <%= tableName %>_backup;", | ||
@@ -476,6 +451,6 @@ "DROP TABLE <%= tableName %>_backup;" | ||
return (attrNameAfter === attr) ? attrNameBefore + ' AS ' + attr : attr | ||
}).join(', '), | ||
}.bind(this)).join(', '), | ||
attributeNamesExport: Utils._.keys(attributes).map(function(attr) { | ||
return attr | ||
}).join(', ') | ||
}.bind(this)).join(', ') | ||
}) | ||
@@ -486,3 +461,28 @@ }, | ||
return sql.replace(/DEFAULT '?false'?/g, "DEFAULT 0").replace(/DEFAULT '?true'?/g, "DEFAULT 1") | ||
}, | ||
quoteIdentifier: function(identifier, force) { | ||
return Utils.addTicks(identifier, "`") | ||
}, | ||
quoteIdentifiers: function(identifiers, force) { | ||
return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.') | ||
}, | ||
escape: function(value) { | ||
if (value instanceof Date) { | ||
value = Utils.toSqlDate(value) | ||
} | ||
if (typeof value === 'string') { | ||
return "'" + value.replace(/'/g, "''") + "'"; | ||
} else if (typeof value === 'boolean') { | ||
return value ? 1 : 0; // SQLite has no type boolean | ||
} else if (value === null || value === undefined) { | ||
return 'NULL'; | ||
} else { | ||
return value; | ||
} | ||
} | ||
} | ||
@@ -489,0 +489,0 @@ |
@@ -9,9 +9,9 @@ var util = require("util") | ||
if (fct) { | ||
if(process.domain) { | ||
return process.domain.bind(fct); | ||
} | ||
if (process.domain) { | ||
return process.domain.bind(fct) | ||
} | ||
} | ||
return fct; | ||
}; | ||
return fct | ||
} | ||
@@ -58,2 +58,3 @@ module.exports = (function() { | ||
// emit the events on the foreign emitter once events got triggered for me | ||
CustomEventEmitter.prototype.proxy = function(emitter) { | ||
@@ -78,5 +79,3 @@ proxyEventKeys.forEach(function (eventKey) { | ||
return CustomEventEmitter; | ||
return CustomEventEmitter | ||
})() | ||
@@ -283,4 +283,4 @@ var Utils = require('./utils') | ||
QueryInterface.prototype.bulkDelete = function(tableName, identifier) { | ||
var sql = this.QueryGenerator.deleteQuery(tableName, identifier, {limit: null}) | ||
QueryInterface.prototype.bulkDelete = function(tableName, identifier, options) { | ||
var sql = this.QueryGenerator.deleteQuery(tableName, identifier, Utils._.defaults(options || {}, {limit: null})) | ||
return queryAndEmit.call(this, sql, 'bulkDelete') | ||
@@ -362,2 +362,29 @@ } | ||
// Helper methods useful for querying | ||
/** | ||
* Escape an identifier (e.g. a table or attribute name). If force is true, | ||
* the identifier will be quoted even if the `quoteIdentifiers` option is | ||
* false. | ||
*/ | ||
QueryInterface.prototype.quoteIdentifier = function(identifier, force) { | ||
return this.QueryGenerator.quoteIdentifier(identifier, force) | ||
} | ||
/** | ||
* Split an identifier into .-separated tokens and quote each part. | ||
* If force is true, the identifier will be quoted even if the | ||
* `quoteIdentifiers` option is false. | ||
*/ | ||
QueryInterface.prototype.quoteIdentifiers = function(identifiers, force) { | ||
return this.QueryGenerator.quoteIdentifiers(identifiers, force) | ||
} | ||
/** | ||
* Escape a value (e.g. a string, number or date) | ||
*/ | ||
QueryInterface.prototype.escape = function(value) { | ||
return this.QueryGenerator.escape(value) | ||
} | ||
// private | ||
@@ -364,0 +391,0 @@ |
@@ -29,2 +29,3 @@ var url = require("url") | ||
@param {Object} [options.pool={}] Something. | ||
@param {Boolean} [options.quoteIdentifiers=true] Set to `false` to make table names and attributes case-insensitive on Postgres and skip double quoting of them. | ||
@@ -82,3 +83,4 @@ @example | ||
replication: false, | ||
pool: {} | ||
pool: {}, | ||
quoteIdentifiers: true | ||
}, options || {}) | ||
@@ -85,0 +87,0 @@ |
@@ -38,14 +38,2 @@ var util = require("util") | ||
}, | ||
TICK_CHAR: '`', | ||
addTicks: function(s, tickChar) { | ||
tickChar = tickChar || Utils.TICK_CHAR | ||
return tickChar + Utils.removeTicks(s, tickChar) + tickChar | ||
}, | ||
removeTicks: function(s, tickChar) { | ||
tickChar = tickChar || Utils.TICK_CHAR | ||
return s.replace(new RegExp(tickChar, 'g'), "") | ||
}, | ||
escape: function(s) { | ||
return SqlString.escape(s, true, "local").replace(/\\"/g, '"') | ||
}, | ||
format: function(arr, dialect) { | ||
@@ -184,2 +172,18 @@ var timeZone = null; | ||
return now | ||
}, | ||
// Note: Use the `quoteIdentifier()` and `escape()` methods on the | ||
// `QueryInterface` instead for more portable code. | ||
TICK_CHAR: '`', | ||
addTicks: function(s, tickChar) { | ||
tickChar = tickChar || Utils.TICK_CHAR | ||
return tickChar + Utils.removeTicks(s, tickChar) + tickChar | ||
}, | ||
removeTicks: function(s, tickChar) { | ||
tickChar = tickChar || Utils.TICK_CHAR | ||
return s.replace(new RegExp(tickChar, 'g'), "") | ||
}, | ||
escape: function(s) { | ||
return SqlString.escape(s, true, "local").replace(/\\"/g, '"') | ||
} | ||
@@ -190,2 +194,2 @@ } | ||
Utils.QueryChainer = require("./query-chainer") | ||
Utils.Lingo = require("lingo") | ||
Utils.Lingo = require("lingo") |
{ | ||
"name": "sequelize", | ||
"description": "Multi dialect ORM for Node.JS", | ||
"version": "1.7.0-alpha2", | ||
"version": "2.0.0-alpha1", | ||
"author": "Sascha Depold <sascha@depold.com>", | ||
@@ -6,0 +6,0 @@ "contributors": [ |
@@ -11,3 +11,6 @@ var config = require("../config/config") | ||
var setup = function() { | ||
User = sequelize.define('User', { username: Sequelize.STRING }) | ||
User = sequelize.define('User', { username: Sequelize.STRING, enabled: { | ||
type: Sequelize.BOOLEAN, | ||
defaultValue: true | ||
}}) | ||
Task = sequelize.define('Task', { title: Sequelize.STRING }) | ||
@@ -92,2 +95,25 @@ } | ||
it('extends the id where param with the supplied where params', function() { | ||
Task.belongsTo(User, {as: 'User'}) | ||
Helpers.async(function(done) { | ||
User.sync({force: true}).success(function() { | ||
Task.sync({force: true}).success(done) | ||
}) | ||
}) | ||
Helpers.async(function(done) { | ||
User.create({username: 'asd', enabled: false}).success(function(u) { | ||
Task.create({title: 'a task'}).success(function(t) { | ||
t.setUser(u).success(function() { | ||
t.getUser({where: {enabled: true}}).success(function(user) { | ||
expect(user).toEqual(null) | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
it("handles self associations", function() { | ||
@@ -94,0 +120,0 @@ Helpers.async(function(done) { |
@@ -311,7 +311,8 @@ var config = require("./config/config") | ||
}).success(function(user) { | ||
var emitter = user.updateAttributes({name: 'foobar'}) | ||
emitter.success(function() { | ||
expect(emitter.query.sql).toMatch(/WHERE [`"]identifier[`"]..identifier./) | ||
done() | ||
}) | ||
user | ||
.updateAttributes({name: 'foobar'}) | ||
.on('sql', function(sql) { | ||
expect(sql).toMatch(/WHERE [`"]identifier[`"]..identifier./) | ||
done() | ||
}) | ||
}) | ||
@@ -318,0 +319,0 @@ }) |
@@ -303,2 +303,8 @@ var config = require("../config/config") | ||
expectation: "DELETE FROM `myTable` WHERE `id`=1 LIMIT 1" | ||
},{ | ||
arguments: ['myTable', undefined, {truncate: true}], | ||
expectation: "TRUNCATE `myTable`" | ||
},{ | ||
arguments: ['myTable', 1, {limit: 10, truncate: true}], | ||
expectation: "TRUNCATE `myTable`" | ||
}, { | ||
@@ -396,3 +402,4 @@ arguments: ['myTable', 1, {limit: 10}], | ||
var context = test.context || {options: {}}; | ||
var conditions = QueryGenerator[suiteTitle].apply(context, test.arguments) | ||
QueryGenerator.options = context.options | ||
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) | ||
@@ -399,0 +406,0 @@ expect(conditions).toEqual(test.expectation) |
@@ -71,2 +71,30 @@ var config = require("../config/config") | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: [{id: {type: 'INTEGER', references: 'Bar'}}], | ||
expectation: {id: 'INTEGER REFERENCES Bar (id)'}, | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: [{id: {type: 'INTEGER', references: 'Bar', referencesKey: 'pk'}}], | ||
expectation: {id: 'INTEGER REFERENCES Bar (pk)'}, | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: [{id: {type: 'INTEGER', references: 'Bar', onDelete: 'CASCADE'}}], | ||
expectation: {id: 'INTEGER REFERENCES Bar (id) ON DELETE CASCADE'}, | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: [{id: {type: 'INTEGER', references: 'Bar', onUpdate: 'RESTRICT'}}], | ||
expectation: {id: 'INTEGER REFERENCES Bar (id) ON UPDATE RESTRICT'}, | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: [{id: {type: 'INTEGER', allowNull: false, defaultValue: 1, references: 'Bar', onDelete: 'CASCADE', onUpdate: 'RESTRICT'}}], | ||
expectation: {id: 'INTEGER NOT NULL DEFAULT 1 REFERENCES Bar (id) ON DELETE CASCADE ON UPDATE RESTRICT'}, | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
], | ||
@@ -77,3 +105,3 @@ | ||
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}], | ||
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255));" | ||
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255));", | ||
}, | ||
@@ -95,2 +123,29 @@ { | ||
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255), \"otherId\" INTEGER REFERENCES \"otherTable\" (\"id\") ON DELETE CASCADE ON UPDATE NO ACTION);" | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}], | ||
expectation: "CREATE TABLE IF NOT EXISTS myTable (title VARCHAR(255), name VARCHAR(255));", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: ['mySchema.myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}], | ||
expectation: "CREATE TABLE IF NOT EXISTS mySchema.myTable (title VARCHAR(255), name VARCHAR(255));", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: ['myTable', {title: 'ENUM("A", "B", "C")', name: 'VARCHAR(255)'}], | ||
expectation: "DROP TYPE IF EXISTS enum_myTable_title; CREATE TYPE enum_myTable_title AS ENUM(\"A\", \"B\", \"C\"); CREATE TABLE IF NOT EXISTS myTable (title enum_myTable_title, name VARCHAR(255));", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)', id: 'INTEGER PRIMARY KEY'}], | ||
expectation: "CREATE TABLE IF NOT EXISTS myTable (title VARCHAR(255), name VARCHAR(255), id INTEGER , PRIMARY KEY (id));", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)', otherId: 'INTEGER REFERENCES otherTable (id) ON DELETE CASCADE ON UPDATE NO ACTION'}], | ||
expectation: "CREATE TABLE IF NOT EXISTS myTable (title VARCHAR(255), name VARCHAR(255), otherId INTEGER REFERENCES otherTable (id) ON DELETE CASCADE ON UPDATE NO ACTION);", | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
@@ -115,2 +170,24 @@ ], | ||
expectation: "DROP TABLE IF EXISTS \"mySchema\".\"myTable\" CASCADE;" | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['myTable'], | ||
expectation: "DROP TABLE IF EXISTS myTable;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: ['mySchema.myTable'], | ||
expectation: "DROP TABLE IF EXISTS mySchema.myTable;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: ['myTable', {cascade: true}], | ||
expectation: "DROP TABLE IF EXISTS myTable CASCADE;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: ['mySchema.myTable', {cascade: true}], | ||
expectation: "DROP TABLE IF EXISTS mySchema.myTable CASCADE;", | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
@@ -172,2 +249,74 @@ ], | ||
expectation: "SELECT * FROM \"mySchema\".\"myTable\" WHERE \"mySchema\".\"myTable\".\"name\"='foo'';DROP TABLE mySchema.myTable;';" | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['myTable'], | ||
expectation: "SELECT * FROM myTable;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {attributes: ['id', 'name']}], | ||
expectation: "SELECT id, name FROM myTable;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {where: {id: 2}}], | ||
expectation: "SELECT * FROM myTable WHERE myTable.id=2;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {where: {name: 'foo'}}], | ||
expectation: "SELECT * FROM myTable WHERE myTable.name='foo';", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {where: {name: "foo';DROP TABLE myTable;"}}], | ||
expectation: "SELECT * FROM myTable WHERE myTable.name='foo'';DROP TABLE myTable;';", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {where: 2}], | ||
expectation: "SELECT * FROM myTable WHERE myTable.id=2;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['foo', { attributes: [['count(*)', 'count']] }], | ||
expectation: 'SELECT count(*) as count FROM foo;', | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {where: "foo='bar'"}], | ||
expectation: "SELECT * FROM myTable WHERE foo='bar';", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {order: "id DESC"}], | ||
expectation: "SELECT * FROM myTable ORDER BY id DESC;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {group: "name"}], | ||
expectation: "SELECT * FROM myTable GROUP BY name;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {group: ["name"]}], | ||
expectation: "SELECT * FROM myTable GROUP BY name;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {group: ["name","title"]}], | ||
expectation: "SELECT * FROM myTable GROUP BY name, title;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {limit: 10}], | ||
expectation: "SELECT * FROM myTable LIMIT 10;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {limit: 10, offset: 2}], | ||
expectation: "SELECT * FROM myTable LIMIT 10 OFFSET 2;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
title: 'uses offset even if no limit was passed', | ||
arguments: ['myTable', {offset: 2}], | ||
expectation: "SELECT * FROM myTable OFFSET 2;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable'], | ||
expectation: "SELECT * FROM mySchema.myTable;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', {where: {name: "foo';DROP TABLE mySchema.myTable;"}}], | ||
expectation: "SELECT * FROM mySchema.myTable WHERE mySchema.myTable.name='foo'';DROP TABLE mySchema.myTable;';", | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
@@ -213,3 +362,51 @@ ], | ||
expectation: "INSERT INTO \"mySchema\".\"myTable\" (\"name\") VALUES ('foo'';DROP TABLE mySchema.myTable;') RETURNING *;" | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['myTable', {name: 'foo'}], | ||
expectation: "INSERT INTO myTable (name) VALUES ('foo') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}], | ||
expectation: "INSERT INTO myTable (name) VALUES ('foo'';DROP TABLE myTable;') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}], | ||
expectation: "INSERT INTO myTable (name,birthday) VALUES ('foo','2011-03-27 10:01:55.0Z') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: 'foo', foo: 1}], | ||
expectation: "INSERT INTO myTable (name,foo) VALUES ('foo',1) RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: 'foo', nullValue: null}], | ||
expectation: "INSERT INTO myTable (name,nullValue) VALUES ('foo',NULL) RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: 'foo', nullValue: null}], | ||
expectation: "INSERT INTO myTable (name,nullValue) VALUES ('foo',NULL) RETURNING *;", | ||
context: {options: {omitNull: false, quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: 'foo', nullValue: null}], | ||
expectation: "INSERT INTO myTable (name) VALUES ('foo') RETURNING *;", | ||
context: {options: {omitNull: true, quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: 'foo', nullValue: undefined}], | ||
expectation: "INSERT INTO myTable (name) VALUES ('foo') RETURNING *;", | ||
context: {options: {omitNull: true, quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', {name: 'foo'}], | ||
expectation: "INSERT INTO mySchema.myTable (name) VALUES ('foo') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', {name: JSON.stringify({info: 'Look ma a " quote'})}], | ||
expectation: "INSERT INTO mySchema.myTable (name) VALUES ('{\"info\":\"Look ma a \\\" quote\"}') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}], | ||
expectation: "INSERT INTO mySchema.myTable (name) VALUES ('foo'';DROP TABLE mySchema.myTable;') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
], | ||
@@ -254,2 +451,49 @@ | ||
expectation: "INSERT INTO \"mySchema\".\"myTable\" (\"name\") VALUES ('foo'';DROP TABLE mySchema.myTable;'),('bar') RETURNING *;" | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['myTable', [{name: 'foo'}, {name: 'bar'}]], | ||
expectation: "INSERT INTO myTable (name) VALUES ('foo'),('bar') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', [{name: "foo';DROP TABLE myTable;"}, {name: 'bar'}]], | ||
expectation: "INSERT INTO myTable (name) VALUES ('foo'';DROP TABLE myTable;'),('bar') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', [{name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {name: 'bar', birthday: new Date(Date.UTC(2012, 2, 27, 10, 1, 55))}]], | ||
expectation: "INSERT INTO myTable (name,birthday) VALUES ('foo','2011-03-27 10:01:55.0Z'),('bar','2012-03-27 10:01:55.0Z') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', [{name: 'foo', foo: 1}, {name: 'bar', foo: 2}]], | ||
expectation: "INSERT INTO myTable (name,foo) VALUES ('foo',1),('bar',2) RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', [{name: 'foo', nullValue: null}, {name: 'bar', nullValue: null}]], | ||
expectation: "INSERT INTO myTable (name,nullValue) VALUES ('foo',NULL),('bar',NULL) RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', [{name: 'foo', nullValue: null}, {name: 'bar', nullValue: null}]], | ||
expectation: "INSERT INTO myTable (name,nullValue) VALUES ('foo',NULL),('bar',NULL) RETURNING *;", | ||
context: {options: {quoteIdentifiers: false, omitNull: false}}, | ||
}, { | ||
arguments: ['myTable', [{name: 'foo', nullValue: null}, {name: 'bar', nullValue: null}]], | ||
expectation: "INSERT INTO myTable (name,nullValue) VALUES ('foo',NULL),('bar',NULL) RETURNING *;", | ||
context: {options: {omitNull: true, quoteIdentifiers: false}} // Note: We don't honour this because it makes little sense when some rows may have nulls and others not | ||
}, { | ||
arguments: ['myTable', [{name: 'foo', nullValue: undefined}, {name: 'bar', nullValue: undefined}]], | ||
expectation: "INSERT INTO myTable (name,nullValue) VALUES ('foo',NULL),('bar',NULL) RETURNING *;", | ||
context: {options: {omitNull: true, quoteIdentifiers: false}} // Note: As above | ||
}, { | ||
arguments: ['mySchema.myTable', [{name: 'foo'}, {name: 'bar'}]], | ||
expectation: "INSERT INTO mySchema.myTable (name) VALUES ('foo'),('bar') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', [{name: JSON.stringify({info: 'Look ma a " quote'})}, {name: JSON.stringify({info: 'Look ma another " quote'})}]], | ||
expectation: "INSERT INTO mySchema.myTable (name) VALUES ('{\"info\":\"Look ma a \\\" quote\"}'),('{\"info\":\"Look ma another \\\" quote\"}') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', [{name: "foo';DROP TABLE mySchema.myTable;"}, {name: 'bar'}]], | ||
expectation: "INSERT INTO mySchema.myTable (name) VALUES ('foo'';DROP TABLE mySchema.myTable;'),('bar') RETURNING *;", | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
@@ -292,2 +536,45 @@ ], | ||
expectation: "UPDATE \"mySchema\".\"myTable\" SET \"name\"='foo'';DROP TABLE mySchema.myTable;' WHERE \"name\"='foo' RETURNING *" | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], | ||
expectation: "UPDATE myTable SET name='foo',birthday='2011-03-27 10:01:55.0Z' WHERE id=2 RETURNING *", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, 2], | ||
expectation: "UPDATE myTable SET name='foo',birthday='2011-03-27 10:01:55.0Z' WHERE id=2 RETURNING *", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {bar: 2}, {name: 'foo'}], | ||
expectation: "UPDATE myTable SET bar=2 WHERE name='foo' RETURNING *", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}, {name: 'foo'}], | ||
expectation: "UPDATE myTable SET name='foo'';DROP TABLE myTable;' WHERE name='foo' RETURNING *", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {bar: 2, nullValue: null}, {name: 'foo'}], | ||
expectation: "UPDATE myTable SET bar=2,nullValue=NULL WHERE name='foo' RETURNING *", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {bar: 2, nullValue: null}, {name: 'foo'}], | ||
expectation: "UPDATE myTable SET bar=2,nullValue=NULL WHERE name='foo' RETURNING *", | ||
context: {options: {omitNull: false, quoteIdentifiers: false}}, | ||
}, { | ||
arguments: ['myTable', {bar: 2, nullValue: null}, {name: 'foo'}], | ||
expectation: "UPDATE myTable SET bar=2 WHERE name='foo' RETURNING *", | ||
context: {options: {omitNull: true, quoteIdentifiers: false}}, | ||
}, { | ||
arguments: ['myTable', {bar: 2, nullValue: undefined}, {name: 'foo'}], | ||
expectation: "UPDATE myTable SET bar=2 WHERE name='foo' RETURNING *", | ||
context: {options: {omitNull: true, quoteIdentifiers: false}}, | ||
}, { | ||
arguments: ['mySchema.myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], | ||
expectation: "UPDATE mySchema.myTable SET name='foo',birthday='2011-03-27 10:01:55.0Z' WHERE id=2 RETURNING *", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {name: 'foo'}], | ||
expectation: "UPDATE mySchema.myTable SET name='foo'';DROP TABLE mySchema.myTable;' WHERE name='foo' RETURNING *", | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
@@ -304,2 +591,8 @@ ], | ||
}, { | ||
arguments: ['myTable', undefined, {truncate: true}], | ||
expectation: "TRUNCATE \"myTable\"" | ||
}, { | ||
arguments: ['myTable', 1, {limit: 10, truncate: true}], | ||
expectation: "TRUNCATE \"myTable\"" | ||
}, { | ||
arguments: ['myTable', 1, {limit: 10}], | ||
@@ -319,2 +612,33 @@ expectation: "DELETE FROM \"myTable\" WHERE \"id\" IN (SELECT \"id\" FROM \"myTable\" WHERE \"id\"=1 LIMIT 10)" | ||
expectation: "DELETE FROM \"myTable\" WHERE \"id\" IN (SELECT \"id\" FROM \"myTable\" WHERE \"name\"='foo')" | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['myTable', {name: 'foo'}], | ||
expectation: "DELETE FROM myTable WHERE id IN (SELECT id FROM myTable WHERE name='foo' LIMIT 1)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', 1], | ||
expectation: "DELETE FROM myTable WHERE id IN (SELECT id FROM myTable WHERE id=1 LIMIT 1)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', 1, {limit: 10}], | ||
expectation: "DELETE FROM myTable WHERE id IN (SELECT id FROM myTable WHERE id=1 LIMIT 10)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}, {limit: 10}], | ||
expectation: "DELETE FROM myTable WHERE id IN (SELECT id FROM myTable WHERE name='foo'';DROP TABLE myTable;' LIMIT 10)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', {name: 'foo'}], | ||
expectation: "DELETE FROM mySchema.myTable WHERE id IN (SELECT id FROM mySchema.myTable WHERE name='foo' LIMIT 1)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {limit: 10}], | ||
expectation: "DELETE FROM mySchema.myTable WHERE id IN (SELECT id FROM mySchema.myTable WHERE name='foo'';DROP TABLE mySchema.myTable;' LIMIT 10)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['myTable', {name: 'foo'}, {limit: null}], | ||
expectation: "DELETE FROM myTable WHERE id IN (SELECT id FROM myTable WHERE name='foo')", | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
@@ -343,2 +667,28 @@ ], | ||
expectation: 'CREATE INDEX \"user_username_is_admin\" ON \"mySchema\".\"User\" (\"username\", \"isAdmin\")' | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['User', ['username', 'isAdmin']], | ||
expectation: 'CREATE INDEX user_username_is_admin ON User (username, isAdmin)', | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: [ | ||
'User', [ | ||
{ attribute: 'username', length: 10, order: 'ASC'}, | ||
'isAdmin' | ||
] | ||
], | ||
expectation: "CREATE INDEX user_username_is_admin ON User (username(10) ASC, isAdmin)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: [ | ||
'User', ['username', 'isAdmin'], { indicesType: 'FULLTEXT', indexName: 'bar'} | ||
], | ||
expectation: "CREATE FULLTEXT INDEX bar ON User (username, isAdmin)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['mySchema.User', ['username', 'isAdmin']], | ||
expectation: 'CREATE INDEX user_username_is_admin ON mySchema.User (username, isAdmin)', | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
@@ -368,2 +718,17 @@ ], | ||
expectation: "DROP INDEX IF EXISTS \"mySchema\".\"user_foo_bar\"" | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: ['User', 'user_foo_bar'], | ||
expectation: "DROP INDEX IF EXISTS user_foo_bar", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['User', ['foo', 'bar']], | ||
expectation: "DROP INDEX IF EXISTS user_foo_bar", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, { | ||
arguments: ['User', 'mySchema.user_foo_bar'], | ||
expectation: "DROP INDEX IF EXISTS mySchema.user_foo_bar", | ||
context: {options: {quoteIdentifiers: false}} | ||
} | ||
@@ -380,3 +745,15 @@ ], | ||
expectation: "\"id\" IN (NULL)" | ||
} | ||
}, | ||
// Variants when quoteIdentifiers is false | ||
{ | ||
arguments: [{ id: [1,2,3] }], | ||
expectation: "id IN (1,2,3)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
{ | ||
arguments: [{ id: [] }], | ||
expectation: "id IN (NULL)", | ||
context: {options: {quoteIdentifiers: false}} | ||
}, | ||
] | ||
@@ -392,3 +769,5 @@ } | ||
var context = test.context || {options: {}}; | ||
var conditions = QueryGenerator[suiteTitle].apply(context, test.arguments) | ||
QueryGenerator.options = context.options | ||
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) | ||
expect(conditions).toEqual(test.expectation) | ||
@@ -395,0 +774,0 @@ }) |
@@ -202,2 +202,5 @@ var Sequelize = require("../../index") | ||
}, { | ||
arguments: ['myTable', 1, {truncate: true}], | ||
expectation: "DELETE FROM `myTable` WHERE `id`=1" | ||
}, { | ||
arguments: ['myTable', 1, {limit: 10}], | ||
@@ -207,3 +210,3 @@ expectation: "DELETE FROM `myTable` WHERE `id`=1" | ||
arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}, {limit: 10}], | ||
expectation: "DELETE FROM `myTable` WHERE `name`='foo\\';DROP TABLE myTable;'" | ||
expectation: "DELETE FROM `myTable` WHERE `name`='foo'';DROP TABLE myTable;'" | ||
}, { | ||
@@ -223,4 +226,4 @@ arguments: ['myTable', {name: 'foo'}, {limit: null}], | ||
var context = test.context || {options: {}}; | ||
var conditions = QueryGenerator[suiteTitle].apply(context, test.arguments) | ||
QueryGenerator.options = context.options | ||
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) | ||
expect(conditions).toEqual(test.expectation) | ||
@@ -227,0 +230,0 @@ }) |
@@ -12,2 +12,3 @@ if (typeof require === 'function') { | ||
describe(Helpers.getTestDialectTeaser("HasMany"), function() { | ||
before(function(done) { | ||
@@ -14,0 +15,0 @@ var self = this |
@@ -23,3 +23,3 @@ if (typeof require === 'function') { | ||
describe('setAssociation', function() { | ||
it('clears the association if null is passed', function(done) { | ||
it('//clears the association if null is passed', function(done) { | ||
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING }) | ||
@@ -26,0 +26,0 @@ , Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING }) |
@@ -219,5 +219,7 @@ if(typeof require === 'function') { | ||
username: user.username | ||
}).success(function (_user) { | ||
}).success(function (_user, created) { | ||
expect(_user.id).toEqual(user.id) | ||
expect(_user.username).toEqual('Username') | ||
expect(created).toBeFalse() | ||
done() | ||
@@ -236,6 +238,8 @@ }) | ||
this.User.create(data).success(function (user) { | ||
self.User.findOrCreate(data).success(function (_user) { | ||
self.User.findOrCreate(data).success(function (_user, created) { | ||
expect(_user.id).toEqual(user.id) | ||
expect(_user.username).toEqual('Username') | ||
expect(_user.data).toEqual('ThisIsData') | ||
expect(created).toBeFalse() | ||
done() | ||
@@ -255,5 +259,7 @@ }) | ||
this.User.findOrCreate(data, default_values).success(function (user) { | ||
this.User.findOrCreate(data, default_values).success(function (user, created) { | ||
expect(user.username).toEqual('Username') | ||
expect(user.data).toEqual('ThisIsData') | ||
expect(created).toBeTrue() | ||
done() | ||
@@ -260,0 +266,0 @@ }) |
@@ -425,3 +425,3 @@ if (typeof require === 'function') { | ||
expect(err.validateTest[0]).toBeDefined() | ||
expect(err.validateTest[0].indexOf('Invalid integer:')).toBeGreaterThan(-1) | ||
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1) | ||
done() | ||
@@ -428,0 +428,0 @@ }) |
@@ -10,3 +10,3 @@ if(typeof require === 'function') { | ||
describe(Helpers.getTestDialectTeaser("DAO"), function() { | ||
describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { | ||
describe('validations', function() { | ||
@@ -205,3 +205,3 @@ before(function(done) { | ||
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function() { | ||
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function(done) { | ||
var validations = {} | ||
@@ -226,7 +226,11 @@ , message = validator + "(" + failingValue + ")" | ||
var failingUser = UserFail.build({ name : failingValue }) | ||
, errors = failingUser.validate() | ||
, errors = undefined; | ||
expect(errors).not.toBeNull() | ||
expect(errors).toEqual({ name : [message] }) | ||
}) | ||
failingUser.validate().done( function(err,_errors) { | ||
expect(_errors).not.toBeNull(); | ||
expect(_errors).toEqual({ name : [message] }); | ||
done(); | ||
}); | ||
}); | ||
} | ||
@@ -240,3 +244,3 @@ | ||
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function() { | ||
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function(done) { | ||
var validations = {} | ||
@@ -259,4 +263,11 @@ | ||
var successfulUser = UserSuccess.build({ name: succeedingValue }) | ||
expect(successfulUser.validate()).toBeNull() | ||
var successfulUser = UserSuccess.build({ name: succeedingValue }); | ||
successfulUser.validate().success( function() { | ||
expect(arguments.length).toBe(0); | ||
done(); | ||
}).error(function(err) { | ||
expect(err).toBe({}) | ||
done(); | ||
}); | ||
}) | ||
@@ -267,3 +278,3 @@ } | ||
it('correctly validates using custom validation methods', function() { | ||
it('correctly validates using custom validation methods', function(done) { | ||
var User = this.sequelize.define('User' + Math.random(), { | ||
@@ -273,5 +284,7 @@ name: { | ||
validate: { | ||
customFn: function(val) { | ||
customFn: function(val, next) { | ||
if (val !== "2") { | ||
throw new Error("name should equal '2'") | ||
next("name should equal '2'") | ||
} else { | ||
next() | ||
} | ||
@@ -284,12 +297,19 @@ } | ||
var failingUser = User.build({ name : "3" }) | ||
, errors = failingUser.validate() | ||
expect(errors).not.toBeNull(null) | ||
expect(errors).toEqual({ name: ["name should equal '2'"] }) | ||
failingUser.validate().success(function(errors) { | ||
expect(errors).toEqual({ name: ["name should equal '2'"] }) | ||
var successfulUser = User.build({ name : "2" }) | ||
expect(successfulUser.validate()).toBeNull() | ||
var successfulUser = User.build({ name : "2" }) | ||
successfulUser.validate().success(function() { | ||
expect(arguments.length).toBe(0); | ||
done(); | ||
}).error(function(err) { | ||
expect(err).toBe({}); | ||
done(); | ||
}) | ||
}); | ||
}) | ||
it('skips other validations if allowNull is true and the value is null', function() { | ||
it('skips other validations if allowNull is true and the value is null', function(done) { | ||
var User = this.sequelize.define('User' + Math.random(), { | ||
@@ -305,16 +325,18 @@ age: { | ||
var failingUser = User.build({ age: -1 }) | ||
, errors = failingUser.validate() | ||
User | ||
.build({ age: -1 }) | ||
.validate() | ||
.success(function(errors) { | ||
expect(errors).not.toBeNull(null) | ||
expect(errors).toEqual({ age: ['must be positive'] }) | ||
expect(errors).not.toBeNull(null) | ||
expect(errors).toEqual({ age: ['must be positive'] }) | ||
var successfulUser1 = User.build({ age: null }) | ||
expect(successfulUser1.validate()).toBeNull() | ||
var successfulUser2 = User.build({ age: 1 }) | ||
expect(successfulUser2.validate()).toBeNull() | ||
User.build({ age: null }).validate().success(function() { | ||
User.build({ age: 1 }).validate().success(function() { | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('validates a model with custom model-wide validation methods', function() { | ||
it('validates a model with custom model-wide validation methods', function(done) { | ||
var Foo = this.sequelize.define('Foo' + Math.random(), { | ||
@@ -331,5 +353,7 @@ field1: { | ||
validate: { | ||
xnor: function() { | ||
xnor: function(done) { | ||
if ((this.field1 === null) === (this.field2 === null)) { | ||
throw new Error('xnor failed'); | ||
done('xnor failed') | ||
} else { | ||
done() | ||
} | ||
@@ -340,11 +364,19 @@ } | ||
var failingFoo = Foo.build({ field1: null, field2: null }) | ||
, errors = failingFoo.validate() | ||
expect(errors).not.toBeNull() | ||
expect(errors).toEqual({ 'xnor': ['xnor failed'] }) | ||
Foo | ||
.build({ field1: null, field2: null }) | ||
.validate() | ||
.success(function(errors) { | ||
expect(errors).not.toBeNull() | ||
expect(errors).toEqual({ 'xnor': ['xnor failed'] }) | ||
var successfulFoo = Foo.build({ field1: 33, field2: null }) | ||
expect(successfulFoo.validate()).toBeNull() | ||
Foo | ||
.build({ field1: 33, field2: null }) | ||
.validate() | ||
.success(function(errors) { | ||
expect(errors).not.toBeDefined() | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
@@ -68,2 +68,58 @@ if(typeof require === 'function') { | ||
}) | ||
describe('[POSTGRES] Unquoted identifiers', function() { | ||
before(function(done) { | ||
var self = this | ||
Helpers.initTests({ | ||
dialect: dialect, | ||
beforeComplete: function(sequelize, DataTypes) { | ||
self.sequelize = sequelize | ||
self.sequelize.options.quoteIdentifiers = false | ||
self.User = sequelize.define('User', { | ||
username: DataTypes.STRING, | ||
fullName: DataTypes.STRING // Note mixed case | ||
}) | ||
}, | ||
onComplete: function() { | ||
// We can create a table with non-quoted identifiers | ||
self.User.sync({ force: true }).success(done) | ||
} | ||
}) | ||
}) | ||
it("can insert and select", function(done) { | ||
var self = this | ||
self.User | ||
.create({ username: 'user', fullName: "John Smith" }) | ||
.success(function(user) { | ||
// We can insert into a table with non-quoted identifiers | ||
expect(user.id).toBeDefined() | ||
expect(user.id).not.toBeNull() | ||
expect(user.username).toEqual('user') | ||
expect(user.fullName).toEqual('John Smith') | ||
// We can query by non-quoted identifiers | ||
self.User.find({ | ||
where: {fullName: "John Smith"} | ||
}) | ||
.success(function(user2) { | ||
// We can map values back to non-quoted identifiers | ||
expect(user2.id).toEqual(user.id) | ||
expect(user2.username).toEqual('user') | ||
expect(user2.fullName).toEqual('John Smith') | ||
done(); | ||
}) | ||
.error(function(err) { | ||
console.log(err) | ||
}) | ||
}) | ||
.error(function(err) { | ||
console.log(err) | ||
}) | ||
}) | ||
}) | ||
} |
@@ -290,3 +290,3 @@ if (typeof require === 'function') { | ||
expect(err.validateTest[0]).toBeDefined() | ||
expect(err.validateTest[0].indexOf('Invalid integer:')).toBeGreaterThan(-1) | ||
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1) | ||
done() | ||
@@ -293,0 +293,0 @@ }) |
@@ -96,3 +96,4 @@ if(typeof require === 'function') { | ||
username: Helpers.Sequelize.STRING, | ||
isAdmin: Helpers.Sequelize.BOOLEAN | ||
isAdmin: Helpers.Sequelize.BOOLEAN, | ||
enumVals: Helpers.Sequelize.ENUM('hello', 'world') | ||
}).success(done) | ||
@@ -107,2 +108,3 @@ }) | ||
var isAdmin = metadata.isAdmin | ||
var enumVals = metadata.enumVals | ||
@@ -117,2 +119,7 @@ expect(username.type).toEqual(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)') | ||
if (dialect === 'postgres') { | ||
expect(enumVals.special).toBeArray(); | ||
expect(enumVals.special.length).toEqual(2); | ||
} | ||
done() | ||
@@ -119,0 +126,0 @@ }) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1058131
145
16146