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

Commit 4b7c0e94 by Mick Hansen

Update 2.0.0 branch

2 parents 525760c2 8547ea1a
REPORTER ?= dot
REPORTER ?= spec
TESTS = $(shell find ./test/* -name "*.test.js")
DIALECT ?= mysql
......
......@@ -10,6 +10,8 @@ There is a parallel "branch" of the project, released as `2.0.0-alphaX` in NPM.
and will get all the changes of the master. However, `2.0.0` will contain backwards compatibility breaking changes. Check the
changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/2.0.0/changelog.md
##### 2.0.0 API should be considered unstable
### 1.6.0 ###
- We changed the way timestamps are handled. From v1.6.0 on timestamps are stored and loaded as UTC.
......@@ -37,11 +39,11 @@ changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/
## Documentation and Updates ##
You can find the documentation and announcements of updates on the [project's website](http://www.sequelizejs.com).
You can find the documentation and announcements of updates on the [project's website](http://sequelizejs.com).
If you want to know about latest development and releases, follow me on [Twitter](http://twitter.com/sdepold).
Also make sure to take a look at the examples in the repository. The website will contain them soon, as well.
- [Documentation](http://www.sequelizejs.com)
- [Documentation](http://sequelizejs.com)
- [Twitter](http://twitter.com/sdepold)
- [IRC](http://webchat.freenode.net?channels=sequelizejs)
- [Google Groups](https://groups.google.com/forum/#!forum/sequelize)
......@@ -105,7 +107,16 @@ a productive developer, I would recommend the latest v0.8. Also I usually recomm
Once Node.JS is installed on your computer, you will also have access to the lovely
Node Package Manager (NPM).
### 2. Database... Come to me! ###
### 2. Install the dependencies ###
Just "cd" into sequelize directory and run `npm install`, see an example below:
```console
$ cd path/to/sequelize
$ npm install
```
### 3. Database... Come to me! ###
First class citizen of Sequelize was MySQL. Over time, Sequelize began to
become compatible to SQLite and PostgreSQL. In order to provide a fully
......@@ -130,16 +141,32 @@ $ echo "CREATE DATABASE sequelize_test;" | mysql -uroot
**AND ONE LAST THING:** Once `npm install` worked for you (see below), you'll
get SQLite tests for free :)
#### 3a. Docker
If you don't feel like setting up databases and users, you can use our [docker](http://docker.io) [image](https://index.docker.io/u/mhansen/sequelize-contribution/) for sequelize contribution.
Getting the image:
```console
$ sudo docker pull mhansen/sequelize-contribution
```
### 3. Install the dependencies ###
Start the container and save references to container id and ip:
```console
$ CONTAINER=$(sudo docker run -d -i -t mhansen/sequelize-contribution)
$ CONTAINER_IP=$(sudo docker inspect -format='{{.NetworkSettings.IPAddress}}' $CONTAINER)
```
Just "cd" into sequelize directory and run `npm install`, see an example below:
Run tests:
```console
$ SEQ_HOST=$CONTAINER_IP SEQ_USER=sequelize_test make all
```
Stop the container:
```console
$ cd path/to/sequelize
$ npm install
$ sudo docker stop $CONTAINER
```
When running tests repeatedly, you only need to redo step 3 if you haven't stopped the container.
### 4. Run the tests ###
Right now, the test base is split into the `test` folder (which contains the
......
......@@ -22,6 +22,15 @@ var relativeConfigFile = function() {
return path.relative(process.cwd(), configuration.configFile)
}
// Taken from
// http://stackoverflow.com/questions/15375544/how-can-i-robustly-detect-a-relative-path-in-node-js/17521358#17521358
var isRelativePath = function(p) {
var normal = path.normalize(p)
, absolute = path.resolve(p);
return normal != absolute;
}
var writeDefaultConfig = function(config) {
var configPath = path.dirname(configuration.configFile)
......@@ -109,7 +118,11 @@ program
.parse(process.argv)
if(typeof program.config === 'string') {
configuration.configFile = program.config
if (isRelativePath(program.config)) {
configuration.configFile = path.join(process.cwd(), program.config);
} else {
configuration.configFile = program.config
}
}
if(typeof program.env === 'string') {
......
......@@ -3,22 +3,32 @@ var Utils = require("./../utils")
, Helpers = require('./helpers')
module.exports = (function() {
var BelongsTo = function(srcDAO, targetDAO, options) {
this.associationType = 'BelongsTo'
this.source = srcDAO
this.target = targetDAO
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
var BelongsTo = function(source, target, options) {
this.associationType = 'BelongsTo'
this.source = source
this.target = target
this.options = options
this.isSingleAssociation = true
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
if (this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) {
this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as, this.source.options.language) + "Id", this.source.options.underscored)
}
this.options.useHooks = options.useHooks
if (!this.options.as) {
this.options.as = Utils.singularize(this.target.tableName, this.target.options.language)
}
this.associationAccessor = this.isSelfAssociation
? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName)
: this.options.as || this.target.tableName
? Utils.combineTableNames(this.target.tableName, this.options.as)
: this.options.as
this.options.useHooks = options.useHooks
this.accessors = {
get: Utils._.camelize('get_' + this.options.as),
set: Utils._.camelize('set_' + this.options.as)
}
}
// the id is in the source table
......@@ -40,11 +50,10 @@ module.exports = (function() {
BelongsTo.prototype.injectGetter = function(obj) {
var self = this
, accessor = Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language)))
, primaryKeys = Object.keys(self.target.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
obj[accessor] = function(params) {
obj[this.accessors.get] = function(params) {
var id = this[self.identifier]
, where = {}
, options = Utils._.pick(params || {}, 'transaction')
......@@ -69,9 +78,8 @@ module.exports = (function() {
BelongsTo.prototype.injectSetter = function(obj) {
var self = this
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language)))
obj[accessor] = function(associatedObject, options) {
obj[this.accessors.set] = function(associatedObject, options) {
var primaryKeys = !!associatedObject && !!associatedObject.daoFactory ? Object.keys(associatedObject.daoFactory.primaryKeys) : []
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
......
......@@ -4,25 +4,30 @@ var Utils = require("./../utils")
module.exports = (function() {
var HasOne = function(srcDAO, targetDAO, options) {
this.associationType = 'HasOne'
this.source = srcDAO
this.target = targetDAO
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
this.associationType = 'HasOne'
this.source = srcDAO
this.target = targetDAO
this.options = options
this.isSingleAssociation = true
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
if (this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) {
this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as, this.target.options.language) + "Id", this.options.underscored)
}
if (!this.options.as) {
this.options.as = Utils.singularize(this.target.tableName, this.target.options.language)
}
this.associationAccessor = this.isSelfAssociation
? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName)
: this.options.as || this.target.tableName
? Utils.combineTableNames(this.target.tableName, this.options.as)
: this.options.as
this.options.useHooks = options.useHooks
this.accessors = {
get: Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language))),
set: Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language)))
get: Utils._.camelize('get_' + this.options.as),
set: Utils._.camelize('set_' + this.options.as)
}
}
......
......@@ -6,14 +6,14 @@ var Utils = require("./../utils")
/* Defines Mixin for all models. */
var Mixin = module.exports = function(){}
Mixin.hasOne = function(associatedDAO, options) {
Mixin.hasOne = function(associatedDAOFactory, options) {
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options = options || {}
options.hooks = options.hooks === undefined ? false : Boolean(options.hooks)
options.useHooks = options.hooks
// the id is in the foreign table
var association = new HasOne(this, associatedDAO, Utils._.extend((options||{}), this.options))
var association = new HasOne(this, associatedDAOFactory, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
association.injectGetter(this.DAO.prototype);
......@@ -22,14 +22,14 @@ Mixin.hasOne = function(associatedDAO, options) {
return this
}
Mixin.belongsTo = function(associatedDAO, options) {
Mixin.belongsTo = function(associatedDAOFactory, options) {
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options = options || {}
options.hooks = options.hooks === undefined ? false : Boolean(options.hooks)
options.useHooks = options.hooks
// the id is in this table
var association = new BelongsTo(this, associatedDAO, Utils._.extend(options, this.options))
var association = new BelongsTo(this, associatedDAOFactory, Utils._.extend(options, this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
association.injectGetter(this.DAO.prototype)
......@@ -38,14 +38,14 @@ Mixin.belongsTo = function(associatedDAO, options) {
return this
}
Mixin.hasMany = function(associatedDAO, options) {
Mixin.hasMany = function(associatedDAOFactory, options) {
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options = options || {}
options.hooks = options.hooks === undefined ? false : Boolean(options.hooks)
options.useHooks = options.hooks
// the id is in the foreign table or in a connecting table
var association = new HasMany(this, associatedDAO, Utils._.extend((options||{}), this.options))
var association = new HasMany(this, associatedDAOFactory, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
association.injectGetter(this.DAO.prototype)
......
......@@ -168,7 +168,10 @@ module.exports = (function() {
})
this.DAO.prototype.__factory = this
this.DAO.prototype.daoFactory = this
this.DAO.prototype.Model = this
this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues)
this.DAO.prototype.daoFactoryName = this.name
return this
}
......@@ -555,13 +558,23 @@ module.exports = (function() {
DAOFactory.prototype.build = function(values, options) {
options = options || { isNewRecord: true, isDirty: true }
var self = this
, instance = new this.DAO(values, this.options, options.isNewRecord)
if (options.hasOwnProperty('include') && (!options.includeValidated || !options.includeNames)) {
options.includeNames = []
options.include = options.include.map(function(include) {
include = validateIncludedElement.call(this, include)
options.includeNames.push(include.as)
return include
}.bind(this))
}
instance.isNewRecord = options.isNewRecord
instance.daoFactoryName = this.name
instance.daoFactory = this
instance.isDirty = options.isDirty
if (options.includeNames) {
options.includeNames = options.includeNames.concat(options.includeNames.map(function (key) {
return key.slice(0,1).toLowerCase() + key.slice(1)
}))
}
var self = this
, instance = new this.DAO(values, options)
return instance
}
......@@ -843,6 +856,9 @@ module.exports = (function() {
* @return {Object} A promise which fires `success`, `error`, `complete` and `sql`.
*/
DAOFactory.prototype.destroy = function(where, options) {
options = options || {}
options.force = options.force === undefined ? false : Boolean(options.force)
var self = this
, query = null
, args = []
......@@ -855,7 +871,7 @@ module.exports = (function() {
where = newWhere || where
if (self.options.timestamps && self.options.paranoid) {
if (self.options.timestamps && self.options.paranoid && options.force === false) {
var attr = Utils._.underscoredIf(self.options.deletedAt, self.options.underscored)
var attrValueHash = {}
attrValueHash[attr] = Utils.now()
......@@ -1118,18 +1134,20 @@ module.exports = (function() {
options = options || {}
options.where = options.where || {}
var deletedAtCol = Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)
// Don't overwrite our explicit deletedAt search value if we provide one
if (!!options.where[this.options.deletedAt]) {
if (!!options.where[deletedAtCol]) {
return options
}
if (typeof options.where === "string") {
options.where += ' AND ' + this.QueryInterface.quoteIdentifier(this.options.deletedAt) + ' IS NULL '
options.where += ' AND ' + this.QueryInterface.quoteIdentifier(deletedAtCol) + ' IS NULL '
}
else if (Array.isArray(options.where)) {
options.where[0] += ' AND ' + this.QueryInterface.quoteIdentifier(this.options.deletedAt) + ' IS NULL '
options.where[0] += ' AND ' + this.QueryInterface.quoteIdentifier(deletedAtCol) + ' IS NULL '
} else {
options.where[this.options.deletedAt] = null
options.where[deletedAtCol] = null
}
}
......@@ -1215,6 +1233,11 @@ module.exports = (function() {
var usesAlias = (include.as !== include.daoFactory.tableName)
, association = (usesAlias ? this.getAssociationByAlias(include.as) : this.getAssociation(include.daoFactory))
// If single (1:1) association, we singularize the alias, so it will match the automatically generated alias of belongsTo/HasOne
if (association && !usesAlias && association.isSingleAssociation) {
include.as = Utils.singularize(include.daoFactory.tableName, include.daoFactory.options.language)
}
// check if the current daoFactory is actually associated with the passed daoFactory
if (!!association && (!association.options.as || (association.options.as === include.as))) {
include.association = association
......
......@@ -3,16 +3,22 @@ var Utils = require("./utils")
, DaoValidator = require("./dao-validator")
, DataTypes = require("./data-types")
, hstore = require('./dialects/postgres/hstore')
, _ = require('lodash')
module.exports = (function() {
var DAO = function(values, options, isNewRecord) {
var DAO = function(values, options) {
this.dataValues = {}
this.__options = options
this.hasPrimaryKeys = options.hasPrimaryKeys
this.selectedValues = values
this.__options = this.__factory.options
this.options = options
this.hasPrimaryKeys = this.__factory.options.hasPrimaryKeys
// What is selected values even used for?
this.selectedValues = options.include ? _.omit(values, options.includeNames) : values
this.__eagerlyLoadedAssociations = []
this.isNewRecord = options.isNewRecord
initAttributes.call(this, values, isNewRecord)
initAttributes.call(this, values, options)
this.isDirty = options.isDirty
}
Utils._.extend(DAO.prototype, Mixin.prototype)
......@@ -36,17 +42,7 @@ module.exports = (function() {
Object.defineProperty(DAO.prototype, 'values', {
get: function() {
var result = {}
, self = this
this.attributes.concat(this.__eagerlyLoadedAssociations).forEach(function(attr) {
result[attr] = self.dataValues.hasOwnProperty(attr)
? self.dataValues[attr]
: self[attr]
;
})
return result
return this.dataValues
}
})
......@@ -104,7 +100,7 @@ module.exports = (function() {
options = Utils._.extend({}, options, fieldsOrOptions)
var self = this
, values = options.fields ? {} : this.dataValues
, values = options.fields ? {} : (this.options.includeNames ? _.omit(this.dataValues, this.options.includeNames) : this.dataValues)
, updatedAtAttr = Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)
, createdAtAttr = Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)
......@@ -215,7 +211,7 @@ module.exports = (function() {
return emitter.emit('error', err)
}
result.dataValues = newValues
result.dataValues = _.extend(result.dataValues, newValues)
emitter.emit('success', result)
})
})
......@@ -241,7 +237,7 @@ module.exports = (function() {
this.__factory.find({
where: where,
limit: 1,
include: this.__eagerlyLoadedOptions || []
include: this.options.include || []
}, options)
.on('sql', function(sql) { emitter.emit('sql', sql) })
.on('error', function(error) { emitter.emit('error', error) })
......@@ -327,6 +323,9 @@ module.exports = (function() {
}
DAO.prototype.destroy = function(options) {
options = options || {}
options.force = options.force === undefined ? false : Boolean(options.force)
var self = this
, query = null
......@@ -336,7 +335,7 @@ module.exports = (function() {
return emitter.emit('error', err)
}
if (self.__options.timestamps && self.__options.paranoid) {
if (self.__options.timestamps && self.__options.paranoid && options.force === false) {
var attr = Utils._.underscoredIf(self.__options.deletedAt, self.__options.underscored)
self.dataValues[attr] = new Date()
query = self.save(options)
......@@ -469,25 +468,20 @@ module.exports = (function() {
}
DAO.prototype.toJSON = function() {
return this.values;
return this.dataValues;
}
// private
var initAttributes = function(values, isNewRecord) {
var initAttributes = function(values, options) {
// set id to null if not passed as value, a newly created dao has no id
var defaults = this.hasPrimaryKeys ? {} : { id: null },
attrs = {},
key;
// add all passed values to the dao and store the attribute names in this.attributes
for (key in values) {
if (values.hasOwnProperty(key)) {
this.addAttribute(key, values[key])
}
}
if (isNewRecord) {
if (options.isNewRecord) {
if (this.hasDefaultValues) {
Utils._.each(this.defaultValues, function(valueFn, key) {
if (!defaults.hasOwnProperty(key)) {
......@@ -532,22 +526,43 @@ module.exports = (function() {
}
for (key in attrs) {
this.addAttribute(key, attrs[key])
}
if (options.include && options.includeNames.indexOf(key) !== -1) {
if (!Array.isArray(attrs[key])) attrs[key] = [attrs[key]];
// this.addAttributes COMPLETELY destroys the structure of our DAO due to __defineGetter__ resetting the object
// so now we have to rebuild for bulkInserts, bulkUpdates, etc.
var rebuild = {}
var include = _.find(options.include, function (include) {
return include.as === key || (include.as.slice(0,1).toLowerCase() + include.as.slice(1)) === key
})
var association = include.association
, self = this
var accessor = Utils._.camelize(key)
// downcase the first char
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
attrs[key].forEach(function(data) {
var daoInstance = include.daoFactory.build(data, { isNewRecord: false, isDirty: false })
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers)
if (association.isSingleAssociation) {
accessor = Utils.singularize(accessor, self.sequelize.language)
self.dataValues[accessor] = isEmpty ? null : daoInstance
self[accessor] = self.dataValues[accessor]
} else {
if (!self.dataValues[accessor]) {
self.dataValues[accessor] = []
self[accessor] = self.dataValues[accessor]
}
// Get the correct map....
Utils._.each(this.attributes, function(key) {
if (this.dataValues.hasOwnProperty(key)) {
rebuild[key] = this.dataValues[key]
if (!isEmpty) {
self.dataValues[accessor].push(daoInstance)
}
}
}.bind(this))
} else {
this.addAttribute(key, attrs[key])
}
}.bind(this))
// This allows for aliases, etc.
this.dataValues = Utils._.extend(rebuild, this.dataValues)
}
}
return DAO
......
......@@ -188,7 +188,7 @@ module.exports = {
FLOAT: FLOAT,
NOW: 'NOW',
BLOB: BLOB,
UUID: 'CHAR(36)',
UUID: 'UUID',
UUIDV1: 'UUIDV1',
UUIDV4: 'UUIDV4',
......
......@@ -388,7 +388,7 @@ module.exports = (function() {
var table = include.daoFactory.tableName
, as = include.as
if (!include.association.connectorDAO) {
if (!(Object(include.association.through) === include.association.through)) {
var primaryKeysLeft = ((include.association.associationType === 'BelongsTo') ? Object.keys(include.association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys))
, tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName)
, attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0])
......@@ -407,7 +407,7 @@ module.exports = (function() {
, identTarget = include.association.foreignIdentifier
, attrTarget = ((!include.association.target.hasPrimaryKeys || primaryKeysTarget.length !== 1) ? 'id' : primaryKeysTarget[0])
var tableJunction = include.association.connectorDAO.tableName
var tableJunction = include.association.through.tableName
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(tableJunction) + " ON " + this.quoteIdentifier(tableSource) + "." + this.quoteIdentifier(attrSource) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identSource)
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableTarget) + "." + this.quoteIdentifier(attrTarget) + " = " + this.quoteIdentifier(tableJunction) + "." + this.quoteIdentifier(identTarget)
}
......@@ -635,14 +635,14 @@ module.exports = (function() {
joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName)
joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.identifier)
joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField)
} else if (association.connectorDAO){
joinedTables[association.connectorDAO.tableName] = true;
joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.connectorDAO.tableName)
} else if (Object(association.through) === association.through) {
joinedTables[association.through.tableName] = true;
joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.through.tableName)
joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.source.autoIncrementField)
joins += ' = ' + self.quoteIdentifiers(association.connectorDAO.tableName + '.' + association.identifier)
joins += ' = ' + self.quoteIdentifiers(association.through.tableName + '.' + association.identifier)
joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName)
joins += ' ON ' + self.quoteIdentifiers(association.connectorDAO.tableName + '.' + association.foreignIdentifier)
joins += ' ON ' + self.quoteIdentifiers(association.through.tableName + '.' + association.foreignIdentifier)
joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField)
} else {
joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName)
......@@ -687,7 +687,7 @@ module.exports = (function() {
if (Array.isArray(value)) {
result.push(this.arrayValue(value, key, _key, dao))
} else if ((value) && (typeof value == 'object') && !(value instanceof Date)) {
} else if ((value) && (typeof value == 'object') && !(value instanceof Date) && !Buffer.isBuffer(value)) {
if (!!value.join) {
//using as sentinel for join column => value
_value = this.quoteIdentifiers(value.join)
......
var Utils = require('../../utils')
, CustomEventEmitter = require("../../emitters/custom-event-emitter")
, Dot = require('dottie')
, _ = require('lodash')
module.exports = (function() {
var AbstractQuery = function(database, sequelize, callee, options) {}
......@@ -100,39 +101,6 @@ module.exports = (function() {
}
/**
Shortcut methods (success, ok) for listening for success events.
Params:
- fct: A function that gets executed once the *success* event was triggered.
Result:
The function returns the instance of the query.
*/
AbstractQuery.prototype.success =
AbstractQuery.prototype.ok =
function(fct) {
this.on('success', fct)
return this
}
/**
Shortcut methods (failure, fail, error) for listening for error events.
Params:
- fct: A function that gets executed once the *error* event was triggered.
Result:
The function returns the instance of the query.
*/
AbstractQuery.prototype.failure =
AbstractQuery.prototype.fail =
AbstractQuery.prototype.error =
function(fct) {
this.on('error', fct)
return this
}
/**
* This function is a wrapper for private methods.
*
* @param {String} fctName The name of the private method.
......@@ -167,10 +135,13 @@ module.exports = (function() {
if (!this.options.include) {
return null
}
if (!this.options.includeNames) {
this.options.includeNames = this.options.include.map(function(include) {
return include.as
})
}
var tableNames = this.options.include.map(function(include) {
return include.as
}).filter(function(include) {
var tableNames = this.options.includeNames.filter(function(include) {
return attribute.indexOf(include + '.') === 0
})
......@@ -181,20 +152,6 @@ module.exports = (function() {
}
}
var queryResultHasJoin = function(results) {
if (!!results[0]) {
var keys = Object.keys(results[0])
for (var i = 0; i < keys.length; i++) {
if (!!findTableNameInAttribute.call(this, keys[i])) {
return true
}
}
}
return false
}
var isInsertQuery = function(results, metaData) {
var result = true
......@@ -244,6 +201,7 @@ module.exports = (function() {
var handleSelectQuery = function(results) {
var result = null
// Raw queries
if (this.options.raw) {
result = results.map(function(result) {
var o = {}
......@@ -258,8 +216,22 @@ module.exports = (function() {
})
result = result.map(Dot.transform)
// Queries with include
} else if (this.options.hasJoin === true) {
result = transformRowsWithEagerLoadingIntoDaos.call(this, results)
this.options.includeNames = this.options.include.map(function (include) {
return include.as
})
results = groupJoinData.call(this, results)
result = results.map(function(result) {
return this.callee.build(result, {
isNewRecord: false,
isDirty: false,
include:this.options.include,
includeNames: this.options.includeNames,
includeValidated: true
})
}.bind(this))
} else if (this.options.hasJoinTableModel === true) {
result = results.map(function(result) {
result = Dot.transform(result)
......@@ -275,6 +247,8 @@ module.exports = (function() {
return mainDao
}.bind(this))
// Regular queries
} else {
result = results.map(function(result) {
return this.callee.build(result, { isNewRecord: false, isDirty: false })
......@@ -289,75 +263,6 @@ module.exports = (function() {
return result
}
var transformRowsWithEagerLoadingIntoDaos = function(results) {
var result = []
result = prepareJoinData.call(this, results)
result = groupDataByCalleeFactory.call(this, result).map(function(result) {
return transformRowWithEagerLoadingIntoDao.call(this, result)
}.bind(this))
return result
}
var transformRowWithEagerLoadingIntoDao = function(result, dao) {
// let's build the actual dao instance first...
dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false, isDirty: false })
// ... and afterwards the prefetched associations
for (var tableName in result) {
if (result.hasOwnProperty(tableName) && (tableName !== this.callee.tableName)) {
buildAssociatedDaoInstances.call(this, tableName, result[tableName], dao)
}
}
return dao
}
var buildAssociatedDaoInstances = function(tableName, associationData, dao) {
var associatedDaoFactory = this.sequelize.daoFactoryManager.getDAO(tableName, { attribute: 'tableName' })
, association = null
, self = this
if (!!associatedDaoFactory) {
association = this.callee.getAssociation(associatedDaoFactory)
} else {
associatedDaoFactory = this.sequelize.daoFactoryManager.getDAO(Utils.pluralize(tableName, this.sequelize.language), { attribute: 'tableName' })
if (!!associatedDaoFactory) {
association = this.callee.getAssociation(associatedDaoFactory)
} else {
association = this.callee.getAssociationByAlias(tableName)
associatedDaoFactory = association.target
}
}
var accessor = Utils._.camelize(tableName)
// downcase the first char
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
associationData.forEach(function(data) {
var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false, isDirty: false })
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers)
if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) {
accessor = Utils.singularize(accessor, self.sequelize.language)
dao[accessor] = isEmpty ? null : daoInstance
} else {
dao[accessor] = dao[accessor] || []
if (!isEmpty) {
dao[accessor].push(daoInstance)
}
}
// add the accessor to the eagerly loaded associations array
dao.__eagerlyLoadedAssociations = Utils._.uniq(dao.__eagerlyLoadedAssociations.concat([accessor]))
dao.__eagerlyLoadedOptions = (this.options && this.options.include) ? this.options.include : []
}.bind(this))
}
var isShowOrDescribeQuery = function() {
var result = false
......@@ -381,15 +286,18 @@ module.exports = (function() {
the associated data by the callee.
Example:
groupDataByCalleeFactory([
groupJoinData([
{
callee: { some: 'data', id: 1 },
some: 'data',
id: 1,
association: { foo: 'bar', id: 1 }
}, {
callee: { some: 'data', id: 1 },
some: 'data',
id: 1,
association: { foo: 'bar', id: 2 }
}, {
callee: { some: 'data', id: 1 },
some: 'data',
id: 1,
association: { foo: 'bar', id: 3 }
}
])
......@@ -399,7 +307,8 @@ module.exports = (function() {
[
{
callee: { some: 'data', id: 1 },
some: 'data',
id: 1,
association: [
{ foo: 'bar', id: 1 },
{ foo: 'bar', id: 2 },
......@@ -408,67 +317,38 @@ module.exports = (function() {
}
]
*/
var groupDataByCalleeFactory = function(data) {
var result = []
, calleeTableName = this.callee.tableName
data.forEach(function(row) {
var calleeData = row[calleeTableName]
, existingEntry = result.filter(function(groupedRow) {
return Utils._.isEqual(groupedRow[calleeTableName], calleeData)
})[0]
if (!existingEntry) {
existingEntry = {}
result.push(existingEntry)
existingEntry[calleeTableName] = calleeData
var groupJoinData = function(data) {
var self = this
, results = []
, existingResult
, calleeData
data.forEach(function (row) {
row = Dot.transform(row)
calleeData = _.omit(row, self.options.includeNames)
existingResult = _.find(results, function (result) {
return Utils._.isEqual(_.omit(result, self.options.includeNames), calleeData)
})
if (!existingResult) {
results.push(existingResult = calleeData)
}
for (var attrName in row) {
if (row.hasOwnProperty(attrName) && (attrName !== calleeTableName)) {
existingEntry[attrName] = existingEntry[attrName] || []
var attrRowExists = existingEntry[attrName].some(function(attrRow) {
if (row.hasOwnProperty(attrName) && Object(row[attrName]) === row[attrName] && self.options.includeNames.indexOf(attrName) !== -1) {
existingResult[attrName] = existingResult[attrName] || []
var attrRowExists = existingResult[attrName].some(function(attrRow) {
return Utils._.isEqual(attrRow, row[attrName])
})
if (!attrRowExists) {
existingEntry[attrName].push(row[attrName])
existingResult[attrName].push(row[attrName])
}
}
}
}
})
return result
}
/**
* This function will prepare the result of select queries with joins.
*
* @param {Array} data This array contains objects.
* @return {Array} The array will have the needed format for groupDataByCalleeFactory.
*/
var prepareJoinData = function(data) {
var result = data.map(function(row) {
var nestedRow = {}
for (var key in row) {
if (row.hasOwnProperty(key)) {
var tableName = findTableNameInAttribute.call(this, key)
if (!!tableName) {
nestedRow[tableName] = nestedRow[tableName] || {}
nestedRow[tableName][key.replace(tableName + '.', '')] = row[key]
} else {
nestedRow[this.callee.tableName] = nestedRow[this.callee.tableName] || {}
nestedRow[this.callee.tableName][key] = row[key]
}
}
}
return nestedRow
}.bind(this))
return result
return results
}
return AbstractQuery
......
......@@ -88,6 +88,7 @@ module.exports = (function() {
.on('error', function(err) {
errorDetected = true
self.emit('sql', self.sql)
err.sql = sql
self.emit('error', err, self.callee)
})
.on('end', function(info) {
......
......@@ -50,7 +50,7 @@ module.exports = (function() {
for (var attr in attributes) {
if (attributes.hasOwnProperty(attr)) {
var dataType = attributes[attr]
var dataType = this.mysqlDataTypeMapping(tableName, attr, attributes[attr])
if (Utils._.includes(dataType, 'PRIMARY KEY')) {
primaryKeys.push(attr)
......@@ -117,7 +117,7 @@ module.exports = (function() {
attrString.push(Utils._.template('`<%= attrName %>` <%= definition %>')({
attrName: attrName,
definition: definition
definition: this.mysqlDataTypeMapping(tableName, attrName, definition)
}))
}
......@@ -496,6 +496,14 @@ module.exports = (function() {
*/
dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteIdentifier(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';'
},
mysqlDataTypeMapping: function(tableName, attr, dataType) {
if (Utils._.includes(dataType, 'UUID')) {
dataType = dataType.replace(/UUID/, 'CHAR(36) BINARY')
}
return dataType
}
}
......
......@@ -27,6 +27,7 @@ module.exports = (function() {
this.emit('sql', this.sql)
if (err) {
err.sql = sql
this.emit('error', err, this.callee)
} else {
this.emit('success', this.formatResults(results))
......
......@@ -27,6 +27,7 @@ module.exports = (function() {
this.disconnectTimeoutId = null
this.pendingQueries = 0
this.clientDrained = true
this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
process.on('exit', function() {
......@@ -51,6 +52,7 @@ module.exports = (function() {
var self = this
self.pendingQueries++
self.clientDrained = false
return new Utils.CustomEventEmitter(function(emitter) {
self.connect()
......@@ -64,8 +66,6 @@ module.exports = (function() {
.complete(function(err) {
self.endQuery.call(self)
done && done(err) })
.success(function(results) { self.endQuery.call(self) })
.error(function(err) { self.endQuery.call(self) })
.proxy(emitter)
})
}).run()
......@@ -152,6 +152,12 @@ module.exports = (function() {
this.client.connect(function(err, client, done) {
connectCallback(err, client || self.client, done)
})
// Closes a client correctly even if we have backed up queries
// https://github.com/brianc/node-postgres/pull/346
this.client.on('drain', function() {
self.clientDrained = true
})
}
}
......@@ -168,9 +174,9 @@ module.exports = (function() {
}
if (this.client) {
// Closes a client correctly even if we have backed up queries
// https://github.com/brianc/node-postgres/pull/346
this.client.on('drain', this.client.end.bind(this.client))
if (this.clientDrained) {
this.client.end()
}
this.client = null
}
......
......@@ -256,9 +256,12 @@ module.exports = (function() {
},
insertQuery: function(tableName, attrValueHash, attributes) {
var query
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;"
, emptyQuery = "INSERT INTO <%= table %> DEFAULT VALUES RETURNING *;"
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;"
// Remove serials that are null or undefined, which causes an error in PG
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (tables[tableName]) {
......@@ -284,17 +287,32 @@ module.exports = (function() {
, values: rowValues.join(",")
}
query = replacements.attributes.length ? valueQuery : emptyQuery
return Utils._.template(query)(replacements)
},
bulkInsertQuery: function(tableName, attrValueHashes) {
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %> RETURNING *;"
, tuples = []
, serials = []
Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
if (i === 0) {
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (tables[tableName] && tables[tableName][key]) {
if (['bigserial', 'serial'].indexOf(tables[tableName][key]) !== -1) {
serials.push(key)
}
}
})
}
Utils._.forEach(attrValueHashes, function(attrValueHash) {
removeSerialsFromHash(tableName, attrValueHash)
tuples.push("(" +
Utils._.values(attrValueHash).map(function(value){
Utils._.map(attrValueHash, function(value, key){
if (serials.indexOf(key) !== -1) {
return 'DEFAULT';
}
return this.escape(value)
}.bind(this)).join(",") +
")")
......@@ -435,8 +453,8 @@ module.exports = (function() {
return Utils._.compact([
"CREATE", options.indicesType, "INDEX", this.quoteIdentifiers(options.indexName),
(options.indexType ? ('USING ' + options.indexType) : undefined),
"ON", this.quoteIdentifiers(tableName), '(' + transformedAttributes.join(', ') + ')'
"ON", this.quoteIdentifiers(tableName), (options.indexType ? ('USING ' + options.indexType) : undefined),
'(' + transformedAttributes.join(', ') + ')'
]).join(' ')
},
......
......@@ -36,6 +36,7 @@ module.exports = (function() {
query.on('error', function(err) {
receivedError = true
err.sql = sql
self.emit('error', err, self.callee)
})
......
......@@ -156,7 +156,9 @@ module.exports = (function() {
insertQuery: function(tableName, attrValueHash) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);";
var query
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);"
, emptyQuery = "INSERT INTO <%= table %> DEFAULT VALUES;"
var replacements = {
table: this.quoteIdentifier(tableName),
......@@ -166,6 +168,7 @@ module.exports = (function() {
}.bind(this)).join(",")
}
query = replacements.attributes.length ? valueQuery : emptyQuery
return Utils._.template(query)(replacements)
},
......
......@@ -44,6 +44,7 @@ module.exports = (function() {
self.emit('sql', self.sql)
if (err) {
err.sql = self.sql
onFailure.call(self, err)
} else {
this.columnTypes = columnTypes
......
......@@ -28,6 +28,15 @@ module.exports = (function() {
return this
}
/**
Shortcut methods (success, ok) for listening for success events.
Params:
- fct: A function that gets executed once the *success* event was triggered.
Result:
The function returns the instance of the query.
*/
CustomEventEmitter.prototype.success =
CustomEventEmitter.prototype.ok =
function(fct) {
......@@ -35,6 +44,15 @@ module.exports = (function() {
return this
}
/**
Shortcut methods (failure, fail, error) for listening for error events.
Params:
- fct: A function that gets executed once the *error* event was triggered.
Result:
The function returns the instance of the query.
*/
CustomEventEmitter.prototype.failure =
CustomEventEmitter.prototype.fail =
CustomEventEmitter.prototype.error =
......
......@@ -660,6 +660,7 @@ module.exports = (function() {
var sql = this.QueryGenerator.selectQuery(tableName, options, factory)
queryOptions = Utils._.extend({}, queryOptions, { include: options.include })
return queryAndEmit.call(this, [sql, factory, queryOptions], 'select')
}
......
......@@ -79,7 +79,7 @@ module.exports = (function() {
define: {},
query: {},
sync: {},
logging: console.log,
logging: false,
omitNull: false,
queue: true,
native: false,
......
Utils = require('./utils')
var Utils = require('./utils')
var TransactionManager = module.exports = function(sequelize) {
this.sequelize = sequelize
......
var cJSON = require('circular-json')
, _ = require('lodash') // Don't require Utils here, as it creates a circular dependency
var ParameterValidator = module.exports = {
check: function(value, expectation, options) {
options = Utils._.extend({
options = _.extend({
throwError: true,
deprecated: false,
deprecationWarning: generateDeprecationWarning(value, expectation, options),
......
......@@ -64,7 +64,8 @@
"chai-spies": "~0.5.1",
"lcov-result-merger": "0.0.2",
"istanbul": "~0.1.45",
"coveralls": "~2.5.0"
"coveralls": "~2.5.0",
"async": "~0.2.9"
},
"keywords": [
"mysql",
......@@ -84,4 +85,4 @@
"node": ">=0.6.21"
},
"license": "MIT"
}
\ No newline at end of file
}
......@@ -16,7 +16,7 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
Group.belongsTo(User, { foreignKey: 'primaryGroupId', as: 'primaryUsers' })
Group.belongsTo(User, { foreignKey: 'secondaryGroupId', as: 'secondaryUsers' })
expect(Object.keys(Group.associations)).to.deep.equal(['Users', 'primaryUsers', 'secondaryUsers'])
expect(Object.keys(Group.associations)).to.deep.equal(['User', 'primaryUsers', 'secondaryUsers'])
})
})
......
......@@ -676,10 +676,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
{ tableName: 'tasks' }
)
this.User.hasMany(this.Task,
{ joinTableName: 'user_has_tasks' }
)
this.Task.hasMany(this.User)
this.User.hasMany(this.Task, { joinTableName: 'user_has_tasks' })
this.Task.hasMany(this.User, { joinTableName: 'user_has_tasks' })
this.User.sync({ force: true }).success(function() {
self.Task.sync({force: true}).success(function() {
......@@ -693,16 +691,18 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
expect(associationName).not.to.equal(this.User.tableName)
expect(associationName).not.to.equal(this.Task.tableName)
var joinTableName = this.User.associations[associationName].options.joinTableName
if (typeof joinTableName !== 'undefined') {
expect(joinTableName).to.equal(associationName)
var through = this.User.associations[associationName].through
if (typeof through !== 'undefined') {
expect(through.tableName).to.equal(associationName)
}
var tableName = this.User.associations[associationName].options.tableName
if (typeof tableName !== 'undefined') {
expect(tableName).to.equal(associationName)
}
}
done()
setTimeout(function () {
done()
}, 50)
})
})
......@@ -766,8 +766,6 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
describe('inserting in join table', function () {
describe('add', function () {
it('should insert data provided on the object into the join table', function (done) {
var self = this
......@@ -800,6 +798,32 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
})
})
it('should be able to add twice (second call result in UPDATE call) without any attributes (and timestamps off) on the through model', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
, Task = this.sequelize.define('Task', {}, {timestamps: false})
, WorkerTasks = this.sequelize.define('WorkerTasks', {}, {timestamps: false})
Worker.hasMany(Task, { through: WorkerTasks })
Task.hasMany(Worker, { through: WorkerTasks })
this.sequelize.sync().done(function(err) {
expect(err).not.to.be.ok
Worker.create().done(function (err, worker) {
expect(err).not.to.be.ok
Task.create().done(function (err, task) {
expect(err).not.to.be.ok
worker.addTask(task).done(function (err) {
expect(err).not.to.be.ok
worker.addTask(task).done(function (err) {
expect(err).not.to.be.ok
done()
})
})
})
})
})
})
})
describe('set', function () {
......@@ -832,6 +856,68 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
})
})
it('should be able to set twice (second call result in UPDATE calls) without any attributes (and timestamps off) on the through model', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
, Task = this.sequelize.define('Task', {}, {timestamps: false})
, WorkerTasks = this.sequelize.define('WorkerTasks', {}, {timestamps: false})
Worker.hasMany(Task, { through: WorkerTasks })
Task.hasMany(Worker, { through: WorkerTasks })
this.sequelize.sync().done(function(err) {
expect(err).not.to.be.ok
Worker.create().done(function (err, worker) {
expect(err).not.to.be.ok
Task.bulkCreate([{}, {}]).done(function (err) {
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 (err) {
expect(err).not.to.be.ok
done()
})
})
})
})
})
})
})
})
})
describe('removing from the join table', function () {
it('should remove a single entry without any attributes (and timestamps off) on the through model', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
, Task = this.sequelize.define('Task', {}, {timestamps: false})
, WorkerTasks = this.sequelize.define('WorkerTasks', {}, {timestamps: false})
Worker.hasMany(Task, { through: WorkerTasks })
Task.hasMany(Worker, { through: WorkerTasks })
this.sequelize.sync().done(function(err) {
expect(err).not.to.be.ok
Worker.create({}).done(function (err, worker) {
expect(err).not.to.be.ok
Task.bulkCreate([{}, {}]).done(function (err) {
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.removeTask(tasks[0]).done(function (err) {
expect(err).not.to.be.ok
worker.getTasks().done(function (err, tasks) {
expect(tasks.length).to.equal(1)
done()
})
})
})
})
})
})
})
})
})
})
......@@ -900,6 +986,77 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
})
})
describe('alias', function () {
it("creates the join table when through is a string", function (done) {
var self = this
, User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
User.hasMany(Group, { as: 'MyGroups', through: 'group_user'})
Group.hasMany(User, { as: 'MyUsers', through: 'group_user'})
this.sequelize.sync({force:true}).success(function () {
self.sequelize.getQueryInterface().showAllTables().success(function (result) {
expect(result.indexOf('group_user')).not.to.equal(-1)
done()
})
})
})
it("creates the join table when through is a model", function (done) {
var self = this
, User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
, UserGroup = this.sequelize.define('GroupUser', {}, {tableName: 'user_groups'})
User.hasMany(Group, { as: 'MyGroups', through: UserGroup})
Group.hasMany(User, { as: 'MyUsers', through: UserGroup})
this.sequelize.sync({force:true}).success(function () {
self.sequelize.getQueryInterface().showAllTables().success(function (result) {
expect(result.indexOf('user_groups')).not.to.equal(-1)
done()
})
})
})
it("correctly identifies its counterpart when through is a string", function (done) {
var self = this
, User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
User.hasMany(Group, { as: 'MyGroups', through: 'group_user'})
Group.hasMany(User, { as: 'MyUsers', through: 'group_user'})
expect(Group.associations.MyUsers.through === User.associations.MyGroups.through);
expect(Group.associations.MyUsers.through.rawAttributes.UserId).to.exist;
expect(Group.associations.MyUsers.through.rawAttributes.GroupId).to.exist;
setTimeout(function () {
done()
}, 50)
})
it("correctly identifies its counterpart when through is a model", function (done) {
var self = this
, User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
, UserGroup = this.sequelize.define('GroupUser', {}, {tableName: 'user_groups'})
User.hasMany(Group, { as: 'MyGroups', through: UserGroup})
Group.hasMany(User, { as: 'MyUsers', through: UserGroup})
expect(Group.associations.MyUsers.through === User.associations.MyGroups.through);
expect(Group.associations.MyUsers.through.rawAttributes.UserId).to.exist;
expect(Group.associations.MyUsers.through.rawAttributes.GroupId).to.exist;
setTimeout(function () {
done()
}, 50)
})
})
})
describe("Foreign key constraints", function() {
......
......@@ -16,7 +16,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
Group.hasOne(User, { foreignKey: 'primaryGroupId', as: 'primaryUsers' })
Group.hasOne(User, { foreignKey: 'secondaryGroupId', as: 'secondaryUsers' })
expect(Object.keys(Group.associations)).to.deep.equal(['Users', 'primaryUsers', 'secondaryUsers'])
expect(Object.keys(Group.associations)).to.deep.equal(['User', 'primaryUsers', 'secondaryUsers'])
})
})
......
......@@ -944,7 +944,9 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
user.age = user.age + 1 // happy birthday joe
user.save().success(function() {
user.save().done(function(err) {
expect(err).not.to.be.ok
expect(user.username).to.equal('joe')
expect(user.age).to.equal(2)
expect(user.projects).to.exist
......@@ -1078,7 +1080,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
it('returns a response that can be stringified', function(done) {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.stringify(user)).to.deep.equal('{"username":"test.user","age":99,"isAdmin":true,"id":null}')
expect(JSON.stringify(user)).to.deep.equal('{"id":null,"username":"test.user","age":99,"isAdmin":true}')
done()
})
......
......@@ -25,7 +25,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() {
[Sequelize.TEXT, 'TEXT', 'TEXT'],
[Sequelize.DATE, 'DATE', 'DATETIME'],
[Sequelize.NOW, 'NOW', 'NOW'],
[Sequelize.UUID, 'UUID', 'CHAR(36)'],
[Sequelize.UUID, 'UUID', 'UUID'],
[Sequelize.BOOLEAN, 'BOOLEAN', 'TINYINT(1)'],
[Sequelize.BLOB, 'BLOB', 'BLOB'],
......
......@@ -6,12 +6,12 @@ var chai = require('chai')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Language Util"), function() {
before(function(done) {
beforeEach(function(done) {
this.sequelize.options.language = 'es'
done()
})
after(function(done) {
afterEach(function(done) {
this.sequelize.options.language = 'en'
done()
})
......@@ -37,8 +37,8 @@ describe(Support.getTestDialectTeaser("Language Util"), function() {
table2.belongsTo(table)
table3.hasMany(table2)
expect(table.associations.androides.identifier).to.equal('arbolId')
expect(table2.associations.arboles).to.exist
expect(table.associations.androide.identifier).to.equal('arbolId')
expect(table2.associations.arbol).to.exist
expect(table3.associations.androideshombres).to.exist
done()
})
......
......@@ -268,6 +268,11 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', {where: null}],
expectation: "SELECT * FROM `myTable` WHERE 1=1;",
context: QueryGenerator
}, {
title: 'buffer as where argument',
arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field`=X'53657175656c697a65';",
context: QueryGenerator
}
],
......
......@@ -38,12 +38,16 @@ if (dialect.match(/^postgres/)) {
it("should not use a combined name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).not.to.exist
done()
setTimeout(function () {
done()
}, 50)
})
it("should use the specified name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).to.exist
done()
setTimeout(function () {
done()
}, 50)
})
})
})
......
......@@ -333,6 +333,11 @@ if (dialect.match(/^postgres/)) {
}, {
arguments: ['mySchema.myTable', {where: {name: "foo';DROP TABLE mySchema.myTable;"}}],
expectation: "SELECT * FROM \"mySchema\".\"myTable\" WHERE \"mySchema\".\"myTable\".\"name\"='foo'';DROP TABLE mySchema.myTable;';"
}, {
title: 'buffer as where argument',
arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}],
expectation: "SELECT * FROM \"myTable\" WHERE \"myTable\".\"field\"=E'\\\\x53657175656c697a65';",
context: QueryGenerator
},
// Variants when quoteIdentifiers is false
......@@ -410,6 +415,10 @@ if (dialect.match(/^postgres/)) {
insertQuery: [
{
arguments: ['myTable', {}],
expectation: "INSERT INTO \"myTable\" DEFAULT VALUES RETURNING *;"
},
{
arguments: ['myTable', {name: 'foo'}],
expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo') RETURNING *;"
}, {
......
......@@ -255,6 +255,11 @@ if (dialect === 'sqlite') {
arguments: ['myTable', {where: null}],
expectation: "SELECT * FROM `myTable` WHERE 1=1;",
context: QueryGenerator
}, {
title: 'buffer as where argument',
arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field`=X'53657175656c697a65';",
context: QueryGenerator
}
],
......
......@@ -48,6 +48,7 @@ var Support = {
options.pool = options.pool || config.pool
var sequelizeOptions = {
host: options.host || config.host,
logging: options.logging,
dialect: options.dialect,
port: options.port || process.env.SEQ_PORT || config.port,
......@@ -55,10 +56,6 @@ var Support = {
dialectOptions: options.dialectOptions || {}
}
if (!!options.host) {
sequelizeOptions.host = options.host
}
if (!!options.define) {
sequelizeOptions.define = options.define
}
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!