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

Commit 12572005 by Sascha Depold

Merge branch 'master' of github.com:sequelize/sequelize

2 parents 6a36ca11 ae68052e
......@@ -9,6 +9,7 @@
- [BUG] Fixed empty where conditions in MySQL. [#619](https://github.com/sequelize/sequelize/pull/619). Thanks to terraflubb
- [BUG] Allow overriding of default columns. [#635](https://github.com/sequelize/sequelize/pull/635). Thanks to sevastos
- [BUG] Fix where params for belongsTo [#658](https://github.com/sequelize/sequelize/pull/658). Thanks to mweibel
- [BUG] Default ports are now declared in the connector manager, which means the default port for PG correctly becomes 5432. [#633](https://github.com/sequelize/sequelize/issues/633)
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango
- [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango
- [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
......@@ -20,6 +21,8 @@
- [FEATURE] Added Getters/Setters method for DAO. [#538](https://github.com/sequelize/sequelize/pull/538). Thanks to iamjochem
- [FEATURE] Added model wide validations. [#640](https://github.com/sequelize/sequelize/pull/640). Thanks to tremby
- [FEATURE] `findOrCreate` now returns an additional flag (`created`), that is true if a model was created, and false if it was found [#648](https://github.com/sequelize/sequelize/pull/648). janmeier
- [FEATURE] Field and table comments for MySQL and PG. [#523](https://github.com/sequelize/sequelize/pull/523). MySQL by iamjochen. PG by janmeier
- [FEATURE] BigInts can now be used for autoincrement/serial columns. [#673](https://github.com/sequelize/sequelize/pull/673). thanks to sevastos
# v1.6.0 #
- [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work
......
......@@ -24,7 +24,7 @@ module.exports = (function() {
var newAttributes = {}
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.target.tableName) + "Id", this.source.options.underscored)
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
newAttributes[this.identifier] = { type: this.options.keyType || DataTypes.INTEGER }
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.target, this.source, this.options)
Utils._.defaults(this.source.rawAttributes, newAttributes)
......
......@@ -51,8 +51,9 @@ module.exports = (function() {
// define a new model, which connects the models
var combinedTableAttributes = {}
combinedTableAttributes[this.identifier] = {type:DataTypes.INTEGER, primaryKey: true}
combinedTableAttributes[this.foreignIdentifier] = {type:DataTypes.INTEGER, primaryKey: true}
var keyType = this.options.keyType || DataTypes.INTEGER
combinedTableAttributes[this.identifier] = {type: keyType, primaryKey: true}
combinedTableAttributes[this.foreignIdentifier] = {type: keyType, primaryKey: true}
this.connectorDAO = this.source.daoFactoryManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options)
......@@ -65,7 +66,7 @@ module.exports = (function() {
}
} else {
var newAttributes = {}
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
newAttributes[this.identifier] = { type: this.options.keyType || DataTypes.INTEGER }
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, this.options)
Utils._.defaults(this.target.rawAttributes, newAttributes)
}
......
......@@ -29,7 +29,7 @@ module.exports = (function() {
var newAttributes = {}
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
newAttributes[this.identifier] = { type: this.options.keyType || DataTypes.INTEGER }
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, this.options)
Utils._.defaults(this.target.rawAttributes, newAttributes)
......
......@@ -247,13 +247,13 @@ module.exports = (function() {
this.dataValues[attr] = new Date()
return this.save()
} else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id };
return this.QueryInterface.delete(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory.tableName, this.__factory.options.schema), identifier)
}
}
DAO.prototype.increment = function(fields, count) {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id,
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id },
values = {}
if (count === undefined) count = 1;
......@@ -365,20 +365,22 @@ module.exports = (function() {
}
}
if (this.__options.timestamps && isNewRecord) {
defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = Utils.now()
defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = Utils.now()
if (this.__options.paranoid) {
defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null
}
if (isNewRecord) {
if (this.hasDefaultValues) {
Utils._.each(this.defaultValues, function(valueFn, key) {
if (!defaults.hasOwnProperty(key))
defaults[key] = valueFn()
})
}
if (this.__options.timestamps) {
defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = Utils.now()
defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = Utils.now()
if (this.__options.paranoid) {
defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null
}
}
}
if (Utils._.size(defaults)) {
......
......@@ -12,6 +12,7 @@ module.exports = (function() {
this.sequelize = sequelize
this.client = null
this.config = config || {}
this.config.port = this.config.port || 3306
this.disconnectTimeoutId = null
this.queue = []
this.activeQueue = []
......
......@@ -43,7 +43,7 @@ module.exports = (function() {
charset: null
}, options || {})
var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>) ENGINE=<%= engine %> <%= charset %>"
var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)<%= comment %> ENGINE=<%= engine %> <%= charset %>"
, primaryKeys = []
, foreignKeys = {}
, attrStr = []
......@@ -69,6 +69,7 @@ module.exports = (function() {
var values = {
table: this.quoteIdentifier(tableName),
attributes: attrStr.join(", "),
comment: options.comment && Utils._.isString(options.comment) ? " COMMENT " + this.escape(options.comment) : "",
engine: options.engine,
charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "")
}
......@@ -504,6 +505,10 @@ module.exports = (function() {
}
if (dataType.comment && Utils._.isString(dataType.comment) && dataType.comment.length) {
template += " COMMENT " + Utils.escape(dataType.comment)
}
result[name] = template
} else {
result[name] = dataType
......
......@@ -4,10 +4,11 @@ var Query = require("./query")
module.exports = (function() {
var ConnectorManager = function(sequelize, config) {
this.sequelize = sequelize
this.client = null
this.config = config || {}
this.pooling = (!!this.config.poolCfg && (this.config.poolCfg.maxConnections > 0))
this.pg = this.config.native ? require('pg').native : require('pg')
this.client = null
this.config = config || {}
this.config.port = this.config.port || 5432
this.pooling = (!!this.config.poolCfg && (this.config.poolCfg.maxConnections > 0))
this.pg = this.config.native ? require('pg').native : require('pg')
// set pooling parameters if specified
if (this.pooling) {
......
......@@ -48,10 +48,22 @@ module.exports = (function() {
primaryKeys[tableName] = []
tables[tableName] = {}
var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)"
var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)<%= comments %>"
, comments = ""
, attrStr = []
, i
if (options.comment && Utils._.isString(options.comment)) {
comments += "; COMMENT ON TABLE <%= table %> IS " + this.escape(options.comment)
}
for (var attr in attributes) {
if ((i = attributes[attr].indexOf('COMMENT')) !== -1) {
// Move comment to a seperate query
comments += "; " + attributes[attr].substring(i)
attributes[attr] = attributes[attr].substring(0, i)
}
var dataType = this.pgDataTypeMapping(tableName, attr, attributes[attr])
attrStr.push(this.quoteIdentifier(attr) + " " + dataType)
......@@ -62,7 +74,8 @@ module.exports = (function() {
var values = {
table: this.quoteIdentifiers(tableName),
attributes: attrStr.join(", ")
attributes: attrStr.join(", "),
comments: Utils._.template(comments, { table: this.quoteIdentifiers(tableName)})
}
var pks = primaryKeys[tableName].map(function(pk){
......@@ -539,7 +552,7 @@ module.exports = (function() {
}
if (dataType.autoIncrement) {
template +=" SERIAL"
template += " SERIAL"
}
if (dataType.defaultValue !== undefined) {
......@@ -574,7 +587,13 @@ module.exports = (function() {
template += " ON UPDATE <%= onUpdateAction %>"
replacements.onUpdateAction = dataType.onUpdate.toUpperCase()
}
}
if (dataType.comment && Utils._.isString(dataType.comment)) {
template += " COMMENT ON COLUMN <%= tableName %>.<%= columnName %> IS <%= comment %>"
replacements.columnName = this.quoteIdentifier(name)
replacements.tableName = '<%= table %>' // Hacky, table name will be inserted by create table
replacements.comment = this.escape(dataType.comment)
}
result[name] = Utils._.template(template)(replacements)
......@@ -754,6 +773,7 @@ module.exports = (function() {
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (tables[tableName] && tables[tableName][key]) {
switch (tables[tableName][key]) {
case 'bigserial':
case 'serial':
delete hash[key]
returning.push(key)
......
......@@ -69,7 +69,6 @@ module.exports = (function() {
this.options = Utils._.extend({
dialect: 'mysql',
host: 'localhost',
port: 3306,
protocol: 'tcp',
define: {},
query: {},
......
......@@ -17,8 +17,8 @@
"email": "sky@skytrife.com"
},
{
"name": "Jan Aagaard Meier (Innofluence)",
"email": "jam@innofluence.com"
"name": "Jan Aagaard Meier",
"email": ["janzeh@gmail.com", "jmei@itu.dk"]
}
],
"repository": {
......
......@@ -31,6 +31,13 @@ describe('DAOFactory', function() {
expect(User.attributes).toEqual({username:"VARCHAR(255) NOT NULL",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
})
it("handles extended attributes (comment)", function() {
var User = sequelize.define('User' + config.rand(), {
username: {type: Sequelize.STRING, comment: 'This be\'s a comment'}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) COMMENT 'This be\\'s a comment'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
})
it("handles extended attributes (primaryKey)", function() {
var User = sequelize.define('User' + config.rand(), {
username: {type: Sequelize.STRING, primaryKey: true}
......
......@@ -45,6 +45,10 @@ describe('QueryGenerator', function() {
expectation: {id: 'INTEGER UNIQUE'}
},
{
arguments: [{id: {type: 'INTEGER', comment: "I'm a comment!" }}],
expectation: {id: "INTEGER COMMENT 'I\\'m a comment!'" }
},
{
arguments: [{id: {type: 'INTEGER', references: 'Bar'}}],
expectation: {id: 'INTEGER REFERENCES `Bar` (`id`)'}
},
......@@ -72,6 +76,14 @@ describe('QueryGenerator', function() {
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255)) ENGINE=InnoDB;"
},
{
arguments: ['myTable', {title: "INTEGER COMMENT 'I\\'m a comment!'"}],
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` INTEGER COMMENT 'I\\'m a comment!') ENGINE=InnoDB;",
},
{
arguments: ['myTable', {title: "INTEGER"}, {comment: "I'm a comment!"}],
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` INTEGER) COMMENT 'I\\'m a comment!' ENGINE=InnoDB;",
},
{
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {engine: 'MyISAM'}],
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255)) ENGINE=MyISAM;"
},
......
......@@ -49,6 +49,10 @@ describe('QueryGenerator', function() {
expectation: {id: 'INTEGER UNIQUE'}
},
{
arguments: [{id: {type: 'INTEGER', comment: "I'm a comment!" }}],
expectation: {id: "INTEGER COMMENT ON COLUMN <%= table %>.\"id\" IS 'I''m a comment!'" }
},
{
arguments: [{id: {type: 'INTEGER', references: 'Bar'}}],
expectation: {id: 'INTEGER REFERENCES "Bar" ("id")'}
},
......@@ -104,6 +108,14 @@ describe('QueryGenerator', function() {
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255));",
},
{
arguments: ['myTable', {title: "INTEGER COMMENT ON COLUMN <%= table %>.\"title\" IS 'I''m a comment!'"}],
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" INTEGER ); COMMENT ON COLUMN \"myTable\".\"title\" IS 'I''m a comment!';",
},
{
arguments: ['myTable', {title: "INTEGER"}, {comment: "I'm a comment!"}],
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" INTEGER); COMMENT ON TABLE \"myTable\" IS 'I''m a comment!';",
},
{
arguments: ['mySchema.myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}],
expectation: "CREATE TABLE IF NOT EXISTS \"mySchema\".\"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255));"
},
......
......@@ -179,4 +179,29 @@ describe(Helpers.getTestDialectTeaser("BelongsTo"), function() {
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
Task.belongsTo(User, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
......@@ -455,4 +455,30 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
User.hasMany(Task, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
......@@ -179,4 +179,29 @@ describe(Helpers.getTestDialectTeaser("HasOne"), function() {
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
User.hasOne(Task, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
......@@ -7,6 +7,7 @@ if(typeof require === 'function') {
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
before(function(done) {
......@@ -124,6 +125,21 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
expect(Task.build().flag).toEqual(false)
})
it("fills the objects with default values", function() {
var Task = this.sequelize.define('Task', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
}, { timestamps: false })
expect(Task.build().title).toEqual('a task!')
expect(Task.build().foo).toEqual(2)
expect(Task.build().bar).toEqual(undefined)
expect(Task.build().foobar).toEqual('asd')
expect(Task.build().flag).toEqual(false)
})
it("stores the the passed values in a special variable", function() {
var user = this.User.build({ username: 'John Wayne' })
expect(user.selectedValues).toEqual({ username: 'John Wayne' })
......@@ -399,7 +415,7 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
})
})
it('should only store the values passed in the witelist', function(done) {
it('should only store the values passed in the whitelist', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
......@@ -426,6 +442,34 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
})
})
it('can omitt autoincremental columns', function(done) {
var self = this
, data = { title: 'Iliad' }
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT]
dataTypes.forEach(function(dataType, index) {
var Book = self.sequelize.define('Book'+index, {
id: { type: dataType, primaryKey: true, autoIncrement: true },
title: Sequelize.TEXT
})
Book.sync({ force: true }).success(function() {
Book
.create(data)
.success(function(book) {
expect(book.title).toEqual(data.title)
expect(book.author).toEqual(data.author)
expect(Book.rawAttributes.id.type.toString())
.toEqual(dataTypes[index].toString())
Book.drop()
if (index >= dataTypes.length - 1) {
done()
}
})
})
})
})
it('saves data with single quote', function(done) {
var quote = "single'quote"
, self = this
......
......@@ -6,6 +6,7 @@ if (typeof require === 'function') {
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("DAO"), function() {
before(function(done) {
......
......@@ -6,6 +6,7 @@ if(typeof require === 'function') {
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
describe('validations', function() {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!