不要怂,就是干,撸起袖子干!

Commit b5334517 by Sascha Depold

Merge branch 'master' into feature/logging

Conflicts:
	changelog.md
	lib/dialects/postgres/query.js
1 parent 4b217374
......@@ -7,7 +7,7 @@ var path = require("path")
, moment = require("moment")
, _ = Sequelize.Utils._
, url = require("url")
, supportCS = undefined
, supportsCoffee
var configuration = {
configFile: process.cwd() + '/config/config.json',
......@@ -16,6 +16,19 @@ var configuration = {
migrationsPath: process.cwd() + '/migrations'
}
var loadDefaultOptions = function(pathToOptionsFile) {
try {
var options = require(path.resolve(process.cwd(), '/', pathToOptionsFile))
Object.keys(options).forEach(function(key) {
program[key] = options[key]
})
} catch(e) {
console.log(e);
throw new Error('Error reading "' + pathToOptionsFile + '".')
}
}
var configFileExists = function() {
return fs.existsSync(configuration.configFile)
}
......@@ -82,12 +95,30 @@ var createMigrationsFolder = function(force) {
}
try {
fs.mkdirSync(configuration.migrationsPath)
mkdirp(configuration.migrationsPath)
console.log('Successfully created migrations folder at "' + configuration.migrationsPath + '".')
} catch(e) {
}
}
var mkdirp = function (path, root) {
var dirs = path.split('/')
, dir = dirs.shift()
, root = (root || '') + dir + '/'
try {
fs.mkdirSync(root)
} catch (e) {
// dir wasn't made, something went wrong
if (!fs.statSync(root).isDirectory()) {
throw new Error(e)
}
}
return !dirs.length || mkdirp(dirs.join('/'), root)
}
var parseDbUrl = function(urlString) {
var urlParts,
config = {};
......@@ -142,6 +173,8 @@ var readConfig = function() {
config = config[configuration.environment]
}
supportsCoffee = program.coffee || config.coffee
return config
}
......@@ -153,11 +186,17 @@ program
.option('-u, --undo', 'Undo the last migration.')
.option('-f, --force', 'Forces the action to be done.')
.option('-c, --create-migration [migration-name]', 'Creates a new migration.')
.option('-p, --migrations-path <migration-path>', 'migration directory path.')
.option('-U, --url <url>', 'Database url. An alternative to a config file')
.option('-o,--options-path <options_file>', 'Specifies lib options from file.')
.option('--config <config_file>', 'Specifies alternate config file.')
.option('--coffee', 'Consider coffee script files as migration source.')
.parse(process.argv)
if(typeof program.optionsPath === 'string') {
loadDefaultOptions(program.optionsPath);
}
if(typeof program.config === 'string') {
if (isRelativePath(program.config)) {
configuration.configFile = path.join(process.cwd(), program.config);
......@@ -166,23 +205,12 @@ if(typeof program.config === 'string') {
}
}
if(typeof program.env === 'string') {
configuration.environment = program.env
if(typeof program.migrationsPath=== 'string') {
configuration.migrationsPath = program.migrationsPath;
}
var supportCoffeeScript = function() {
if (supportCS === undefined) {
try {
config = readConfig()
} catch(e) {
console.log(e.message)
process.exit(1)
}
supportCS = program.coffee || config.coffee
}
return supportCS
if(typeof program.env === 'string') {
configuration.environment = program.env
}
if (program.migrate || program.undo) {
......@@ -225,7 +253,7 @@ if (program.migrate || program.undo) {
var sequelize = new Sequelize(config.database, config.username, config.password, options)
, migratorOptions = { path: configuration.migrationsPath }
if (supportCoffeeScript()) {
if (supportsCoffee) {
migratorOptions = _.merge(migratorOptions, { filesFilter: /\.js$|\.coffee$/ })
}
......@@ -272,14 +300,22 @@ if (program.migrate || program.undo) {
} else if(program.createMigration) {
createMigrationsFolder()
var mirationContent = ""
, migrationExtension = (supportCoffeeScript()) ? '.coffee' : '.js'
var config;
try {
config = readConfig()
} catch(e) {
console.log(e.message)
process.exit(1)
}
var migrationContent = ""
, migrationExtension = supportsCoffee ? '.coffee' : '.js'
, migrationName = [
moment().format('YYYYMMDDHHmmss'),
(typeof program.createMigration === 'string') ? program.createMigration : 'unnamed-migration'
].join('-') + migrationExtension
if (supportCoffeeScript()) {
if (supportsCoffee) {
migrationContent = [
"module.exports = ",
" up: (migration, DataTypes, done) ->",
......
Notice: All 1.7.x changes are present in 2.0.x aswell
# v2.0.0-dev11
# v2.0.0-dev11 (next)
### Caution: This release contains many changes and is highly experimental
- [PERFORMANCE] increased build performance when using include, which speeds up findAll etc.
- [BUG] Made it possible to use HSTORE both in attribute: HSTORE and attribute: { type: HSTORE } form. Thanks to @tomchentw [#1458](https://github.com/sequelize/sequelize/pull/1458)
......@@ -12,6 +12,8 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
- [BUG] Fix a case where timestamps were not being write protected in `set` when underscored=true. janmeier [#1523](https://github.com/sequelize/sequelize/pull/1523)
- [FEATURE/BUG] Prefetching/includes now fully support schemas
- [FEATURE] Centralize logging. [#1566](https://github.com/sequelize/sequelize/pull/1566)
- [FEATURE/BUG] hstore values are now parsed on find/findAll. Thanks to @nunofgs [#1560](https://github.com/sequelize/sequelize/pull/1560)
- [FEATURE] Read cli options from a file. Thanks to @codeinvain [#1540](https://github.com/sequelize/sequelize/pull/1540)
#### Backwards compatability changes
......@@ -465,4 +467,3 @@ None
- first stable version
- implemented all basic functions
- associations are working
......@@ -82,7 +82,7 @@ module.exports = (function() {
var self = this
, chainer = new Utils.QueryChainer()
, targetAssociation = self.association.targetAssociation
, foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier
, foreignIdentifier = self.association.foreignIdentifier
, sourceKeys = Object.keys(self.association.source.primaryKeys)
, targetKeys = Object.keys(self.association.target.primaryKeys)
, obsoleteAssociations = []
......@@ -175,7 +175,7 @@ module.exports = (function() {
HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes, exists) {
var attributes = {}
, targetAssociation = this.association.targetAssociation
, foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier
, foreignIdentifier = targetAssociation.identifier
, options = {}
var sourceKeys = Object.keys(this.association.source.primaryKeys);
......
......@@ -58,7 +58,7 @@ module.exports = (function() {
}
/*
* If self association, this association is target association
* If self association, this is the target association - Unless we find a pairing association
*/
if (this.isSelfAssociation) {
this.targetAssociation = this
......@@ -67,7 +67,7 @@ module.exports = (function() {
/*
* Else find partner DAOFactory if present, to identify double linked association
*/
else if (this.through) {
if (this.through) {
_.each(this.target.associations, function (association, accessor) {
if (self.source === association.target) {
var paired = false
......@@ -91,7 +91,6 @@ module.exports = (function() {
}
})
}
/*
* If we are double linked, and through is either default or a string, we create the through model and set it on both associations
*/
......@@ -145,24 +144,34 @@ module.exports = (function() {
// is there already a single sided association between the source and the target?
// or is the association on the model itself?
if ((this.isSelfAssociation && Object(this.through) === this.through) || doubleLinked) {
// remove the obsolete association identifier from the source
if (this.isSelfAssociation) {
// We need to remove the keys that 1:M have added
if (this.isSelfAssociation && doubleLinked) {
if (self.through.rawAttributes[this.targetAssociation.identifier]._autoGenerated) {
delete self.through.rawAttributes[this.targetAssociation.identifier];
}
if (self.through.rawAttributes[this.targetAssociation.foreignIdentifier]._autoGenerated) {
delete self.through.rawAttributes[this.targetAssociation.foreignIdentifier];
}
}
this.foreignIdentifier = this.targetAssociation.identifier
this.targetAssociation.foreignIdentifier = this.identifier
if (this.isSelfAssociation && this.foreignIdentifier === this.identifier) {
this.foreignIdentifier = Utils._.camelizeIf(
[(this.options.as || this.target.name), this.target.primaryKeyAttribute].join('_'),
!this.target.options.underscored
)
} else {
this.foreignIdentifier = this.targetAssociation.identifier
this.targetAssociation.foreignIdentifier = this.identifier
[Utils.singularize(this.as, this.source.options.language), this.source.primaryKeyAttribute].join("_"),
!this.source.options.underscored
);
}
if (isForeignKeyDeletionAllowedFor.call(this, this.source, this.foreignIdentifier)) {
delete this.source.rawAttributes[this.foreignIdentifier]
}
if (isForeignKeyDeletionAllowedFor.call(this, this.source, this.foreignIdentifier)) {
delete this.source.rawAttributes[this.foreignIdentifier]
}
if (isForeignKeyDeletionAllowedFor.call(this, this.target, this.identifier)) {
delete this.targetAssociation.source.rawAttributes[this.identifier]
}
if (isForeignKeyDeletionAllowedFor.call(this, this.target, this.identifier)) {
delete this.targetAssociation.source.rawAttributes[this.identifier]
}
// remove any PKs previously defined by sequelize
......@@ -204,7 +213,21 @@ module.exports = (function() {
combinedTableAttributes[this.identifier] = sourceAttribute
combinedTableAttributes[this.foreignIdentifier] = targetAttribute
this.through.rawAttributes = Utils._.merge(this.through.rawAttributes, combinedTableAttributes)
if (!this.through.rawAttributes[this.identifier]) {
this.through.rawAttributes[this.identifier] = {
_autoGenerated: true
}
}
if (!this.through.rawAttributes[this.foreignIdentifier]) {
this.through.rawAttributes[this.foreignIdentifier] = {
_autoGenerated: true
}
}
this.through.rawAttributes[this.identifier] = Utils._.merge(this.through.rawAttributes[this.identifier], sourceAttribute);
this.through.rawAttributes[this.foreignIdentifier] = Utils._.merge(this.through.rawAttributes[this.foreignIdentifier], targetAttribute);
this.through.init(this.through.daoFactoryManager)
} else {
var newAttributes = {}
......@@ -371,19 +394,23 @@ module.exports = (function() {
if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
options.transaction = fieldsOrOptions.transaction
delete fieldsOrOptions.transaction
}
return new Utils.CustomEventEmitter(function(emitter) {
// Create the related model instance
self.target
.create(values, fieldsOrOptions)
.proxy(emitter, { events: ['error', 'sql'] })
.success(function(newAssociatedObject) {
instance[self.accessors.add](newAssociatedObject, options)
.proxy(emitter)
})
}).run()
if (Object(self.through) === self.through) {
return new Utils.CustomEventEmitter(function(emitter) {
// Create the related model instance
self.target
.create(values, fieldsOrOptions)
.proxy(emitter, { events: ['error', 'sql'] })
.success(function(newAssociatedObject) {
instance[self.accessors.add](newAssociatedObject, options)
.proxy(emitter)
})
}).run()
} else {
values[self.identifier] = instance.get(self.source.primaryKeyAttribute);
return self.target.create(values, fieldsOrOptions)
}
}
return this
......
......@@ -126,7 +126,7 @@ module.exports = (function() {
this._timestampAttributes.createdAt = Utils._.underscoredIf(this.options.createdAt, this.options.underscored)
}
if (this.options.updatedAt) {
this._timestampAttributes.updatedAt = Utils._.underscoredIf(this.options.updatedAt, this.options.underscored)
this._timestampAttributes.updatedAt = Utils._.underscoredIf(this.options.updatedAt, this.options.underscored)
}
if (this.options.paranoid && this.options.deletedAt) {
this._timestampAttributes.deletedAt = Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)
......@@ -194,6 +194,7 @@ module.exports = (function() {
this._booleanAttributes = []
this._dateAttributes = []
this._hstoreAttributes = []
this._defaultValues = {}
this.DAO.prototype.validators = {}
......@@ -204,6 +205,9 @@ module.exports = (function() {
if (((definition === DataTypes.DATE) || (definition.type === DataTypes.DATE) || (definition.originalType === DataTypes.DATE))) {
self._dateAttributes.push(name);
}
if (((definition === DataTypes.HSTORE.type) || (definition.type === DataTypes.HSTORE.type) || (definition.originalType === DataTypes.HSTORE.type))) {
self._hstoreAttributes.push(name);
}
if (definition.hasOwnProperty('defaultValue')) {
self._defaultValues[name] = Utils._.partial(
Utils.toDefaultValue, definition.defaultValue)
......@@ -224,6 +228,11 @@ module.exports = (function() {
return self._dateAttributes.indexOf(key) !== -1
})
this._hasHstoreAttributes = !!this._hstoreAttributes.length
this._isHstoreAttribute = Utils._.memoize(function (key) {
return self._hstoreAttributes.indexOf(key) !== -1
})
this.DAO.prototype.Model = this
this._hasDefaultValues = !Utils._.isEmpty(this._defaultValues)
......@@ -453,6 +462,13 @@ module.exports = (function() {
hasJoin = true
validateIncludedElements.call(this, options, tableNames)
if (options.attributes) {
if (options.attributes.indexOf(this.primaryKeyAttribute) === -1) {
options.originalAttributes = options.attributes;
options.attributes = [this.primaryKeyAttribute].concat(options.attributes);
}
}
}
// whereCollection is used for non-primary key updates
......@@ -700,21 +716,22 @@ module.exports = (function() {
}.bind(this))
}
DAOFactory.prototype.create = function(values, fieldsOrOptions) {
DAOFactory.prototype.create = function(values, options) {
Utils.validateParameter(values, Object, { optional: true })
Utils.validateParameter(fieldsOrOptions, Object, { deprecated: Array, optional: true, index: 2, method: 'DAOFactory#create' })
Utils.validateParameter(options, Object, { deprecated: Array, optional: true, index: 2, method: 'DAOFactory#create' })
if (fieldsOrOptions instanceof Array) {
fieldsOrOptions = { fields: fieldsOrOptions }
if (options instanceof Array) {
options = { fields: options }
}
fieldsOrOptions = Utils._.extend({
options = Utils._.extend({
transaction: null
}, fieldsOrOptions || {})
}, options || {})
return this.build(values, {
isNewRecord: true
}).save(fieldsOrOptions)
isNewRecord: true,
attributes: options.fields
}).save(options)
}
DAOFactory.prototype.findOrInitialize = DAOFactory.prototype.findOrBuild = function (params, defaults, options) {
......@@ -1146,7 +1163,7 @@ module.exports = (function() {
return emitter.emit('error', err)
}
query = self.QueryInterface.bulkUpdate(self.getTableName(), attrValueHash, where, options)
query = self.QueryInterface.bulkUpdate(self.getTableName(), attrValueHash, where, options, self.rawAttributes)
query.on('sql', function(sql) {
emitter.emit('sql', sql)
})
......
......@@ -2,7 +2,6 @@ var Utils = require("./utils")
, Mixin = require("./associations/mixin")
, DaoValidator = require("./dao-validator")
, DataTypes = require("./data-types")
, hstore = require('./dialects/postgres/hstore')
, _ = require('lodash')
, defaultsOptions = { raw: true }
......@@ -13,8 +12,6 @@ module.exports = (function() {
this._previousDataValues = {}
this.__options = this.Model.options
this.options = options
this.hasPrimaryKeys = this.Model.options.hasPrimaryKeys
this.__eagerlyLoadedAssociations = []
this.isNewRecord = options.isNewRecord
initValues.call(this, values, options);
......@@ -161,7 +158,9 @@ module.exports = (function() {
}
} else {
options || (options = {})
originalValue = this.dataValues[key]
if (!options.raw) {
originalValue = this.dataValues[key]
}
// If not raw, and there's a customer setter
if (!options.raw && this._customSetters[key]) {
......@@ -169,24 +168,32 @@ module.exports = (function() {
} else {
// Check if we have included models, and if this key matches the include model names/aliases
if (this.options && this.options.include && this.options.includeNames.indexOf(key) !== -1) {
if (this.options && this.options.include && this.options.includeNames.indexOf(key) !== -1 && value) {
// Pass it on to the include handler
this._setInclude(key, value, options)
return
} else {
// If not raw, and attribute is not in model definition, return
if (!options.raw && !this._isAttribute(key)) {
return;
}
// Bunch of stuff we won't do when its raw
if (!options.raw) {
// If attribute is not in model definition, return
if (!this._isAttribute(key)) {
return;
}
// If attempting to set primary key and primary key is already defined, return
if (this.Model._hasPrimaryKeys && originalValue && this.Model._isPrimaryKey(key)) {
return
}
// If attempting to set primary key and primary key is already defined, return
if (this.Model._hasPrimaryKeys && originalValue && this.Model._isPrimaryKey(key)) {
return
}
// If attempting to set read only attributes, return
if (this.Model._hasReadOnlyAttributes && this.Model._isReadOnlyAttribute(key)) {
return
}
// If attempting to set read only attributes, return
if (!options.raw && this.Model._hasReadOnlyAttributes && this.Model._isReadOnlyAttribute(key)) {
return
// Convert date fields to real date objects
if (this.Model._hasDateAttributes && this.Model._isDateAttribute(key) && value !== null && !(value instanceof Date)) {
value = new Date(value)
}
}
// Convert boolean-ish values to booleans
......@@ -194,18 +201,15 @@ module.exports = (function() {
value = !!value
}
// Convert date fields to real date objects
if (this.Model._hasDateAttributes && this.Model._isDateAttribute(key) && value !== null && !(value instanceof Date)) {
value = new Date(value)
}
if (originalValue !== value) {
if (!options.raw && originalValue !== value) {
this._previousDataValues[key] = originalValue
}
this.dataValues[key] = value
}
}
}
return this;
}
DAO.prototype.changed = function(key) {
......@@ -303,9 +307,9 @@ module.exports = (function() {
}
return new Utils.CustomEventEmitter(function(emitter) {
self.hookValidate().error(function (err) {
return emitter.emit('error', err)
}).success(function() {
self.hookValidate({
skip: _.difference(Object.keys(self.rawAttributes), options.fields)
}).proxy(emitter, { events: ['error'] }).success(function() {
options.fields.forEach(function(field) {
if (self.dataValues[field] !== undefined) {
values[field] = self.dataValues[field]
......@@ -315,7 +319,6 @@ module.exports = (function() {
for (var attrName in self.Model.rawAttributes) {
if (self.Model.rawAttributes.hasOwnProperty(attrName)) {
var definition = self.Model.rawAttributes[attrName]
, isHstore = !!definition.type && (definition.type.toString() === DataTypes.HSTORE.toString())
, isEnum = !!definition.type && (definition.type.toString() === DataTypes.ENUM.toString())
, isMySQL = ['mysql', 'mariadb'].indexOf(self.Model.daoFactoryManager.sequelize.options.dialect) !== -1
, ciCollation = !!self.Model.options.collate && self.Model.options.collate.match(/_ci$/i)
......@@ -331,12 +334,6 @@ module.exports = (function() {
values[attrName] = definition.values[scopeIndex]
}
}
if (isHstore) {
if (typeof values[attrName] === "object") {
values[attrName] = hstore.stringify(values[attrName])
}
}
}
}
......@@ -498,7 +495,7 @@ module.exports = (function() {
}
DAO.prototype.setAttributes = function(updates) {
this.set(updates)
return this.set(updates)
}
DAO.prototype.destroy = function(options) {
......@@ -551,6 +548,7 @@ module.exports = (function() {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id }
, updatedAtAttr = this.Model._timestampAttributes.updatedAt
, values = {}
, where
if (countOrOptions === undefined) {
countOrOptions = { by: 1, transaction: null }
......@@ -560,9 +558,12 @@ module.exports = (function() {
countOrOptions = Utils._.extend({
by: 1,
attributes: {}
attributes: {},
where: {}
}, countOrOptions)
where = _.extend(countOrOptions.where, identifier);
if (Utils._.isString(fields)) {
values[fields] = countOrOptions.by
} else if (Utils._.isArray(fields)) {
......@@ -577,7 +578,7 @@ module.exports = (function() {
countOrOptions.attributes[updatedAtAttr] = Utils.now(this.Model.daoFactoryManager.sequelize.options.dialect)
}
return this.QueryInterface.increment(this, this.QueryInterface.QueryGenerator.addSchema(this.Model.tableName, this.Model.options.schema), values, identifier, countOrOptions)
return this.QueryInterface.increment(this, this.QueryInterface.QueryGenerator.addSchema(this.Model.tableName, this.Model.options.schema), values, where, countOrOptions)
}
DAO.prototype.decrement = function (fields, countOrOptions) {
......
......@@ -779,7 +779,7 @@ module.exports = (function() {
joinQueryItem += " AND "+self.getWhereConditions(include.where, self.sequelize.literal(self.quoteIdentifier(as)), include.daoFactory, whereOptions)
// If its a multi association we need to add a where query to the main where (executed in the subquery)
if (subQuery && association.isMultiAssociation) {
if (subQuery && association.isMultiAssociation && include.required) {
if (!options.where) options.where = {}
// Creating the as-is where for the subQuery, checks that the required association exists
......
......@@ -96,7 +96,7 @@ module.exports = (function() {
}
} else if (isCallQuery.call(this)) {
result = data[0]
} else if (isBulkUpateQuery.call(this) || isBulkDeleteQuery.call(this)) {
} else if (isBulkUpdateQuery.call(this) || isBulkDeleteQuery.call(this)) {
result = data.affectedRows
}
......@@ -197,7 +197,7 @@ module.exports = (function() {
return this.options.type === QueryTypes.SELECT
}
var isBulkUpateQuery = function() {
var isBulkUpdateQuery = function() {
return this.options.type === QueryTypes.BULKUPDATE
}
......@@ -245,7 +245,7 @@ module.exports = (function() {
includeNames: this.options.includeNames,
includeMap: this.options.includeMap,
includeValidated: true,
attributes: this.options.attributes,
attributes: this.options.originalAttributes || this.options.attributes,
raw: true
})
} else if (this.options.hasJoinTableModel === true) {
......@@ -338,6 +338,7 @@ module.exports = (function() {
*/
// includeOptions are 'level'-specific where options is a general directive
var i = 0;
var groupJoinData = function(data, includeOptions, options) {
var results = []
, existingResult
......@@ -350,37 +351,41 @@ module.exports = (function() {
})
delete result.__children
},
primaryKeyAttribute
// Identify singular primaryKey attribute for equality check (if possible)
if (includeOptions.daoFactory.primaryKeyAttributes.length === 1) {
primaryKeyAttribute = includeOptions.daoFactory.primaryKeyAttributes[0]
} else if (includeOptions.daoFactory.rawAttributes.id) {
primaryKeyAttribute = 'id'
}
primaryKeyAttribute,
primaryKeyMap = {}
// Ignore all include keys on main data
if (includeOptions.includeNames) {
calleeDataIgnore = calleeDataIgnore.concat(includeOptions.includeNames)
}
if (includeOptions.daoFactory.primaryKeyAttributes.length === 1) {
primaryKeyAttribute = includeOptions.daoFactory.primaryKeyAttribute
}
data.forEach(function (row) {
data.forEach(function parseRow(row) {
row = Dot.transform(row)
calleeData = _.omit(row, calleeDataIgnore)
calleeData = row
// If there are :M associations included we need to see if the main result of the row has already been identified
existingResult = options.checkExisting && _.find(results, function (result) {
// If we can, detect equality on the singular primary key
if (options.checkExisting) {
if (primaryKeyAttribute) {
return result[primaryKeyAttribute] === calleeData[primaryKeyAttribute]
// If we can, detect equality on the singular primary key
existingResult = primaryKeyMap[calleeData[primaryKeyAttribute]]
} else {
// If we can't identify on a singular primary key, do a full row equality check
existingResult = _.find(results, function (result) {
return Utils._.isEqual(_.omit(result, calleeDataIgnore), calleeData)
})
}
// If we can't identify on a singular primary key, do a full row equality check
return Utils._.isEqual(_.omit(result, calleeDataIgnore), calleeData)
})
} else {
existingResult = null
}
if (!existingResult) {
results.push(existingResult = calleeData)
if (options.checkExisting && primaryKeyAttribute) {
primaryKeyMap[existingResult[primaryKeyAttribute]] = existingResult
}
}
for (var attrName in row) {
......@@ -398,6 +403,9 @@ module.exports = (function() {
}
existingResult.__children[attrName].push(row[attrName])
// Remove from main
delete existingResult[attrName]
}
}
}
......
......@@ -325,6 +325,10 @@ module.exports = (function() {
template += " PRIMARY KEY"
}
if (dataType.comment && Utils._.isString(dataType.comment) && dataType.comment.length) {
template += " COMMENT " + this.escape(dataType.comment)
}
if(dataType.references) {
template += " REFERENCES " + this.quoteTable(dataType.references)
......@@ -344,10 +348,6 @@ module.exports = (function() {
}
if (dataType.comment && Utils._.isString(dataType.comment) && dataType.comment.length) {
template += " COMMENT " + this.escape(dataType.comment)
}
result[name] = template
} else {
result[name] = dataType
......
var _ = require('lodash')
module.exports = {
stringifyPart: function(part) {
switch(typeof part) {
......@@ -30,7 +32,7 @@ module.exports = {
case '[':
return JSON.parse(part)
default:
return part
return _.isFinite(part) ? parseFloat(part) : part
}
},
parse: function(string) {
......
var Utils = require("../../utils")
, hstore = require('./hstore')
, util = require("util")
, DataTypes = require("../../data-types")
, SqlString = require("../../sql-string")
......@@ -273,7 +274,7 @@ module.exports = (function() {
if (serials.indexOf(key) !== -1) {
return attrValueHash[key] || 'DEFAULT';
}
return this.escape(attrValueHash[key])
return this.escape(attrValueHash[key], modelAttributes && modelAttributes[key])
}.bind(this)).join(",") +
")")
}.bind(this))
......@@ -445,7 +446,7 @@ module.exports = (function() {
// get populated on all databases as DEFAULT value
// i.e. mysql requires: DEFAULT CURRENT_TIMESTAMP
template += " DEFAULT <%= defaultValue %>"
replacements.defaultValue = this.escape(dataType.defaultValue)
replacements.defaultValue = this.escape(dataType.defaultValue, dataType)
}
if (dataType.unique === true) {
......@@ -771,6 +772,10 @@ module.exports = (function() {
dataType = dataType.replace(/NOT NULL/, '')
}
if (Utils._.includes(dataType, 'INTEGER') || Utils._.includes(dataType, 'BIGINT')) {
dataType = dataType.replace(/(INTEGER|BIGINT)\s*\(\d+\)/, '$1') // Postgres does not allow length on INTEGER and BIGINT
}
if (dataType.lastIndexOf('BLOB') !== -1 || dataType.lastIndexOf('BINARY') !== -1) {
dataType = 'bytea'
}
......@@ -796,6 +801,21 @@ module.exports = (function() {
}
},
/*
Escape a value (e.g. a string, number or date)
*/
escape: function(value, field) {
if (value instanceof Utils.fn || value instanceof Utils.col || value instanceof Utils.literal || value instanceof Utils.cast) {
return value.toString(this)
} else {
if (field && field.type && field.type.toString() === DataTypes.HSTORE.type && Utils._.isObject(value)) {
value = hstore.stringify(value);
}
return SqlString.escape(value, false, null, this.dialect, field)
}
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
......
......@@ -115,12 +115,12 @@ module.exports = (function() {
} else {
// Postgres will treat tables as case-insensitive, so fix the case
// of the returned values to match attributes
if(this.options.raw === false && this.sequelize.options.quoteIdentifiers === false) {
if (this.options.raw === false && 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) {
if (targetAttr != key) {
row[targetAttr] = row[key]
delete row[key]
}
......@@ -128,27 +128,26 @@ module.exports = (function() {
})
}
// Parse hstore fields if the model has any hstore fields.
// This cannot be done in the 'pg' lib because hstore is a UDT.
if (!!this.callee && !!this.callee._hasHstoreAttributes) {
rows.forEach(function(row) {
Utils._.keys(row).forEach(function(key) {
if (self.callee._isHstoreAttribute(key)) {
row[key] = hstore.parse(row[key])
}
})
})
}
this.emit('success', this.send('handleSelectQuery', rows))
}
} else if (this.send('isShowOrDescribeQuery')) {
this.emit('success', results)
} else if (this.send('isInsertQuery')) {
if(this.callee !== null) { // may happen for bulk inserts
for (var key in rows[0]) {
if (rows[0].hasOwnProperty(key)) {
var record = rows[0][key]
if (!!this.callee.Model && !!this.callee.Model.rawAttributes && !!this.callee.Model.rawAttributes[key] && !!this.callee.Model.rawAttributes[key].type && this.callee.Model.rawAttributes[key].type.toString() === DataTypes.HSTORE.toString()) {
record = hstore.parse(record)
}
this.callee.dataValues[key] = record
}
}
}
this.emit('success', this.callee)
} else if ([QueryTypes.BULKUPDATE, QueryTypes.BULKDELETE].indexOf(this.options.type) !== -1) {
this.emit('success', result.rowCount)
} else if (this.send('isUpdateQuery')) {
if(this.callee !== null) { // may happen for bulk updates
} else if (this.send('isInsertQuery') || this.send('isUpdateQuery')) {
if (this.callee !== null) { // may happen for bulk inserts or bulk updates
for (var key in rows[0]) {
if (rows[0].hasOwnProperty(key)) {
var record = rows[0][key]
......
......@@ -161,5 +161,9 @@ module.exports = (function() {
}).spread(onFulfilled, onRejected)
}
CustomEventEmitter.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
return CustomEventEmitter
})()
......@@ -400,6 +400,11 @@ module.exports = (function() {
defaultValue: data.defaultValue
}
// fix: a not-null column cannot have null as default value
if (data.defaultValue === null && !data.allowNull) {
delete options[attrNameAfter].defaultValue;
}
if (this.sequelize.options.dialect === 'sqlite') {
// sqlite needs some special treatment as it cannot rename a column
SQLiteQueryInterface.renameColumn.call(this, tableName, attrNameBefore, attrNameAfter, emitter, queryAndEmit)
......@@ -494,8 +499,8 @@ module.exports = (function() {
return this.queryAndEmit([sql, dao, options], 'update')
}
QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier, options) {
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier, options)
QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier, options, attributes) {
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier, options, attributes)
return this.queryAndEmit([sql, null, options], 'bulkUpdate')
}
......@@ -624,7 +629,8 @@ module.exports = (function() {
includeMap: options.includeMap,
hasSingleAssociation: options.hasSingleAssociation,
hasMultiAssociation: options.hasMultiAssociation,
attributes: options.attributes
attributes: options.attributes,
originalAttributes: options.originalAttributes
})
return queryAndEmit.call(this, [sql, factory, queryOptions], 'select')
......
......@@ -46,7 +46,7 @@
"lingo": "~0.0.5",
"validator": "~3.2.0",
"moment": "~2.5.0",
"dottie": "0.2.0",
"dottie": "0.2.1",
"toposort-class": "~0.3.0",
"generic-pool": "2.0.4",
"sql": "~0.35.0",
......
......@@ -61,7 +61,7 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo', gender: 'male' }).success(function(user) {
User.create({ username: 'bar', gender: 'female' }).success(function(falsePositiveCheck) {
User.create({ username: 'bar', gender: 'female' }).success(function() {
Task.create({ title: 'task', status: 'inactive' }).success(function(task) {
task.setUserXYZ(user).success(function() {
task.getUserXYZ({where: ['gender = ?', 'female']}).success(function(user) {
......@@ -195,14 +195,14 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
Post.hasOne(Comment);
Comment.belongsTo(Post);
this.sequelize.sync().done(function (err) {
this.sequelize.sync().done(function () {
Post.create({
title: 'Post title',
}).done(function(err, post) {
Comment.create({
text: 'OLD VALUE',
}).done(function(err, comment) {
comment.setPost(post).done(function(err) {
comment.setPost(post).done(function() {
expect(comment.text).to.equal('UPDATED VALUE');
done()
});
......
......@@ -117,7 +117,6 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
describe('hasAll', function() {
beforeEach(function(done) {
var self = this
this.Article = this.sequelize.define('Article', {
'title': DataTypes.STRING
})
......@@ -332,7 +331,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Article.sync({ force: true }).success(function() {
Label.sync({ force: true }).success(function() {
Article.create({ title: 'foo' }).success(function(article) {
article.createLabel({ text: 'bar' }).success(function(label) {
article.createLabel({ text: 'bar' }).success(function() {
Label
.findAll({ where: { ArticleId: article.id }})
......@@ -346,8 +345,37 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
})
it('creates the object with the association directly', function(done) {
var spy = sinon.spy()
var Article = this.sequelize.define('Article', {
'title': DataTypes.STRING
}), Label = this.sequelize.define('Label', {
'text': DataTypes.STRING,
'ArticleId': {
type: DataTypes.INTEGER,
allowNull: false
}
})
Article.hasMany(Label)
Article.sync({ force: true }).success(function() {
Label.sync({ force: true }).success(function() {
Article.create({ title: 'foo' }).success(function(article) {
article.createLabel({ text: 'bar' }).on('sql', spy).complete(function(err, label) {
expect(err).not.to.be.ok
expect(spy.calledOnce).to.be.true
expect(label.ArticleId).to.equal(article.id)
done()
})
})
})
})
})
it('supports transactions', function(done) {
var self = this
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var Article = sequelize.define('Article', { 'title': DataTypes.STRING })
, Label = sequelize.define('Label', { 'text': DataTypes.STRING })
......@@ -358,12 +386,15 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Label.sync({ force: true }).success(function() {
Article.create({ title: 'foo' }).success(function(article) {
sequelize.transaction(function (t) {
article.createLabel({ text: 'bar' }, { transaction: t }).success(function(label) {
Label.findAll({ where: { ArticleId: article.id }}).success(function(labels) {
expect(labels.length).to.equal(0)
Label.findAll({ where: { ArticleId: article.id }}, { transaction: t }).success(function(labels) {
expect(labels.length).to.equal(1)
t.rollback().success(function() { done() })
article.createLabel({ text: 'bar' }, { transaction: t }).success(function() {
Label.findAll().success(function (labels) {
expect(labels.length).to.equal(0);
Label.findAll({ where: { ArticleId: article.id }}).success(function(labels) {
expect(labels.length).to.equal(0)
Label.findAll({ where: { ArticleId: article.id }}, { transaction: t }).success(function(labels) {
expect(labels.length).to.equal(1)
t.rollback().success(function() { done() })
})
})
})
})
......@@ -549,6 +580,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
])
chainer.run().success(function (results, john, task1, task2) {
self.tasks = [task1, task2];
john.setTasks([task1, task2]).done(function(err) {
expect(err).not.to.be.ok
done()
......@@ -607,6 +639,46 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
})
it('supports a where not in', function (done) {
this.User.find({
where: {
username: 'John'
}
}).success(function (john) {
john.getTasks({
where: {
title: {
not: ['Get rich']
}
}
}).success(function (tasks) {
expect(tasks).to.have.length(1)
done()
})
})
});
it('supports a where not in on the primary key', function (done) {
var self = this;
this.User.find({
where: {
username: 'John'
}
}).success(function (john) {
john.getTasks({
where: {
id: {
not: [self.tasks[0].get('id')]
}
}
}).success(function (tasks) {
expect(tasks).to.have.length(1)
done()
})
})
});
it("only gets objects that fulfill options with a formatted value", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks({where: ['active = ?', true]}).success(function (tasks) {
......@@ -624,6 +696,30 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
})
it("get associated objects with an eager load with conditions but not required", function(done) {
var Label = this.sequelize.define('Label', { 'title': DataTypes.STRING, 'isActive': DataTypes.BOOLEAN })
, Task = this.Task
, User = this.User
Task.hasMany(Label)
Label.sync({ force: true }).success(function() {
User.find({
where: { username: 'John'},
include: [
{ model: Task, required: false, include: [
{ model: Label, required: false, where: { isActive: true } }
]}
]
}).done(function (err, john) {
expect(err).not.to.be.ok
expect(john.tasks).to.have.length(2);
done();
})
})
})
it('should support schemas', function (done) {
var self = this
, AcmeUser = self.sequelize.define('User', {
......@@ -770,7 +866,6 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
it('supports transactions', function(done) {
var self = this
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('User', { username: DataTypes.STRING })
, Task = sequelize.define('Task', { title: DataTypes.STRING })
......@@ -830,7 +925,6 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
describe('optimizations using bulk create, destroy and update', function () {
beforeEach(function (done) {
var self = this
this.User = this.sequelize.define('User', { username: DataTypes.STRING }, {timestamps: false})
this.Task = this.sequelize.define('Task', { title: DataTypes.STRING }, {timestamps: false})
......@@ -944,7 +1038,6 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
describe('primary key handling for join table', function () {
beforeEach(function (done) {
var self = this
this.User = this.sequelize.define('User',
{ username: DataTypes.STRING },
{ tableName: 'users'}
......@@ -1215,7 +1308,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
expect(err).not.to.be.ok
Task.findAll().done(function (err, tasks) {
expect(err).not.to.be.ok
worker.setTasks(tasks).done(function (err) {
worker.setTasks(tasks).done(function () {
worker.setTasks(tasks).done(function (err) {
expect(err).not.to.be.ok
done()
......@@ -1246,7 +1339,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
expect(err).not.to.be.ok
Task.findAll().done(function (err, tasks) {
expect(err).not.to.be.ok
worker.setTasks(tasks).done(function (err) {
worker.setTasks(tasks).done(function () {
worker.removeTask(tasks[0]).done(function (err) {
expect(err).not.to.be.ok
......@@ -1364,8 +1457,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
it("correctly identifies its counterpart when through is a string", function (done) {
var self = this
, User = this.sequelize.define('User', {})
var User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
User.hasMany(Group, { as: 'MyGroups', through: 'group_user'})
......@@ -1381,8 +1473,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
it("correctly identifies its counterpart when through is a model", function (done) {
var self = this
, User = this.sequelize.define('User', {})
var User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
, UserGroup = this.sequelize.define('GroupUser', {}, {tableName: 'user_groups'})
......
......@@ -104,7 +104,6 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
it('can set an association with predefined primary keys', function(done) {
var User = this.sequelize.define('UserXYZZ', { userCoolIdTag: { type: Sequelize.INTEGER, primaryKey: true }, username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZZ', { taskOrSomething: { type: Sequelize.INTEGER, primaryKey: true }, title: Sequelize.STRING })
, self = this
User.hasOne(Task, {foreignKey: 'userCoolIdTag'})
......@@ -191,7 +190,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
sequelize.sync({ force: true }).success(function() {
User.create({ username: 'bob' }).success(function(user) {
sequelize.transaction(function(t) {
user.createGroup({ name: 'testgroup' }, { transaction: t }).success(function(group) {
user.createGroup({ name: 'testgroup' }, { transaction: t }).success(function() {
User.all().success(function (users) {
users[0].getGroup().success(function (group) {
expect(group).to.be.null;
......
......@@ -3,6 +3,7 @@ var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, _ = require('lodash')
chai.config.includeStack = true
......@@ -31,4 +32,90 @@ describe(Support.getTestDialectTeaser("Self"), function() {
})
})
})
it('can handle 1:m associations', function (done) {
var Person = this.sequelize.define('Person', { name: DataTypes.STRING });
Person.hasMany(Person, { as: 'Children', foreignKey: 'parent_id'});
expect(Person.rawAttributes.parent_id).to.be.ok
this.sequelize.sync({force: true}).done(function () {
Person.create({ name: 'Mary' }).complete(function(err, mary) {
expect(err).to.not.be.ok
Person.create({ name: 'John' }).complete(function(err, john) {
expect(err).to.not.be.ok
Person.create({ name: 'Chris' }).complete(function(err, chris) {
expect(err).to.not.be.ok
mary.setChildren([john, chris]).done(function (err) {
expect(err).not.to.be.ok
done();
});
});
});
});
});
});
it('can handle n:m associations', function(done) {
var Person = this.sequelize.define('Person', { name: DataTypes.STRING });
var Family = this.sequelize.define('Family', {
preexisting_child: {
type: DataTypes.INTEGER,
primaryKey: true
},
preexisting_parent: {
type: DataTypes.INTEGER,
primaryKey: true
}
}, { timestamps: false });
Person.hasMany(Person, { as: 'Parents', through: Family, foreignKey: 'preexisting_child' });
Person.hasMany(Person, { as: 'Children', through: Family, foreignKey: 'preexisting_parent' });
var foreignIdentifiers = _.map(_.values(Person.associations), 'foreignIdentifier')
var rawAttributes = _.keys(Family.rawAttributes)
expect(foreignIdentifiers.length).to.equal(2)
expect(rawAttributes.length).to.equal(2)
expect(foreignIdentifiers).to.have.members([ 'preexisting_parent', 'preexisting_child' ]);
expect(rawAttributes).to.have.members([ 'preexisting_parent', 'preexisting_child' ]);
this.sequelize.sync({ force: true }).complete(function() {
Person.create({ name: 'Mary' }).complete(function(err, mary) {
expect(err).to.not.be.ok
Person.create({ name: 'John' }).complete(function(err, john) {
expect(err).to.not.be.ok
Person.create({ name: 'Chris' }).complete(function(err, chris) {
expect(err).to.not.be.ok
mary.setParents([john]).done(function (err) {
expect(err).to.not.be.ok
mary.addParent(chris).on('sql', function(sql) {
if (sql.match(/INSERT/)) {
expect(sql).to.have.string('preexisting_child');
expect(sql).to.have.string('preexisting_parent');
}
}).complete(function(err) {
expect(err).to.not.be.ok
john.getChildren().on('sql', function(sql) {
var whereClause = sql.split('WHERE')[1]; // look only in the whereClause
expect(whereClause).to.have.string('preexisting_child');
expect(whereClause).to.have.string('preexisting_parent');
}).complete(function(err, children) {
expect(_.map(children, 'id')).to.have.members([mary.id]);
done()
})
})
}).on('sql', function(sql) {
if (sql.match(/INSERT/)) {
expect(sql).to.have.string('preexisting_child');
expect(sql).to.have.string('preexisting_parent');
}
});
})
})
})
})
})
})
\ No newline at end of file
var path = require('path')
module.exports = {
configFile: path.resolve('config', 'database.json'),
migrationsPath: path.resolve('db', 'migrate')
}
......@@ -1679,6 +1679,25 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done()
})
})
it("works with comments", function (done) {
// Test for a case where the comment was being moved to the end of the table when there was also a reference on the column, see #1521
var Member = this.sequelize.define('Member', {})
, Profile = this.sequelize.define('Profile', {
id: {
type: Sequelize.INTEGER,
primaryKey:true,
references: Member,
referencesKey: 'id',
autoIncrement: false,
comment: 'asdf'
}
})
this.sequelize.sync({ force: true }).success(function () {
done()
})
})
})
describe("syntax sugar", function() {
......
......@@ -6,12 +6,7 @@ var chai = require('chai')
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
, async = require('async')
chai.use(datetime)
chai.config.includeStack = true
......@@ -110,6 +105,28 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
it('allows nesting of Sequelize.or using object notation', function(done) {
this.User.find({
where: Sequelize.and( Sequelize.or({username: {eq: "foo"}}, {username: {eq:"bar"}}),
Sequelize.or({id: {eq: 1}}, {id: {eq:4}}) )
}).on('sql', function(sql) {
var expectation = ({
mysql: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1",
sqlite: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1",
postgres: 'WHERE (("User"."username" = \'foo\' OR "User"."username" = \'bar\') AND ("User"."id" = 1 OR "User"."id" = 4)) LIMIT 1',
mariadb: "WHERE ((`User`.`username` = 'foo' OR `User`.`username` = 'bar') AND (`User`.`id` = 1 OR `User`.`id` = 4)) LIMIT 1"
})[Support.getTestDialect()]
if (!expectation) {
console.log(sql)
throw new Error('Undefined expectation for ' + Support.getTestDialect())
}
expect(sql).to.contain(expectation)
done()
})
})
it('allows nesting of Sequelize.and', function(done) {
this.User.find({
where: Sequelize.or( Sequelize.and("1=1", "2=2"), Sequelize.and("3=3", "4=4") )
......@@ -119,6 +136,28 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
it('allows nesting of Sequelize.and using object notation', function(done) {
this.User.find({
where: Sequelize.or( Sequelize.and({username: {eq: "foo"}}, {username: {eq:"bar"}}),
Sequelize.and({id: {eq: 1}}, {id: {eq:4}}) )
}).on('sql', function(sql) {
var expectation = ({
mysql: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1",
sqlite: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1",
postgres: 'WHERE (("User"."username" = \'foo\' AND "User"."username" = \'bar\') OR ("User"."id" = 1 AND "User"."id" = 4)) LIMIT 1',
mariadb: "WHERE ((`User`.`username` = 'foo' AND `User`.`username` = 'bar') OR (`User`.`id` = 1 AND `User`.`id` = 4)) LIMIT 1"
})[Support.getTestDialect()]
if (!expectation) {
console.log(sql)
throw new Error('Undefined expectation for ' + Support.getTestDialect())
}
expect(sql).to.contain(expectation)
done()
})
})
if (dialect !== 'postgres') {
it('still allows simple arrays lookups', function (done) {
this.User.find({
......
......@@ -6,12 +6,8 @@ var chai = require('chai')
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
, async = require('async')
chai.use(datetime)
chai.config.includeStack = true
......@@ -35,7 +31,6 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
describe('findOrCreate', function () {
it("supports transactions", function(done) {
var self = this
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('user_with_transaction', { username: Sequelize.STRING, data: Sequelize.STRING })
......@@ -180,7 +175,6 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
it('supports transactions', function(done) {
var self = this
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('user_with_transaction', { username: Sequelize.STRING })
......@@ -697,7 +691,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank creates (with timestamps: false)', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.sync().done(function() {
Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok
expect(worker).to.be.ok
......@@ -708,7 +702,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow truly blank creates', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.sync().done(function() {
Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok
expect(worker).to.be.ok
......@@ -717,6 +711,39 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
it('should only set passed fields', function (done) {
var User = this.sequelize.define('User', {
'email': {
type: DataTypes.STRING
},
'name': {
type: DataTypes.STRING
}
})
this.sequelize.sync({force: true}).done(function (err) {
expect(err).not.to.be.ok;
User.create({
name: 'Yolo Bear',
email: 'yolo@bear.com'
}, {
fields: ['name']
}).done(function (err, user) {
expect(err).not.to.be.ok;
expect(user.name).to.be.ok;
expect(user.email).not.to.be.ok;
User.find(user.id).done(function (err, user) {
expect(err).not.to.be.ok;
expect(user.name).to.be.ok;
expect(user.email).not.to.be.ok;
done();
});
});
});
});
describe('enums', function() {
it('correctly restores enum values', function(done) {
var self = this
......@@ -758,7 +785,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
Enum.sync({ force: true }).success(function() {
Enum.create({ state: 'happy' }).success(function(_item) {
Enum.create({ state: 'happy' }).success(function() {
done()
});
});
......@@ -770,7 +797,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
Enum.sync({ force: true }).success(function() {
Enum.create({ state: 'happy' }).success(function(_item) {
Enum.create({ state: 'happy' }).success(function() {
done()
});
});
......@@ -786,7 +813,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
Enum.sync({ force: true }).success(function() {
Enum.create({ state: 'happy' }).success(function(_item) {
Enum.create({ state: 'happy' }).success(function() {
done()
});
});
......@@ -800,14 +827,14 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
Enum.sync({ force: true }).success(function() {
Enum.create({ state: 'happy' }).success(function(_item) {
Enum.create({ state: 'happy' }).success(function() {
done()
});
});
})
})
describe('can safely sync multiple times', function(done) {
describe('can safely sync multiple times', function() {
it('through the factory', function(done) {
var Enum = this.sequelize.define('Enum', {
state: {
......@@ -1062,7 +1089,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank arrays (return immediatly)', function (done) {
var Worker = this.sequelize.define('Worker', {})
Worker.sync().done(function(err) {
Worker.sync().done(function() {
Worker.bulkCreate([]).done(function (err, workers) {
expect(err).not.to.be.ok
expect(workers).to.be.ok
......@@ -1074,7 +1101,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank creates (with timestamps: false)', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.sync().done(function() {
Worker.bulkCreate([{}, {}]).done(function (err, workers) {
expect(err).not.to.be.ok
expect(workers).to.be.ok
......@@ -1085,7 +1112,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow autoincremented attributes to be set', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.sync().done(function() {
Worker.bulkCreate([
{id: 5},
{id: 10}
......
......@@ -5,12 +5,9 @@ var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
, async = require('async')
chai.use(datetime)
......@@ -79,7 +76,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('does not modify the passed arguments', function (done) {
var options = { where: ['specialkey = ?', 'awesome']}
this.UserPrimary.find(options).success(function(user) {
this.UserPrimary.find(options).success(function() {
expect(options).to.deep.equal({ where: ['specialkey = ?', 'awesome']})
done()
})
......@@ -101,7 +98,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
xit('doesn\'t throw an error when entering in a non integer value', function(done) {
it.skip('doesn\'t throw an error when entering in a non integer value', function(done) {
this.User.find('a string value').success(function(user) {
expect(user).to.be.null
done()
......@@ -218,7 +215,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
UserPrimary.create({
id: 'a string based id',
name: 'Johnno'
}).success(function(u) {
}).success(function() {
UserPrimary.find('a string based id').success(function(u2) {
expect(u2.id).to.equal('a string based id')
expect(u2.name).to.equal('Johnno')
......@@ -420,8 +417,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
this.sequelize.sync({ force: true }).success(function() {
User.create({username: 'test_testerson'}).success(function(user) {
Message.create({user_id: user.id, message: 'hi there!'}).success(function(message) {
Message.create({user_id: user.id, message: 'a second message'}).success(function(message) {
Message.create({user_id: user.id, message: 'hi there!'}).success(function() {
Message.create({user_id: user.id, message: 'a second message'}).success(function() {
Message.findAll({
where: {user_id: user.id},
......@@ -546,7 +543,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done()
})
describe('alias', function(done) {
describe('alias', function() {
beforeEach(function(done) {
var self = this
this.Worker.hasOne(this.Task, { as: 'ToDo' })
......@@ -654,9 +651,9 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.Photo.create({ img: 'img.jpg' }).success(function(somePhoto) {
self.PhoneNumber.create({ phone: '000000' }).success(function(somePhone1) {
self.PhoneNumber.create({ phone: '111111' }).success(function(somePhone2) {
someContact.setPhotos([somePhoto]).complete(function (err, data) {
someContact.setPhotos([somePhoto]).complete(function (err) {
expect(err).to.be.null
someContact.setPhoneNumbers([somePhone1, somePhone2]).complete(function (err, data) {
someContact.setPhoneNumbers([somePhone1, somePhone2]).complete(function () {
self.Contact.find({
where: {
name: 'Boris'
......@@ -698,7 +695,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.sequelize.sync({ force: true }).success(function() {
self.User.create({ username: 'someone' }).success(function(someUser) {
self.Group.create({ name: 'people' }).success(function(someGroup) {
someUser.setGroupPKeagerones([someGroup]).complete(function (err, data) {
someUser.setGroupPKeagerones([someGroup]).complete(function (err) {
expect(err).to.be.null
self.User.find({
where: {
......@@ -803,7 +800,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
this.Product.hasMany(this.Tag, {as: 'Tags', through: 'product_tag'})
this.Tag.hasMany(this.Product, {as: 'Products', through: 'product_tag'})
this.sequelize.sync().done(function (err) {
this.sequelize.sync().done(function () {
async.auto({
createProducts: function (callback) {
self.Product.bulkCreate([
......
......@@ -7,7 +7,6 @@ var chai = require('chai')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
......@@ -62,7 +61,6 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
describe('special where conditions/smartWhere object', function() {
beforeEach(function(done) {
var self = this
this.buf = new Buffer(16);
this.buf.fill('\x01');
......@@ -70,7 +68,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
this.User.bulkCreate([
{username: 'boo', intVal: 5, theDate: '2013-01-01 12:00'},
{username: 'boo2', intVal: 10, theDate: '2013-01-10 12:00', binary: this.buf }
]).success(function(user2) {
]).success(function() {
done()
})
})
......@@ -91,7 +89,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
where: {
id: [1, 2, 3]
}
}).success(function(users){
}).success(function(){
done();
});
})
......@@ -1225,7 +1223,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('does not modify the passed arguments', function (done) {
var options = { where: ['username = ?', 'awesome']}
this.User.findAll(options).success(function(user) {
this.User.findAll(options).success(function() {
expect(options).to.deep.equal({ where: ['username = ?', 'awesome']})
done()
})
......
......@@ -5,13 +5,7 @@ var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
, async = require('async')
chai.use(datetime)
chai.config.includeStack = true
......
......@@ -156,7 +156,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
})
// In my opinion this is bad logic, null is different from an empty string
xit("returns false for two empty attributes", function(done) {
it.skip("returns false for two empty attributes", function(done) {
this.User.create({ username: null }).success(function(user) {
user.username = ''
expect(user.isDirty).to.be.false
......@@ -294,6 +294,18 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
})
})
it('supports where conditions', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.increment(['aNumber'], { by: 2, where: { bNumber: 1 } }).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(0)
done()
})
})
})
})
it('with array', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
......@@ -780,6 +792,15 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
})
})
it('only validates fields in passed array', function (done) {
this.User.build({
validateTest: 'cake', // invalid, but not saved
validateCustom: '1'
}).save(['validateCustom']).success(function () {
done()
})
})
it("stores an entry in the database", function(done) {
var username = 'user'
, User = this.User
......
......@@ -548,6 +548,37 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
})
})
it('supports promises with custom validation methods', function(done) {
var self = this
, User = this.sequelize.define('User' + config.rand(), {
name: {
type: Sequelize.STRING,
validate: {
customFn: function(val) {
return User.findAll()
.then(function () {
if (val === "error") {
throw new Error("Invalid username")
}
})
}
}
}
})
User.sync().success(function () {
User.build({ name : "error" }).validate().success(function(error) {
expect(error).to.be.an.instanceOf(self.sequelize.ValidationError)
expect(error.name[0].message).to.equal("Invalid username")
User.build({ name : "no error" }).validate().success(function() {
expect(arguments).to.have.length(0)
done()
})
})
})
})
it('skips other validations if allowNull is true and the value is null', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
age: {
......
......@@ -4,12 +4,7 @@ var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, uuid = require('node-uuid')
, _ = require('lodash')
chai.use(datetime)
chai.config.includeStack = true
......@@ -205,7 +200,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
price: {
type: Sequelize.FLOAT,
get: function() {
return this.dataValues['price'] * 100
return this.dataValues.price * 100
}
}
})
......@@ -224,7 +219,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
}, {
getterMethods: {
price: function() {
return this.dataValues['priceInCents'] / 100
return this.dataValues.priceInCents / 100
}
}
})
......@@ -240,7 +235,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
price: {
type: Sequelize.STRING,
get: function() {
return this.dataValues['price'] * 100
return this.dataValues.price * 100
}
}
}, {
......@@ -283,8 +278,6 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
contact.save().done(function(err, me) {
expect(err).not.to.be.ok
var idToTest = me.id;
expect(me.get('tags')).to.deep.equal(['yes', 'no'])
done();
});
......@@ -298,7 +291,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
name: {type: DataTypes.STRING}
})
User.sync().done(function (err) {
User.sync().done(function () {
User.create({name: 'Jan Meier'}).done(function (err, user) {
expect(err).not.to.be.ok
expect(user.changed('name')).to.be.false
......@@ -328,7 +321,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
name: {type: DataTypes.STRING}
})
User.sync().done(function (err) {
User.sync().done(function () {
var user = User.build({
name: 'Jan Meier'
})
......
......@@ -40,7 +40,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
it('should not throw an error when an empty include is named the same as the foreign key', function (done) {
var section = this.sequelize.define('section', { name: DataTypes.STRING });
var layout = this.sequelize.define('layout', { name: DataTypes.STRING });
section.belongsTo(layout, {
as: layout.name,
foreignKey: layout.name,
......@@ -565,6 +565,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
Group.hasMany(Group, { through: 'groups_outsourcing_companies', as: 'OutsourcingCompanies'});
this.sequelize.sync({force: true}).done(function (err) {
expect(err).not.to.be.ok;
Group.bulkCreate([
{name: 'SoccerMoms'},
{name: 'Coca Cola'},
......
......@@ -5,12 +5,7 @@ var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
, async = require('async')
chai.use(datetime)
......@@ -398,7 +393,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
]
}).done(function (err, users) {
expect(err).not.to.be.ok
users.forEach(function (user, i) {
users.forEach(function (user) {
user.memberships.sort(sortById)
expect(user.memberships.length).to.equal(2)
......@@ -473,9 +468,8 @@ describe(Support.getTestDialectTeaser("Include"), function () {
},
singleChain: function (callback) {
var previousInstance
, previousModel
async.eachSeries(singles, function (model, callback, i) {
async.eachSeries(singles, function (model, callback) {
model.create({}).done(function (err, instance) {
if (previousInstance) {
previousInstance["set"+model.name](instance).done(function () {
......@@ -666,7 +660,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
Product.findAll({
......@@ -716,7 +710,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
userGroups: ['users', 'groups', function (callback, results) {
results.users[2].setGroup(results.groups[1]).done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -762,7 +756,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.add(results.users[1].setGroup(results.groups[0]))
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -809,7 +803,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.add(results.users[1].setGroup(results.groups[0]))
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -874,7 +868,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -944,7 +938,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -1014,7 +1008,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -1066,7 +1060,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.add(results.users[0].setLeaderOf(results.projects[0]))
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -1132,7 +1126,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
Product.findAll({
......@@ -1361,7 +1355,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
chainer.add(results.users[3].setGroup(results.groups[1]))
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -1410,6 +1404,87 @@ describe(Support.getTestDialectTeaser("Include"), function () {
})
})
// Test case by @eshell
it('should be possible not to include the main id in the attributes', function (done) {
var Member = this.sequelize.define('Member', {
id: {
type: Sequelize.BIGINT,
primaryKey: true,
autoIncrement: true
},
email:{
type: Sequelize.STRING,
unique: true,
allowNull: false,
validate:{
isEmail: true,
notNull: true,
notEmpty: true
}
},
password: Sequelize.STRING
});
var Album = this.sequelize.define('Album', {
id: {
type: Sequelize.BIGINT,
primaryKey: true,
autoIncrement: true
},
title: {
type: Sequelize.STRING(25),
allowNull: false
}
});
Album.belongsTo(Member);
Member.hasMany(Album);
this.sequelize.sync({force: true}).done(function (err) {
expect(err).not.to.be.ok;
var members = []
, albums = []
, memberCount = 20
for(var i = 1;i <= memberCount;i++) {
members.push({
id: i,
email: 'email'+i+'@lmu.com',
password: 'testing'+i,
});
albums.push({
title: 'Album'+i,
MemberId: i
});
}
Member.bulkCreate(members).done(function (err) {
expect(err).not.to.be.ok;
Album.bulkCreate(albums).done(function (err) {
expect(err).not.to.be.ok;
Member.findAll({
attributes: ['email'],
include:[
{
model:Album
}
]
}).done(function (err, members) {
expect(err).not.to.be.ok;
expect(members.length).to.equal(20);
members.forEach(function (member) {
expect(member.get('id')).not.to.be.ok;
expect(member.albums.length).to.equal(1);
});
done();
});
});
});
});
});
it('should be possible to use limit and a where on a hasMany with additional includes', function (done) {
var self = this
this.fixtureA(function () {
......
......@@ -5,12 +5,7 @@ var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
, async = require('async')
chai.use(datetime)
......@@ -125,7 +120,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
{name: 'Admin', canInvite: 1, canRemove: 1, canPost: 1},
{name: 'Trustee', canInvite: 1, canRemove: 0, canPost: 1},
{name: 'Member', canInvite: 1, canRemove: 0, canPost: 0}
]).done(function (err) {
]).done(function () {
Rank.findAll().done(callback)
})
},
......@@ -407,7 +402,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
]
}).done(function (err, users) {
expect(err).not.to.be.ok
users.forEach(function (user, i) {
users.forEach(function (user) {
expect(user.memberships).to.be.ok
user.memberships.sort(sortById)
......@@ -485,9 +480,8 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
},
singleChain: function (callback) {
var previousInstance
, previousModel
async.eachSeries(singles, function (model, callback, i) {
async.eachSeries(singles, function (model, callback) {
model.create({}).done(function (err, instance) {
if (previousInstance) {
previousInstance["set"+model.name](instance).done(function () {
......@@ -678,7 +672,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
Product.findAll({
......@@ -728,7 +722,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
userGroups: ['users', 'groups', function (callback, results) {
results.users[2].setGroup(results.groups[1]).done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -774,7 +768,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.add(results.users[1].setGroup(results.groups[0]))
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -821,7 +815,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.add(results.users[1].setGroup(results.groups[0]))
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -885,7 +879,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -955,7 +949,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -1025,7 +1019,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -1077,7 +1071,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.add(results.users[0].setLeaderOf(results.projects[0]))
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -1143,7 +1137,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
Product.findAll({
......@@ -1372,7 +1366,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
chainer.add(results.users[3].setGroup(results.groups[1]))
chainer.run().done(callback)
}]
}, function (err, results) {
}, function (err) {
expect(err).not.to.be.ok
User.findAll({
......@@ -1480,7 +1474,7 @@ describe(Support.getTestDialectTeaser("Includes with schemas"), function () {
})
})
xit('should support including date fields, with the correct timeszone', function (done) {
it.skip('should support including date fields, with the correct timeszone', function (done) {
var User = this.sequelize.define('user', {
dateField: Sequelize.DATE
}, {timestamps: false, schema: "account"})
......
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, sinon = require('sinon')
, DataTypes = require(__dirname + "/../../lib/data-types")
chai.config.includeStack = true
......
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, sinon = require('sinon')
, DataTypes = require(__dirname + "/../../lib/data-types")
......
......@@ -3,7 +3,6 @@ var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
chai.config.includeStack = true
......
......@@ -2,8 +2,6 @@
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, util = require("util")
, _ = require('lodash')
, QueryGenerator = require("../../lib/dialects/mysql/query-generator")
......@@ -545,7 +543,7 @@ if (Support.dialectIsMySQL()) {
_.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() {
tests.forEach(function(test) {
var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments)
var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments)
it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
......
......@@ -13,9 +13,9 @@ if (dialect.match(/^postgres/)) {
this.sequelize.options.quoteIdentifiers = true
this.User = this.sequelize.define('User', {
username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)},
email: { type: DataTypes.ARRAY(DataTypes.TEXT) },
settings: DataTypes.HSTORE,
document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'}
document: { type: DataTypes.HSTORE, defaultValue: { default: 'value' } }
})
this.User.sync({ force: true }).success(function() {
done()
......@@ -254,9 +254,7 @@ if (dialect.match(/^postgres/)) {
})
})
it("should handle hstore correctly", function(done) {
var self = this
it("should save hstore correctly", function(done) {
this.User
.create({ username: 'user', email: ['foo@bar.com'], settings: { created: { test: '"value"' }}})
.success(function(newUser) {
......@@ -273,6 +271,67 @@ if (dialect.match(/^postgres/)) {
})
.error(console.log)
})
it("should update hstore correctly", function(done) {
var self = this
this.User
.create({ username: 'user', email: ['foo@bar.com'], settings: { created: { test: '"value"' }}})
.success(function(newUser) {
// Check to see if the default value for an hstore field works
expect(newUser.document).to.deep.equal({default: 'value'})
expect(newUser.settings).to.deep.equal({ created: { test: '"value"' }})
// Check to see if updating an hstore field works
self.User.update({settings: {should: 'update', to: 'this', first: 'place'}}, newUser.identifiers).success(function() {
newUser.reload().success(function() {
// Postgres always returns keys in alphabetical order (ascending)
expect(newUser.settings).to.deep.equal({first: 'place', should: 'update', to: 'this'})
done()
});
})
})
.error(console.log)
})
it("should read hstore correctly", function(done) {
var self = this
var data = { username: 'user', email: ['foo@bar.com'], settings: { created: { test: '"value"' }}}
this.User
.create(data)
.success(function() {
// Check that the hstore fields are the same when retrieving the user
self.User.find({ where: { username: 'user' }})
.success(function(user) {
expect(user.settings).to.deep.equal(data.settings)
done()
})
})
.error(console.log)
})
it("should read hstore correctly from multiple rows", function(done) {
var self = this
self.User
.create({ username: 'user1', email: ['foo@bar.com'], settings: { created: { test: '"value"' }}})
.then(function() {
return self.User.create({ username: 'user2', email: ['foo2@bar.com'], settings: { updated: { another: '"example"' }}})
})
.then(function() {
// Check that the hstore fields are the same when retrieving the user
return self.User.findAll({ order: 'username' })
})
.then(function(users) {
expect(users[0].settings).to.deep.equal({ created: { test: '"value"' }})
expect(users[1].settings).to.deep.equal({ updated: { another: '"example"' }})
done()
})
.error(console.log)
})
})
describe('[POSTGRES] Unquoted identifiers', function() {
......
......@@ -116,7 +116,7 @@ if (dialect.match(/^postgres/)) {
})
it('should handle multiple keys with different types of values', function(done) {
expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).to.deep.equal({ true: true, false: false, null: null, undefined: null, integer: "1", array: [1,'2'], object: { object: 'value' }})
expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).to.deep.equal({ true: true, false: false, null: null, undefined: null, integer: 1, array: [1,'2'], object: { object: 'value' }})
done()
})
})
......
......@@ -115,6 +115,10 @@ if (dialect.match(/^postgres/)) {
createTableQuery: [
{
arguments: ['myTable', {int: 'INTEGER(1)', bigint: 'BIGINT(12)'}],
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"int\" INTEGER, \"bigint\" BIGINT);",
},
{
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}],
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255));",
},
......@@ -948,7 +952,7 @@ if (dialect.match(/^postgres/)) {
})
tests.forEach(function(test) {
var title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments)
var title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments)
it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
......
......@@ -272,39 +272,40 @@ describe(Support.getTestDialectTeaser("Promise"), function () {
describe('save', function () {
it('should fail a validation upon creating', function(done) {
this.User.create({aNumber: 0, validateTest: 'hello'}).then(null, function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].message).to.equal('Validation isInt failed')
done()
});
this.User.create({aNumber: 0, validateTest: 'hello'})
.catch(function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].message).to.equal('Validation isInt failed')
done()
});
})
it('should fail a validation upon building', function(done) {
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.then(null, function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateCustom).to.be.ok
expect(err.validateCustom).to.be.an("array")
expect(err.validateCustom[0]).to.be.ok
expect(err.validateCustom[0].message).to.equal('Length failed.')
done()
})
.catch(function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateCustom).to.be.ok
expect(err.validateCustom).to.be.an("array")
expect(err.validateCustom[0]).to.be.ok
expect(err.validateCustom[0].message).to.equal('Length failed.')
done()
})
})
it('should fail a validation when updating', function(done) {
this.User.create({aNumber: 0}).then(function (user) {
return user.updateAttributes({validateTest: 'hello'})
}).then(null, function(err) {
}).catch(function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateTest).to.be.ok
expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].message).to.equal('Validation isInt failed')
expect(err.validateTest[0].message).to.equal('Validation isInt failed')
done()
})
})
......@@ -322,7 +323,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () {
expect(user.id).to.equal(1)
expect(arguments.length).to.equal(1)
done()
})
})
})
describe('with spread', function () {
......@@ -334,7 +335,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () {
expect(created).to.equal(false)
expect(arguments.length).to.equal(2)
done()
})
})
})
it('user created', function (done) {
this.User
......@@ -344,7 +345,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () {
expect(created).to.equal(true)
expect(arguments.length).to.equal(2)
done()
})
})
})
it('works for functions with only one return value', function (done) {
this.User
......@@ -353,7 +354,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () {
expect(user.id).to.equal(1)
expect(arguments.length).to.equal(1)
done()
})
})
})
})
})
......
......@@ -174,4 +174,44 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
})
})
})
describe('renameColumn', function() {
it('rename a simple column', function(done) {
var self = this
var Users = self.sequelize.define('_Users', {
username: DataTypes.STRING
}, { freezeTableName: true })
Users.sync({ force: true }).success(function() {
self.queryInterface.renameColumn('_Users', 'username', 'pseudo').complete(done)
})
})
it('rename a column non-null without default value', function(done) {
var self = this
var Users = self.sequelize.define('_Users', {
username: {
type: DataTypes.STRING,
allowNull: false
}
}, { freezeTableName: true })
Users.sync({ force: true }).success(function() {
self.queryInterface.renameColumn('_Users', 'username', 'pseudo').complete(done)
})
})
it('rename a boolean column non-null without default value', function(done) {
var self = this
var Users = self.sequelize.define('_Users', {
active: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
}
}, { freezeTableName: true })
Users.sync({ force: true }).success(function() {
self.queryInterface.renameColumn('_Users', 'active', 'enabled').complete(done)
})
})
})
})
......@@ -67,6 +67,17 @@ if (os.type().toLowerCase().indexOf('windows') === -1) {
})
})(['config', 'migrations'])
it("creates a custom migrations folder", function(done) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init -p ./db/migrate", { cwd: __dirname + '/tmp' }, function() {
exec("ls -ila", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('db')
done()
})
})
})
})
it("creates a config.json file", function(done) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
......@@ -93,6 +104,8 @@ if (os.type().toLowerCase().indexOf('windows') === -1) {
})
})
})
})
})
})(['--init', '-i'])
......@@ -435,6 +448,25 @@ if (os.type().toLowerCase().indexOf('windows') === -1) {
})
})
})(['--url', '-U'])
})
;(function(flags) {
flags.forEach(function(flag) {
describe(flag, function() {
it("using options file instead of cli switches", function(done) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
var _path = path.resolve(__dirname, 'config', 'options.js')
exec("../../bin/sequelize --init " + flag + " " + _path, { cwd: __dirname + '/tmp' }, function(err, stdout) {
exec("ls -ila", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('db')
done()
})
})
})
})
});
})
})(['--options-path', '-o'])
})
}
......@@ -124,7 +124,7 @@ if (dialect === 'sqlite') {
})
})
xit("should make aliased attributes available", function(done) {
it.skip("should make aliased attributes available", function(done) {
this.User.find({ where: { name:'user' }, attributes: ['id', ['name', 'username']] }).success(function(user) {
expect(user.username).to.equal('user')
done()
......
......@@ -473,7 +473,7 @@ if (dialect === 'sqlite') {
_.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() {
tests.forEach(function(test) {
var title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments)
var title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments)
it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!