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

Commit b7fa54b6 by Ruben Bridgewater Committed by Mick Hansen

Remove callee and fix a bunch of tests

1 parent feacd0ad
Showing with 416 additions and 403 deletions
......@@ -14,7 +14,13 @@ Sequelize is a promise-based Node.js/io.js ORM for Postgres, MySQL, MariaDB, SQL
From 3.0.0 and up Sequelize will follow SEMVER.
[Upgrading to 2.0](https://github.com/sequelize/sequelize/wiki/Upgrading-to-2.0)
## Backwards compatibility changes
3.0.0 cleans up a lot of deprecated code, making it easier for us to develop and maintain features in the future. This implies that most of the changes in 3.0.0 are breaking changes! Please read the changelog for 3.0.0 carefully.
[Upgrading to 3.0](https://github.com/sequelize/sequelize/wiki/Upgrading-to-3.0).
We highly recommend to use 3.0 as it also includes security related fixes that can't be backported to either 2.0 or 1.7.
If you still use 1.7 please read our guide [Upgrading to 2.0](https://github.com/sequelize/sequelize/wiki/Upgrading-to-2.0) plus the changelog up to now. Version 2.1 also contained new breaking changes.
## Features
......
......@@ -2,22 +2,23 @@
3.0.0 cleans up a lot of deprecated code, making it easier for us to develop and maintain features in the future.
- [ADDED] findById/findByPrimary takes a single value as argument representing the primary key to find.
- [ADDED] findById / findByPrimary takes a single value as argument representing the primary key to find.
- [CHANGED] belongsToMany relations MUST now be given a `through` argument.
- [CHANGED] findOne/findAll/findAndCount/findOrCreate now only takes a single options argument instead of a options, queryOptions argument. So set transaction, raw, etc on the first options argument.
- [CHANGED] findOne / findAll / findAndCount / findOrCreate now only takes a single options argument instead of a options and queryOptions argument. So set transaction, raw, etc on the first options argument.
- [CHANGED] The accessor for belongsToMany relationships is now either the `as` argument or the target model name pluralized.
- [REMOVED] N:M relationships can no longer be represented by 2 x hasMany
- [REMOVED] Model.create/Model.bulkCreate no longer takes an array of fields as its second argument, use `options.fields` instead.
- [REMOVED] Query Chainer has been remved
- [REMOVED] Model.create / Model.bulkCreate / Instance.save no longer takes an array of fields as its second argument, use `options.fields` instead.
- [REMOVED] Query Chainer has been removed
- [REMOVED] Migrations have been removed, use umzug instead
- [REMOVED] Model.findAllJoin has been removed
- [REMOVED] sequelize.query now only takes `sql, callee, options` as arguments, the 4th argument `replacements` has been removed and should be set via `options.replacements` instead.
- [REMOVED] sequelize.query now only takes `sql and options` as arguments, the second and fourth argument `callee` and `replacements` have been removed and should be set via `options.instance` / `options.model` and `options.replacements` instead.
- [REMOVED] `instance.isDirty` has been removed, use `instance.changed()` instead
- [REMOVED] `instance.values` has been removed, use `instance.get()` instead
- [REMOVED] `instance.primaryKeyValues` has been removed.
- [REMOVED] `instance.identifiers` has been removed, use `instance.where()` instead
- [REMOVED] `instance.isDeleted` has been removed, simply check the timestamp with `get('deletedAt')` instead
- [REMOVED] `instance.increment/decrement` now longer takes a number as it's second argument.
- [REMOVED/SECURITY] findOne no longer takes a string/integer/binary argument to represent a primaryKey. Use findById instead
- [REMOVED/SECURITY] findOne no longer takes a string / integer / binary argument to represent a primaryKey. Use findById instead
- [REMOVED/SECURITY] `where: "raw query"` is no longer legal, you must now explicitely use `where: ["raw query", [replacements]]`
- [BUG] Fix showIndexQuery so appropriate indexes are returned when a schema is used
- [BUG] Fix addIndexQuery error when the model has a schema
......@@ -25,6 +26,7 @@
- [BUG] Fix bulkCreate: do not insert NULL for undefined values [3729](https://github.com/sequelize/sequelize/pull/3729)
- [BUG] Fix trying to roll back a comitted transaction if an error occured while comitting resulting in an unhandled rejection [3726](https://github.com/sequelize/sequelize/pull/3726)
- [BUG] Fix regression in beforeUpdate hook where `instance.changed()` would always be false [3727](https://github.com/sequelize/sequelize/pull/3727)
- [BUG] Fix trying to roll back a comitted transaction if an error occured while comitting
#### Backwards compatibility changes
- Most of the changes in 3.0.0 are BC breaking, read the changelog for 3.0.0 carefully.
......
......@@ -10,7 +10,7 @@ A simple example would be a **User** being part of a team **Team** with the fore
```js
var Player = this.sequelize.define('Player', {/* attributes */})
, Team = this.sequelize.define('Team', {/* attributes */});
Player.belongsTo(Team); // Will add a TeamId attribute to Player to hold the primary key value for Team
```
......@@ -23,7 +23,7 @@ The default casing is `camelCase` however if the source model is configured with
```js
var User = this.sequelize.define('User', {/* attributes */})
, Company = this.sequelize.define('Company', {/* attributes */});
User.belongsTo(Company); // Will add CompanyId to user
var User = this.sequelize.define('User', {/* attributes */}, {underscored: true})
......@@ -33,7 +33,7 @@ var User = this.sequelize.define('User', {/* attributes */}, {underscored: true}
primaryKey: true
}
});
User.belongsTo(Company); // Will add company_uuid to user
```
......@@ -42,7 +42,7 @@ In cases where `as` has been defined it will be used in place of the target mode
```js
var User = this.sequelize.define('User', {/* attributes */})
, UserRole = this.sequelize.define('UserRole', {/* attributes */});
User.belongsTo(UserRole, {as: 'Role'}); // Adds RoleId to user rather than UserRoleId
```
......@@ -52,7 +52,7 @@ When the foreign key option is used, Sequelize will use it as-is:
```js
var User = this.sequelize.define('User', {/* attributes */})
, Company = this.sequelize.define('Company', {/* attributes */});
User.belongsTo(Company, {foreignKey: 'fk_company'}); // Adds fk_company to User
```
......@@ -73,9 +73,9 @@ Project.hasOne(User)
Furthermore, Project.prototype will gain the methods getUser and setUser according
to the first parameter passed to define. If you have underscore style
enabled, the added attribute will be project_id instead of ProjectId.
The foreign key will be placed on the users table.
You can also define the foreign key, e.g. if you already have an existing
database and want to work on it:
*/
......@@ -131,7 +131,7 @@ But we want more! Let's define it the other way around by creating a many to man
## Belongs-To-Many associations
Belongs-To-Many associations are used to connect sources with multiple targets. Furthermore the targets can also have connections to multiple sources.
```js
Project.belongsToMany(User, {through: 'UserProject'});
User.belongsToMany(Project, {through: 'UserProject'});
......@@ -144,13 +144,13 @@ Defining `through` is required. Sequelize would previously attempt to autogenera
This will add methods `getUsers`, `setUsers`, `addUsers` to `Project`, and `getProjects`, `setProjects` and `addProject` to `User`.
Sometimes you may want to rename your models when using them in associations. Let's define users as workers and projects as tasks by using the alias (`as`) option:
```js
```js
User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks' })
Project.belongsToMany(User, { as: 'Workers', through: 'worker_tasks' })
```
Of course you can also define self references with belongsToMany:
```js
Person.belongsToMany(Person, { as: 'Children', through: 'PersonChildren' })
// This will create the table PersonChildren which stores the ids of the objects.
......@@ -170,13 +170,13 @@ Project.belongsToMany(User, { through: UserProjects })
```
To add a new project to a user and set it's status, you pass an extra object to the setter, which contains the attributes for the join table
```js
user.addProject(project, { status: 'started' })
```
By default the code above will add ProjectId and UserId to the UserProjects table, and _remove any previously defined primary key attribute_ - the table will be uniquely identified by the combination of the keys of the two tables, and there is no reason to have other PK columns. To enforce a primary key on the `UserProjects` model you can add it manually.
```js
UserProjects = sequelize.define('UserProjects', {
id: {
......@@ -322,8 +322,8 @@ Post.hasMany(Tag, {
Post.getPendingTags();
```
```sql
SELECT `tag`.* INNER JOIN `item_tags` AS `item_tag`
ON `tag`.`id` = `item_tag`.`tagId`
SELECT `tag`.* INNER JOIN `item_tags` AS `item_tag`
ON `tag`.`id` = `item_tag`.`tagId`
AND `item_tag`.`taggable_id` = 42
AND `item_tag`.`taggable` = 'post'
WHERE (`tag`.`status` = 'pending');
......@@ -343,7 +343,7 @@ User.belongsToMany(Project, { as: { singular: 'task', plural: 'tasks' }})
```
If you know that a model will always use the same alias in associations, you can provide it when creating the model
```js
var Project = sequelize.define('project', attributes, {
name: {
......@@ -360,7 +360,7 @@ This will add the functions `add/set/get Tasks` to user instances.
## Associating objects
Because Sequelize is doing a lot of magic, you have to call `Sequelize.sync` after setting the associations! Doing so will allow you the following:
```js
Project.belongsToMany(Task)
Task.belongsToMany(Project)
......@@ -393,7 +393,7 @@ project.getTasks({attributes: ['title']}).then(function(tasks) {
```
To remove created associations you can just call the set method without a specific id:
```js
// remove the association with task1
project.setTasks([task2]).then(function(associatedTasks) {
......@@ -433,7 +433,7 @@ Task#setAuthor(anAuthor)
```
Adding associations to a relation with a custom join table can be done in two ways (continuing with the associations defined in the previous chapter):
```js
// Either by adding a property with the name of the join table model to the object, before creating the association
project.UserProjects = {
......@@ -471,7 +471,7 @@ u.getProjects().then(function(projects) {
```
If you only need some of the attributes from the join table, you can provide an array with the attributes you want:
```js
// This will select only name from the Projects table, and only status from the UserProjects table
user.getProjects({ attributes: ['name'], joinTableAttributes: ['status']})
......@@ -479,7 +479,7 @@ user.getProjects({ attributes: ['name'], joinTableAttributes: ['status']})
## Check associations
You can also check if an object is already associated with another one (N:M only). Here is how you'd do it:
```js
// check if an object is one of associated ones:
Project.create({ /* */ }).then(function(project) {
......@@ -520,26 +520,26 @@ Task.belongsTo(User)
```
Will generate the following SQL:
```sql
CREATE TABLE IF NOT EXISTS `User` (
`id` INTEGER PRIMARY KEY,
`id` INTEGER PRIMARY KEY,
`username` VARCHAR(255)
);
CREATE TABLE IF NOT EXISTS `Task` (
`id` INTEGER PRIMARY KEY,
`title` VARCHAR(255),
`id` INTEGER PRIMARY KEY,
`title` VARCHAR(255),
`user_id` INTEGER REFERENCES `User` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
);
```
The relation between task and user injects the `user_id` foreign key on tasks, and marks it as a reference to the `User` table. By default `user_id` will be set to `NULL` if the referenced user is deleted, and updated if the id of the user id updated. These options can be overridden by passing `onUpdate` and `onDelete` options to the association calls. The validation options are `RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL`.
The relation between task and user injects the `user_id` foreign key on tasks, and marks it as a reference to the `User` table. By default `user_id` will be set to `NULL` if the referenced user is deleted, and updated if the id of the user id updated. These options can be overridden by passing `onUpdate` and `onDelete` options to the association calls. The validation options are `RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL`.
For 1:1 and 1:m associations the default option is `SET NULL` for deletion, and `CASCADE` for updates. For n:m, the default for both is `CASCADE`. This means, that if you delete or update a row from one side of an n:m association, all the rows in the join table referencing that row will also be deleted or updated.
Adding constraints between tables means that tables must be created in the database in a certain order, when using `sequelize.sync`. If Task has a reference to User, the User table must be created before the Task table can be created. This can sometimes lead to circular references, where sequelize cannot find an order in which to sync. Imagine a scenario of documents and versions. A document can have multiple versions, and for convenience, a document has an reference to it's current version.
```js
var Document = this.sequelize.define('Document', {
author: Sequelize.STRING
......@@ -547,29 +547,29 @@ var Document = this.sequelize.define('Document', {
, Version = this.sequelize.define('Version', {
timestamp: Sequelize.DATE
})
Document.hasMany(Version) // This adds document_id to version
Document.belongsTo(Version, { as: 'Current', foreignKey: 'current_version_id'}) // This adds current_version_id to document
```
However, the code above will result in the following error: `Cyclic dependency found. 'Document' is dependent of itself. Dependency Chain: Document -> Version => Document`. In order to alleviate that, we can pass `constraints: false` to one of the associations:
```js
Document.hasMany(Version)
Document.belongsTo(Version, { as: 'Current', foreignKey: 'current_version_id', constraints: false})
```
Which will allow us to sync the tables correctly:
```sql
CREATE TABLE IF NOT EXISTS `Document` (
`id` INTEGER PRIMARY KEY,
`author` VARCHAR(255),
`id` INTEGER PRIMARY KEY,
`author` VARCHAR(255),
`current_version_id` INTEGER
);
CREATE TABLE IF NOT EXISTS `Version` (
`id` INTEGER PRIMARY KEY,
`timestamp` DATETIME,
`id` INTEGER PRIMARY KEY,
`timestamp` DATETIME,
`document_id` INTEGER REFERENCES `Document` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
);
```
......@@ -577,7 +577,7 @@ CREATE TABLE IF NOT EXISTS `Version` (
### Enforcing a foreign key reference without constraints
Some times you may want to reference another table, without adding any constraints, or associations. In that case you can manually add the reference attributes to your schema definition, and mark the relations between them.
```js
var Series, Trainer, Video
 
......
......@@ -80,5 +80,5 @@ var sequelize = new Sequelize('connectionUri', {
var User = sequelize.define('user', {}); // timestamps is false by default
var Post = sequelize.define('user', {}, {
timestamps: true // timestamps will now be true
});
});
```
......@@ -2,32 +2,32 @@ Hooks (also known as callbacks or lifecycle events), are functions which are cal
## Order of Operations
```
(1)
beforeBulkCreate(instances, options, fn)
beforeBulkDestroy(instances, options, fn)
```
(1)
beforeBulkCreate(instances, options, fn)
beforeBulkDestroy(instances, options, fn)
beforeBulkUpdate(instances, options, fn)
(2)
(2)
beforeValidate(instance, options, fn)
(-)
validate
(3)
(3)
afterValidate(instance, options, fn)
(4)
(4)
beforeCreate(instance, options, fn)
beforeDestroy(instance, options, fn)
beforeUpdate(instance, options, fn)
(-)
create
destroy
(-)
create
destroy
update
(5)
afterCreate(instance, options, fn)
(5)
afterCreate(instance, options, fn)
afterDestroy(instance, options, fn)
afterUpdate(instance, options, fn)
(6)
afterBulkCreate(instances, options, fn)
afterBulkDestory(instances, options, fn)
afterUpdate(instance, options, fn)
(6)
afterBulkCreate(instances, options, fn)
afterBulkDestory(instances, options, fn)
afterBulkUpdate(instances, options, fn)
```
......@@ -35,7 +35,7 @@ Hooks (also known as callbacks or lifecycle events), are functions which are cal
There are currently three ways to programmatically add hooks. A hook function always runs asynchronousĺy, and can be resolved either by calling a callback (passed as the last argument),
or by returning a promise.
```js
// Method 1 via the .define() method
var User = sequelize.define('User', {
......@@ -56,7 +56,7 @@ var User = sequelize.define('User', {
}
}
})
// Method 2 via the .hook() method
var User = sequelize.define('User', {
username: DataTypes.STRING,
......@@ -65,16 +65,16 @@ var User = sequelize.define('User', {
values: ['happy', 'sad', 'neutral']
}
})
User.hook('beforeValidate', function(user, options, fn) {
user.mood = 'happy'
fn(null, user)
})
User.hook('afterValidate', function(user, options) {
return sequelize.Promise.reject("I'm afraid I can't let you do that!")
})
// Method 3 via the direct method
var User = sequelize.define('User', {
username: DataTypes.STRING,
......@@ -83,12 +83,12 @@ var User = sequelize.define('User', {
values: ['happy', 'sad', 'neutral']
}
})
User.beforeValidate(function(user, options) {
user.mood = 'happy'
return sequelize.Promise.resolve(user)
})
User.afterValidate(function(user, options, fn) {
user.username = 'Toni'
fn(null, user)
......@@ -115,15 +115,15 @@ User.beforeCreate(function(user) {
})
```
This example will emit an error:
This example will return an error:
```js
User.create({username: 'Not a Boss', accessLevel: 20}).catch(function(err) {
console.log(err) // You can't grant this user an access level above 10!
})
```
The following example would emit a success event:
The following example would return successful:
```js
User.create({username: 'Boss', accessLevel: 20}).then(function(user) {
......@@ -134,18 +134,18 @@ User.create({username: 'Boss', accessLevel: 20}).then(function(user) {
### Model hooks
Sometimes you'll be editing more than one record at a time by utilizing the `bulkCreate, update, destroy` methods on the model. The following will emit whenever you're using one of those methods.
```
beforeBulkCreate / beforeBulkUpdate / beforeBulkDestroy
afterBulkCreate / afterBulkUpdate / afterBulkDestroy
```
If you want to emit hooks for each individual record, along with the bulk hooks you can pass `individualHooks: true` to the call.
```js
Model.destroy({ where: {accessLevel: 0}, individualHooks: true})
Model.destroy({ where: {accessLevel: 0}, individualHooks: true})
// Will select all records that are about to be deleted and emit before- + after- Destroy on each instance
Model.update({username: 'Toni'}, { where: {accessLevel: 0}, individualHooks: true})
// Will select all records that are about to be updated and emit before- + after- Update on each instance
```
......@@ -157,23 +157,23 @@ Model.beforeBulkCreate(function(records, fields, fn) {
// records = the first argument sent to .bulkCreate
// fields = the second argument sent to .bulkCreate
})
Model.bulkCreate([
{username: 'Toni'}, // part of records argument
{username: 'Tobi'} // part of records argument
], ['username'] /* part of fields argument */)
Model.beforeBulkUpdate(function(attributes, where, fn) {
// attributes = first argument sent to Model.update
// where = second argument sent to Model.update
})
Model.update({gender: 'Male'} /*attributes argument*/, { where: {username: 'Tom'}} /*where argument*/)
Model.beforeBulkDestroy(function(whereClause, fn) {
// whereClause = first argument sent to Model.destroy
})
Model.destroy({ where: {username: 'Tom'}} /*whereClause argument*/)
```
......@@ -183,22 +183,22 @@ For the most part hooks will work the same for instances when being associated e
1. When using add/set\[s\] functions the beforeUpdate/afterUpdate hooks will run.
2. The only way to call beforeDestroy/afterDestroy hooks are on associations with `onDelete: 'cascade'` and the option `hooks: true`. For instance:
```js
```js
var Projects = sequelize.define('Projects', {
title: DataTypes.STRING
})
var Tasks = sequelize.define('Tasks', {
title: DataTypes.STRING
})
Projects.hasMany(Tasks, {onDelete: 'cascade', hooks: true})
Tasks.belongsTo(Projects)
```
This code will run beforeDestroy/afterDestroy on the Tasks table. Sequelize, by default, will try to optimize your queries as much as possible.
When calling cascade on delete, Sequelize will simply execute a
When calling cascade on delete, Sequelize will simply execute a
```sql
DELETE FROM `table` WHERE associatedIdentifiier = associatedIdentifier.primaryKey
```
......@@ -231,14 +231,14 @@ User.beforeCreate(function(user, options) {
## A Note About Transactions
Note that many model operations in Sequelize allow you to specify a transaction in the options parameter of the method. If a transaction _is_ specified in the original call, it will be present in the options parameter passed to the hook function. For example, consider the following snippet:
```js
// Here we use the promise-style of async hooks rather than
// the callback.
User.hook('afterCreate', function(user, options) {
// 'transaction' will be available in options.transaction
// This operation will be part of the same transaction as the
// This operation will be part of the same transaction as the
// original User.create call.
return User.update({
mood: 'sad'
......@@ -249,19 +249,18 @@ User.hook('afterCreate', function(user, options) {
transaction: options.transaction
});
});
sequelize.transaction(function(t) {
User.create({
username: 'someguy',
mood: 'happy'
}, {
mood: 'happy',
transaction: t
});
});
```
If we had not included the transaction option in our call to `User.update` in the preceding code, no change would have occurred, since our newly created user does not exist in the database until the pending transaction has been committed.
If we had not included the transaction option in our call to `User.update` in the preceding code, no change would have occurred, since our newly created user does not exist in the database until the pending transaction has been committed.
### Internal Transactions
......
## Building a non-persistent instance
In order to create instances of defined classes just do as follows. You might recognize the syntax if you coded Ruby in the past. Using the `build`-method will return an unsaved object, which you explicitly have to save.
```js
var project = Project.build({
title: 'my awesome project',
......@@ -56,7 +56,7 @@ Task
## Creating persistent instances
Besides constructing objects, that needs an explicit save call to get stored in the database, there is also the possibility to do all those steps with one single command. It's called `create`.
```js
Task.create({ title: 'foo', description: 'bar', deadline: new Date() }).then(function(task) {
// you can now access the newly created task via the variable task
......@@ -64,7 +64,7 @@ Task.create({ title: 'foo', description: 'bar', deadline: new Date() }).then(fun
```
It is also possible to define which attributes can be set via the create method. This can be especially very handy if you create database entries based on a form which can be filled by a user. Using that would for example allow you to restrict the `User` model to set only a username and an address but not an admin flag:
```js
User.create({ username: 'barfooz', isAdmin: true }, [ 'username' ]).then(function(user) {
// let's assume the default of isAdmin is false:
......@@ -77,7 +77,7 @@ User.create({ username: 'barfooz', isAdmin: true }, [ 'username' ]).then(functio
## Updating / Saving / Persisting an instance
Now lets change some values and save changes to the database... There are two ways to do that:
```js
// way 1
task.title = 'a very different title now'
......@@ -90,7 +90,7 @@ task.updateAttributes({
```
It's also possible to define which attributes should be saved when calling `save`, by passing an array of column names. This is useful when you set attributes based on a previously defined object. E.g. if you get the values of an object via a form of a web app. Furthermore this is used internally for `updateAttributes`. This is how it looks like:
```js
task.title = 'foooo'
task.description = 'baaaaaar'
......@@ -119,7 +119,7 @@ Task.create({ title: 'a task' }).then(function(task) {
```
If the `paranoid` options is true, the object will not be deleted, instead the `deletedAt` column will be set to the current timestamp. To force the deletion, you can pass `force: true` to the destroy call:
```js
task.destroy({ force: true })
```
......@@ -135,7 +135,7 @@ In addition to updating a single instance, you can also create, update, and dele
Since you are working with multiple models, the callbacks will not return DAO instances. BulkCreate will return an array of model instances/DAOs, they will however, unlike `create`, not have the resulting values of autoIncrement attributes.`update` and `destroy` will return the number of affected rows.
First lets look at bulkCreate
```js
User.bulkCreate([
{ username: 'barfooz', isAdmin: true },
......@@ -149,7 +149,7 @@ User.bulkCreate([
```
To update several rows at once:
```js
Task.bulkCreate([
{subject: 'programming', status: 'executing'},
......@@ -157,7 +157,7 @@ Task.bulkCreate([
{subject: 'programming', status: 'finished'}
]).then(function() {
Task.update(
{ status: 'inactive' } /* set attributes' value */,
{ status: 'inactive' } /* set attributes' value */,
{ where: { subject: 'programming' }} /* where criteria */
).then(function(affectedRows) {
// affectedRows will be 2
......@@ -169,14 +169,14 @@ Task.bulkCreate([
```
And delete them:
```js
Task.bulkCreate([
{subject: 'programming', status: 'executing'},
{subject: 'reading', status: 'executing'},
{subject: 'programming', status: 'finished'}
]).then(function() {
Task.destroy({
Task.destroy({
where: {
subject: 'programming'
},
......@@ -191,18 +191,18 @@ Task.bulkCreate([
```
If you are accepting values directly from the user, it might be beneficial to limit the columns that you want to actually insert.`bulkCreate()`accepts an options object as the second parameter. The object can have a `fields` parameter, (an array) to let it know which fields you want to build explicitly
```js
User.bulkCreate([
{ username: 'foo' },
{ username: 'bar', admin: true}
], { fields: ['username'] }).then(function() {
// nope bar, you can't be admin!
// nope bar, you can't be admin!
})
```
`bulkCreate` was originally made to be a mainstream/fast way of inserting records, however, sometimes you want the luxury of being able to insert multiple rows at once without sacrificing model validations even when you explicitly tell Sequelize which columns to sift through. You can do by adding a `validate: true` property to the options object.
```js
var Tasks = sequelize.define('Task', {
name: {
......@@ -226,19 +226,19 @@ Tasks.bulkCreate([
], { validate: true }).catch(function(errors) {
/* console.log(errors) would look like:
[
{ record:
...
errors:
{ record:
...
errors:
{ name: 'SequelizeValidationError',
message: 'Validation error',
errors: [Object] } },
{ record:
{ record:
...
errors:
errors:
{ name: 'SequelizeValidationError',
message: 'Validation error',
errors: [Object] } }
]
errors: [Object] } }
]
*/
})
```
......@@ -246,7 +246,7 @@ Tasks.bulkCreate([
## Values of an instance
If you log an instance you will notice, that there is a lot of additional stuff. In order to hide such stuff and reduce it to the very interesting information, you can use the`get`-attribute. Calling it with the option `plain` = true will only return the values of an instance.
```js
Person.create({
name: 'Rambow',
......@@ -289,7 +289,7 @@ Person.findOne({ where: { name: 'john' } }).then(function(person) {
In order to increment values of an instance without running into concurrency issues, you may use `increment`.
First of all you can define a field and the value you want to add to it.
```js
User.findById(1).then(function(user) {
user.increment('my-integer-field', 2).then(/* ... */)
......@@ -297,7 +297,7 @@ User.findById(1).then(function(user) {
```
Second, you can define multiple fields and the value you want to add to them.
```js
User.findById(1).then(function(user) {
user.increment([ 'my-integer-field', 'my-very-other-field' ], 2).then(/* ... */)
......@@ -305,7 +305,7 @@ User.findById(1).then(function(user) {
```
Third, you can define an object containing fields and its increment values.
```js
User.findById(1).then(function(user) {
user.increment({
......@@ -320,7 +320,7 @@ User.findById(1).then(function(user) {
In order to decrement values of an instance without running into concurrency issues, you may use `decrement`.
First of all you can define a field and the value you want to add to it.
```js
User.findById(1).then(function(user) {
user.decrement('my-integer-field', 2).then(/* ... */)
......@@ -328,7 +328,7 @@ User.findById(1).then(function(user) {
```
Second, you can define multiple fields and the value you want to add to them.
```js
User.findById(1).then(function(user) {
user.decrement([ 'my-integer-field', 'my-very-other-field' ], 2).then(/* ... */)
......
......@@ -30,7 +30,7 @@ $ sequelize help:db:migrate
$ sequelize help:db:migrate:undo
# etc
```
The latter one for example will print out the following output:
```bash
......@@ -48,21 +48,19 @@ OPTIONS
--coffee Enables coffee script support. Default: false
--config The path to the config file. Default: config/config.json
```
## Skeleton
The following skeleton shows a typical migration file. All migrations are expected to be located in a folder called `migrations` at the very top of the project. The sequelize binary can generate a migration skeleton. See the aboves section for more details.
```js
module.exports = {
up: function(migration, DataTypes, done) {
up: function(migration, DataTypes) {
// logic for transforming into the new state
done() // sets the migration as finished
},
 
down: function(migration, DataTypes, done) {
down: function(migration, DataTypes) {
// logic for reverting the changes
done() // sets the migration as finished
}
}
```
......@@ -71,17 +69,8 @@ The passed `migration` object can be used to modify the database. The `Da
```js
module.exports = {
up: function(migration, DataTypes, done) {
migration.dropAllTables().complete(done)
 
// equals:
migration.dropAllTables().complete(function(err) {
if (err) {
done(err)
} else {
done(null)
}
})
up: function(migration, DataTypes) {
return migration.dropAllTables();
}
}
```
......@@ -155,7 +144,7 @@ migration.renameTable('Person', 'User')
This method returns the name of all existing tables in the database.
```js
migration.showAllTables().success(function(tableNames) {})
migration.showAllTables().then(function(tableNames) {})
```
### describeTable(tableName, options)
......@@ -163,7 +152,7 @@ migration.showAllTables().success(function(tableNames) {})
This method returns an array of hashes containing information about all attributes in the table.
```js
migration.describeTable('Person').success(function(attributes) {
migration.describeTable('Person').then(function(attributes) {
/*
attributes will be something like:
 
......@@ -299,7 +288,7 @@ Once you have a migrator object, you can run its migration with `migrator.migrat
```js
migrator
.migrate({ method: 'down' })
.success(function() {
.then(function() {
// The migrations have been executed!
})
```
......
......@@ -67,10 +67,19 @@ Sequelize.TEXT // TEXT
Sequelize.INTEGER // INTEGER
Sequelize.BIGINT // BIGINT
Sequelize.BIGINT(11) // BIGINT(11)
Sequelize.FLOAT // FLOAT
Sequelize.FLOAT(11) // FLOAT(11)
Sequelize.FLOAT(11, 12) // FLOAT(11,12)
Sequelize.REAL // REAL PostgreSQL only.
Sequelize.REAL(11) // REAL(11) PostgreSQL only.
Sequelize.REAL(11, 12) // REAL(11,12) PostgreSQL only.
Sequelize.DOUBLE // DOUBLE
Sequelize.DOUBLE(11) // DOUBLE(11)
Sequelize.DOUBLE(11, 12) // DOUBLE(11,12)
Sequelize.DECIMAL // DECIMAL
Sequelize.DECIMAL(10, 2) // DECIMAL(10,2)
......@@ -85,6 +94,7 @@ Sequelize.JSONB // JSONB column. PostgreSQL only.
Sequelize.BLOB // BLOB (bytea for PostgreSQL)
Sequelize.BLOB('tiny') // TINYBLOB (bytea for PostgreSQL. Other options are medium and long)
Sequelize.UUID // UUID datatype for PostgreSQL and SQLite, CHAR(36) BINARY for MySQL (use defaultValue: Sequelize.UUIDV1 or Sequelize.UUIDV4 to make sequelize generate the ids automatically)
```
......@@ -99,7 +109,8 @@ require('pg').types.setTypeParser(1114, function(stringValue) {
});
```
In addition to the type mentioned above, integer, bigint and float also support unsigned and zerofill properties, which can be combined in any order:
In addition to the type mentioned above, integer, bigint, float and double also support unsigned and zerofill properties, which can be combined in any order:
Be aware that this does not apply for PostgreSQL!
```js
Sequelize.INTEGER.UNSIGNED // INTEGER UNSIGNED
......@@ -142,7 +153,7 @@ var Employee = sequelize.define('Employee', {
type : Sequelize.STRING,
allowNull: false,
get : function() {
var title = this.getDataValue('title');
var title = this.getDataValue('title');
// 'this' allows you to access attributes of the instance
return this.getDataValue('name') + ' (' + title + ')';
},
......@@ -180,7 +191,7 @@ var Foo = sequelize.define('Foo', {
},
setterMethods : {
fullName : function(value) {
fullName : function(value) {
var names = value.split(' ');
this.setDataValue('firstname', names.slice(0, -1).join(' '));
......@@ -711,6 +722,7 @@ Project.findAll({
$overlap: [1, 2] // && [1, 2] (PG array overlap operator)
$contains: [1, 2] // @> [1, 2] (PG array contains operator)
$contained: [1, 2] // <@ [1, 2] (PG array contained by operator)
$any: [2,3] // ANY ARRAY[2, 3]::INTEGER (PG only)
},
status: {
$not: false, // status NOT FALSE
......@@ -982,7 +994,7 @@ Task.belongsTo(User)
User.hasMany(Task)
User.hasMany(Tool, { as: 'Instruments' })
sequelize.sync().done(function() {
sequelize.sync().then(function() {
// this is where we continue ...
})
```
......@@ -1098,7 +1110,7 @@ Company.findAll({
```
### Nested eager loading
You can used nested eager loading to load all related models of a related model:
You can used nested eager loading to load all related models of a related model:
```js
User.findAll({
include: [
......@@ -1130,7 +1142,7 @@ User.findAll({
})
```
Include all also supports nested loading:
Include all also supports nested loading:
```js
User.findAll({ include: [{ all: true, nested: true }]});
......
......@@ -387,7 +387,7 @@ Task.belongsTo(User)
User.hasMany(Task)
User.hasMany(Tool, { as: 'Instruments' })
sequelize.sync().done(function() {
sequelize.sync().then(function() {
// this is where we continue ...
})
```
......
......@@ -57,12 +57,12 @@ $like: '%hat', // LIKE '%hat'
$notLike: '%hat' // NOT LIKE '%hat'
$iLike: '%hat' // ILIKE '%hat' (case insensitive)
$notILike: '%hat' // NOT ILIKE '%hat'
$like: { $any: ['cat', 'hat']}
$like: { $any: ['cat', 'hat']}
// LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike
$overlap: [1, 2] // && [1, 2] (PG array overlap operator)
$contains: [1, 2] // @> [1, 2] (PG array contains operator)
$contained: [1, 2] // <@ [1, 2] (PG array contained by operator)
$any: [2,3] // ANY ARRAY[2, 3]::INTEGER
$any: [2,3] // ANY ARRAY[2, 3]::INTEGER (PG only)
```
### Combinations
......@@ -169,7 +169,7 @@ something.findOne({
// Will order by max(age) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// Will order by otherfunction(`col1`, 12, 'lalala') DESC
// Will order by otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// Both the following statements will be treated literally so should be treated with care
......
......@@ -8,7 +8,7 @@ sequelize.query("UPDATE users SET y = 42 WHERE x = 12").spread(function(results,
})
```
In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do:
In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do:
```js
sequelize.query("SELECT * FROM `users`", { type: sequelize.QueryTypes.SELECT})
......@@ -19,11 +19,11 @@ sequelize.query("SELECT * FROM `users`", { type: sequelize.QueryTypes.SELECT})
Several other query types are available. [Peek into the source for details](https://github.com/sequelize/sequelize/blob/master/lib/query-types.js)
A second, optional, argument is the _callee_, which is a model. If you pass a model the returned data will be instances of that model.
A second option is the model. If you pass a model the returned data will be instances of that model.
```js
// Callee is the model definition. This allows you to easily map a query to a predefined model
sequelize.query('SELECT * FROM projects', Projects).then(function(projects){
sequelize.query('SELECT * FROM projects', { model: Projects }).then(function(projects){
// Each record will now be a instance of Project
})
```
......@@ -35,13 +35,13 @@ Replacements in a query can be done in two different ways, either using named pa
* If an object is passed, `:key` will be replaced with the keys from that object. If the object contains keys not found in the query or vice verca, an exception will be thrown.
```js
sequelize.query('SELECT * FROM projects WHERE status = ?',
sequelize.query('SELECT * FROM projects WHERE status = ?',
{ replacements: ['active'], type: sequelize.QueryTypes.SELECT }
).then(function(projects) {
console.log(projects)
})
sequelize.query('SELECT * FROM projects WHERE status = :status ',
sequelize.query('SELECT * FROM projects WHERE status = :status ',
{ replacements: { status: 'active' }, type: sequelize.QueryTypes.SELECT }
).then(function(projects) {
console.log(projects)
......
## Basic usage
To get the ball rollin' you first have to create an instance of Sequelize&period; Use it the following way&colon;
```js
var sequelize = new Sequelize('database', 'username'[, 'password'])
```
......@@ -15,7 +15,7 @@ var sequelize = new Sequelize('database', 'username', 'password', {
```
If you just don't have a password&colon;
```js
var sequelize = new Sequelize('database', 'username')
// or
......@@ -23,7 +23,7 @@ var sequelize = new Sequelize('database', 'username', null)
```
You can also use a connection string&colon;
```js
var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname', {
// Look to the next section for possible options
......@@ -33,7 +33,7 @@ var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname', {
## Options
Besides the host and the port&comma; Sequelize comes with a whole bunch of options&period; Here they are&colon;
```js
var sequelize = new Sequelize('database', 'username', 'password', {
// custom host; default: localhost
......@@ -108,7 +108,15 @@ var sequelize = new Sequelize('database', 'username', 'password', {
 
// language is used to determine how to translate words into singular or plural form based on the [lingo project](https://github.com/visionmedia/lingo)
// options are: en [default], es
language: 'en'
language: 'en',
// isolation level of each transaction. Defaults to REPEATABLE_READ
// options are:
// READ_UNCOMMITTED
// READ_COMMITTED
// REPEATABLE_READ
// SERIALIZABLE
isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ
})
```
......@@ -117,7 +125,7 @@ var sequelize = new Sequelize('database', 'username', 'password', {
## Read replication
Sequelize supports read replication&comma; i&period;e&period; having multiple servers that you can connect to when you want to do a SELECT query&period; When you do read replication&comma; you specify one or more servers to act as read replicas&comma; and one server to act as the write master&comma; which handles all writes and updates and propagates them to the replicas &lpar;note that the actual replication process is **not** handled by Sequelize&comma; but should be set up in MySql&rpar;&period;
```js
var sequelize = new Sequelize('database', null, null, {
dialect: 'mysql',
......@@ -139,7 +147,7 @@ var sequelize = new Sequelize('database', null, null, {
If you have any general settings that apply to all replicas you do not need to provide them for each instance&period; In the code above&comma; database name and port is propagated to all replicas&period; The same will happen for user and password&comma; if you leave them out for any of the replicas&period; Each replica has the following options&colon;`host`&comma;`port`&comma;`username`&comma;`password`&comma;`database`.
Sequelize uses a pool to manage connections to your replicas&period; The default options are&colon;
```js
{
maxConnections: 10,
......@@ -159,7 +167,7 @@ With the release of Sequelize`1.6.0`&comma; the library got independent from spe
### MySQL
In order to get Sequelize working nicely together with MySQL&comma; you'll need to install`mysql@~2.5.0`or higher. Once that's done you can use it like this&colon;
```js
var sequelize = new Sequelize('database', 'username', 'password', {
// mysql is the default dialect, but you know...
......@@ -177,8 +185,8 @@ for examples (currently only mysql and mariadb are supported).
For MariaDB compatibility you have to install the package `mariasql@~0.1.20`.
The configuration needs to look like this:
```js
```js
var sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mariadb'
})
......@@ -188,7 +196,7 @@ var sequelize = new Sequelize('database', 'username', 'password', {
For SQLite compatibility you'll need`sqlite3@~3.0.0`. Configure Sequelize like this&colon;
```js
```js
var sequelize = new Sequelize('database', 'username', 'password', {
// sqlite! now!
dialect: 'sqlite',
......@@ -202,8 +210,8 @@ var sequelize = new Sequelize('database', 'username', 'password', {
### PostgreSQL
The library for PostgreSQL is`pg@~3.6.0` You'll just need to define the dialect&colon;
```js
```js
var sequelize = new Sequelize('database', 'username', 'password', {
// gimme postgres, please!
dialect: 'postgres'
......@@ -213,8 +221,8 @@ var sequelize = new Sequelize('database', 'username', 'password', {
### MSSQL
The library for MSSQL is`tedious@^1.7.0` You'll just need to define the dialect&colon;
```js
```js
var sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mssql'
})
......@@ -225,29 +233,29 @@ var sequelize = new Sequelize('database', 'username', 'password', {
As there are often use cases in which it is just easier to execute raw &sol; already prepared SQL queries, you can utilize the function `sequelize.query`.
Here is how it works:
```js
// Arguments for raw queries
sequelize.query('your query', [, callee], [, options], [, replacements])
sequelize.query('your query', [, options])
// Quick example
sequelize.query("SELECT * FROM myTable").success(function(myTableRows) {
sequelize.query("SELECT * FROM myTable").then(function(myTableRows) {
console.log(myTableRows)
})
// Callee is the model definition. This allows you to easily map a query
// to a predefined model for sequelizejs e.g:
// If you want to return sequelize instances use the model options.
// This allows you to easily map a query to a predefined model for sequelizejs e.g:
sequelize
.query('SELECT * FROM projects', Projects)
.success(function(projects){
// Each record will now be mapped to the project's DAO-Factory.
.query('SELECT * FROM projects', { model: Projects })
.then(function(projects){
// Each record will now be mapped to the project's model.
console.log(projects)
})
// Options is an object with the following keys:
sequelize
.query('SELECT 1', null, {
.query('SELECT 1', {
// A function (or false) for logging your queries
// Will get called for every SQL query that gets send
// to the server.
......@@ -265,8 +273,8 @@ sequelize
// Even if we declared a callee here, the raw: true would
// supersede and return a raw object.
sequelize
.query('SELECT * FROM projects', null, { raw: true })
.success(function(projects) {
.query('SELECT * FROM projects', { raw: true })
.then(function(projects) {
console.log(projects)
})
```
......@@ -274,7 +282,7 @@ sequelize
Replacements in a query can be done in two different ways, either using
named parameters (starting with `:`), or unnamed, represented by a ?
The syntax used depends on the fourth argument passed to the function:
The syntax used depends on the replacements option passed to the function:
* If an array is passed, `?` will be replaced in the order that they appear in the array
* If an object is passed, `:key` will be replaced with the keys from that object.
......@@ -284,19 +292,19 @@ will be thrown.
```js
sequelize
.query(
'SELECT * FROM projects WHERE status = ?', null,
{ raw: true }, ['active']
'SELECT * FROM projects WHERE status = ?',
{ raw: true, replacements: ['active']
)
.success(function(projects) {
.then(function(projects) {
console.log(projects)
})
sequelize
.query(
'SELECT * FROM projects WHERE status = :status ', null,
{ raw: true }, { status: 'active' }
'SELECT * FROM projects WHERE status = :status ',
{ raw: true, replacements: { status: 'active' } }
)
.success(function(projects) {
.then(function(projects) {
console.log(projects)
})
```
......@@ -304,7 +312,7 @@ sequelize
**One note:** If the attribute names of the table contain dots, the resulting objects will be nested:
```js
sequelize.query('select 1 as `foo.bar.baz`').success(function(rows) {
sequelize.query('select 1 as `foo.bar.baz`').then(function(rows) {
console.log(JSON.stringify(rows))
/*
......
......@@ -11,7 +11,7 @@ module.exports = (function() {
this.source = this.association.source;
};
HasManySingleLinked.prototype.injectGetter = function(options, queryOptions) {
HasManySingleLinked.prototype.injectGetter = function(options) {
var scopeWhere = this.association.scope ? {} : null;
if (this.association.scope) {
Object.keys(this.association.scope).forEach(function (attribute) {
......@@ -39,7 +39,7 @@ module.exports = (function() {
}
}
return model.all(options, queryOptions);
return model.all(options);
};
HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) {
......
......@@ -293,7 +293,7 @@ module.exports = (function() {
}
currentAssociatedObjects.forEach(function(association) {
if (!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) {
if (!Utils._.isEqual(oldAssociatedObject.where(), association.where())) {
newAssociations.push(association);
}
});
......@@ -325,7 +325,7 @@ module.exports = (function() {
// Determine is this is an association we want to remove
var obj = Utils._.find(oldAssociatedObjects, function(oldAssociatedObject) {
return Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers);
return Utils._.isEqual(oldAssociatedObject.where(), association.where());
});
// This is not an association we want to remove. Add it back
......
......@@ -1596,7 +1596,6 @@ module.exports = (function() {
if (
(Array.isArray(where) && where.length === 0) ||
(_.isPlainObject(where) && _.isEmpty(where)) ||
where === '' ||
where === null ||
where === undefined
) {
......
......@@ -5,7 +5,7 @@ var Utils = require('../../utils')
, QueryTypes = require('../../query-types');
module.exports = (function() {
var AbstractQuery = function(database, sequelize, callee, options) {};
var AbstractQuery = function(database, sequelize, options) {};
/**
* Execute the passed sql query.
......@@ -193,7 +193,6 @@ module.exports = (function() {
result = this.callee.bulkBuild(results, {
isNewRecord: false,
isDirty: false,
include: this.options.include,
includeNames: this.options.includeNames,
includeMap: this.options.includeMap,
......@@ -205,7 +204,6 @@ module.exports = (function() {
} else {
result = this.callee.bulkBuild(results, {
isNewRecord: false,
isDirty: false,
raw: true,
attributes: this.options.attributes
});
......
......@@ -5,9 +5,9 @@ var Utils = require('../../utils')
, sequelizeErrors = require('../../errors.js');
module.exports = (function() {
var Query = function(connection, sequelize, callee, options) {
var Query = function(connection, sequelize, options) {
this.connection = connection;
this.callee = callee;
this.callee = options.callee;
this.sequelize = sequelize;
this.options = Utils._.extend({
logging: console.log,
......
......@@ -6,9 +6,9 @@ var Utils = require('../../utils')
, sequelizeErrors = require('../../errors.js');
module.exports = (function() {
var Query = function(connection, sequelize, callee, options) {
var Query = function(connection, sequelize, options) {
this.connection = connection;
this.callee = callee;
this.callee = options.callee;
this.sequelize = sequelize;
this.uuid = uuid.v4();
this.options = Utils._.extend({
......
......@@ -67,10 +67,10 @@ function dialectSpecificFieldDatatypeMap (options, prefix) {
}
module.exports = (function() {
var Query = function(client, sequelize, callee, options) {
var Query = function(client, sequelize, options) {
this.client = client;
this.sequelize = sequelize;
this.callee = callee;
this.callee = options.callee;
this.options = Utils._.extend({
logging: console.log,
plain: false,
......@@ -84,7 +84,6 @@ module.exports = (function() {
Query.prototype.parseDialectSpecificFields = parseDialectSpecificFields;
Query.prototype.run = function(sql) {
/* jshint -W027 */
this.sql = sql;
var self = this
......
......@@ -6,10 +6,10 @@ var Utils = require('../../utils')
, sequelizeErrors = require('../../errors.js');
module.exports = (function() {
var Query = function(database, sequelize, callee, options) {
var Query = function(database, sequelize, options) {
this.database = database;
this.sequelize = sequelize;
this.callee = callee;
this.callee = options.callee;
this.options = Utils._.extend({
logging: console.log,
plain: false,
......
......@@ -392,7 +392,6 @@ module.exports = (function() {
if (!isEmpty) {
childOptions = {
isNewRecord: this.isNewRecord,
isDirty: false,
include: include.include,
includeNames: include.includeNames,
includeMap: include.includeMap,
......@@ -426,11 +425,15 @@ module.exports = (function() {
* @param {Object} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
* @param {Boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
* @param {Boolean} [options.validate=true] If false, validations won't be run.
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction]
*
* @return {Promise<this|Errors.ValidationError>}
*/
Instance.prototype.save = function(options) {
if (arguments.length > 1) {
throw new Error('The second argument was removed in favor of the options object.');
}
options = _.defaults(options || {}, {
hooks: true,
validate: true
......@@ -667,6 +670,7 @@ module.exports = (function() {
*
* @see {Model#find}
* @param {Object} [options] Options that are passed on to `Model.find`
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @return {Promise<this>}
*/
Instance.prototype.reload = function(options) {
......@@ -745,6 +749,7 @@ module.exports = (function() {
*
* @param {Object} [options={}]
* @param {Boolean} [options.force=false] If set to true, paranoid models will actually be deleted
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction]
*
* @return {Promise<undefined>}
......@@ -772,7 +777,7 @@ module.exports = (function() {
for(var i = 0; i < primaryKeys.length; i++) {
where[this.Model.rawAttributes[primaryKeys[i]].field] = this.get(primaryKeys[i], { raw: true });
}
return this.QueryInterface.delete(this, this.Model.getTableName(options), where, _.defaults(options, { type: QueryTypes.DELETE,limit: null}));
return this.sequelize.getQueryInterface().delete(this, this.Model.getTableName(options), where, _.defaults(options, { type: QueryTypes.DELETE,limit: null}));
}
}).tap(function() {
// Run after hook
......@@ -788,6 +793,7 @@ module.exports = (function() {
* Restore the row corresponding to this instance. Only available for paranoid models.
*
* @param {Object} [options={}]
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction]
*
* @return {Promise<undefined>}
......@@ -834,6 +840,7 @@ module.exports = (function() {
* @param {String|Array|Object} fields If a string is provided, that column is incremented by the value of `by` given in options. If an array is provided, the same is true for each column. If and object is provided, each column is incremented by the value given.
* @param {Object} [options]
* @param {Integer} [options.by=1] The number to increment by
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction]
*
* @return {Promise<this>}
......@@ -906,6 +913,7 @@ module.exports = (function() {
* @param {String|Array|Object} fields If a string is provided, that column is decremented by the value of `by` given in options. If an array is provided, the same is true for each column. If and object is provided, each column is decremented by the value given
* @param {Object} [options]
* @param {Integer} [options.by=1] The number to decrement by
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction]
*
* @return {Promise}
......
......@@ -671,13 +671,8 @@ module.exports = (function() {
return this.sequelize.query(sql, options);
};
QueryInterface.prototype.select = function(model, tableName, options, queryOptions) {
QueryInterface.prototype.select = function(model, tableName, options) {
options = options || {};
if (queryOptions) {
_.assign(options, queryOptions);
}
options.type = QueryTypes.SELECT;
options.instance = model;
return this.sequelize.query(
......@@ -704,7 +699,7 @@ module.exports = (function() {
});
}
options = _.defaults(options || {}, {
options = _.defaults(options || {}, {
raw: true,
plain: true,
type: QueryTypes.SELECT
......
'use strict';
var url = require('url')
, _ = require('lodash')
, Path = require('path')
, Utils = require('./utils')
, Model = require('./model')
......@@ -627,20 +626,24 @@ module.exports = (function() {
* @param {Boolean} [options.plain=false] Sets the query type to `SELECT` and return a single row
* @param {Object|Array} [options.replacements] Either an object of named parameter replacements in the format `:param` or an array of unnamed replacements to replace `?` in your SQL.
* @param {Boolean} [options.useMaster=false] Force the query to use the write pool, regardless of the query type.
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Instance} [options.instance] A sequelize instance used to build the return instance
* @param {Model} [options.model] A sequelize model used to build the returned model instances (used to be called callee)
*
* @return {Promise}
*
* @see {Model#build} for more information about callee.
*/
Sequelize.prototype.query = function(sql, callee, options) {
var self = this;
if (_.isPlainObject(callee)) {
options = callee;
callee = options.instance || options.model || undefined;
Sequelize.prototype.query = function(sql, options) {
if (arguments.length > 2) {
// TODO: Remove this note in the next major version (4.0)
throw new Error('Sequelize.query was refactored to only use the parameters `sql` and `options`. Please read the changelog about BC.');
}
var self = this;
options = options || {};
options.callee = options.instance || options.model;
if (Utils._.isPlainObject(sql)) {
if (sql.hasOwnProperty('values')) {
......@@ -658,7 +661,7 @@ module.exports = (function() {
sql = sql.trim();
if (!(callee instanceof Sequelize.Model)) {
if (!(options.callee instanceof Sequelize.Model)) {
// When callee is not set, assume raw query
options.raw = true;
}
......@@ -682,7 +685,7 @@ module.exports = (function() {
}
if (!options.type) {
if (callee || options.nest || options.plain) {
if (options.callee || options.nest || options.plain) {
options.type = QueryTypes.SELECT;
} else {
options.type = QueryTypes.RAW;
......@@ -700,7 +703,7 @@ module.exports = (function() {
return Promise.resolve(
options.transaction ? options.transaction.connection : self.connectionManager.getConnection(options)
).then(function (connection) {
var query = new self.dialect.Query(connection, self, callee, options);
var query = new self.dialect.Query(connection, self, options);
return query.run(sql).finally(function() {
if (options.transaction) return;
return self.connectionManager.releaseConnection(connection);
......@@ -1076,9 +1079,10 @@ module.exports = (function() {
*
* @see {Transaction}
* @param {Object} [options={}]
* @param {Boolean} [options.autocommit=true]
* @param {String} [options.isolationLevel='REPEATABLE_READ'] See `Sequelize.Transaction.ISOLATION_LEVELS` for possible options
* @param {Object} [options={}]
* @param {Boolean} [options.autocommit=true]
* @param {String} [options.isolationLevel='REPEATABLE_READ'] See `Sequelize.Transaction.ISOLATION_LEVELS` for possible options
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @return {Promise}
* @fires error If there is an uncaught error during the transaction
* @fires success When the transaction has ended (either comitted or rolled back)
......@@ -1108,6 +1112,7 @@ module.exports = (function() {
});
});
}).catch(function(err) {
// If an error occured while commiting a transaction do not try to rollback the transaction
if (transaction.finished === 'commit') {
reject(err);
} else {
......
......@@ -60,7 +60,7 @@ Transaction.ISOLATION_LEVELS = {
* t1.LOCK.KEY_SHARE, // Postgres 9.3+ only
* t1.LOCK.NO_KEY_UPDATE // Postgres 9.3+ only
* ```
*
*
* Usage:
* ```js
* t1 // is a transaction
......@@ -70,7 +70,7 @@ Transaction.ISOLATION_LEVELS = {
* lock: t1.LOCK...
* });
* ```
*
*
* Postgres also supports specific locks while eager loading by using OF:
* ```js
* UserModel.findAll({
......@@ -84,7 +84,7 @@ Transaction.ISOLATION_LEVELS = {
* });
* ```
* UserModel will be locked but TaskModel won't!
*
*
* @property LOCK
*/
Transaction.LOCK = Transaction.prototype.LOCK = {
......
......@@ -1079,7 +1079,7 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), function() {
expect(project).to.be.ok;
return project.destroy().return (user);
}).then(function(user) {
return self.User.find({
return self.User.findOne({
where: { id: user.id},
include: [{model: self.Project, as: 'Projects'}]
});
......@@ -1361,7 +1361,7 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), function() {
return u.addProject(p, { status: 'active' });
}).then(function() {
return this.UserProjects.find({ where: { UserId: this.u.id, ProjectId: this.p.id }});
return this.UserProjects.findOne({ where: { UserId: this.u.id, ProjectId: this.p.id }});
}).then(function(up) {
expect(up.status).to.equal('active');
});
......@@ -1449,8 +1449,8 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), function() {
return user.setProjects([this.p1, this.p2], { status: 'active' });
}).then(function() {
return Promise.all([
self.UserProjects.find({ where: { UserId: this.user.id, ProjectId: this.p1.id }}),
self.UserProjects.find({ where: { UserId: this.user.id, ProjectId: this.p2.id }})
self.UserProjects.findOne({ where: { UserId: this.user.id, ProjectId: this.p1.id }}),
self.UserProjects.findOne({ where: { UserId: this.user.id, ProjectId: this.p2.id }})
]);
}).spread(function(up1, up2) {
expect(up1.status).to.equal('inactive');
......@@ -1570,7 +1570,7 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), function() {
.save()
.then(function() { return b1.save(); })
.then(function() { return a1.setRelation1(b1); })
.then(function() { return self.A.find({ where: { name: 'a1' } }); })
.then(function() { return self.A.findOne({ where: { name: 'a1' } }); })
.then(function(a) {
expect(a.relation1Id).to.be.eq(b1.id);
});
......@@ -1596,7 +1596,7 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), function() {
.save()
.then(function() { return b1.save(); })
.then(function() { return b1.setRelation1(a1); })
.then(function() { return self.B.find({ where: { name: 'b1' } }); })
.then(function() { return self.B.findOne({ where: { name: 'b1' } }); })
.then(function(b) {
expect(b.relation1Id).to.be.eq(a1.id);
});
......
......@@ -416,7 +416,7 @@ describe(Support.getTestDialectTeaser('BelongsTo'), function() {
}).then(function(user) {
// the sql query should correctly look at task_id instead of taskId
expect(user).to.not.be.null;
return User.find({
return User.findOne({
where: {username: 'foo'},
include: [Account]
});
......
......@@ -301,7 +301,7 @@ describe(Support.getTestDialectTeaser('HasOne'), function() {
}).then(function(user) {
// the sql query should correctly look at task_id instead of taskId
expect(user).to.not.be.null;
return Task.find({
return Task.findOne({
where: {title: 'task'},
include: [User]
});
......
......@@ -125,10 +125,10 @@ describe(Support.getTestDialectTeaser('associations'), function() {
self.Post.find({
include: [self.Comment]
}),
self.Image.find({
self.Image.findOne({
include: [self.Comment]
}),
self.Question.find({
self.Question.findOne({
include: [self.Comment]
})
);
......@@ -153,9 +153,9 @@ describe(Support.getTestDialectTeaser('associations'), function() {
});
this.PostTag = this.sequelize.define('post_tag');
this.Tag.hasMany(this.Post, {through: this.PostTag});
this.Post.hasMany(this.Tag, {as: 'categories', through: this.PostTag, scope: { type: 'category' }});
this.Post.hasMany(this.Tag, {as: 'tags', through: this.PostTag, scope: { type: 'tag' }});
this.Tag.belongsToMany(this.Post, {through: this.PostTag});
this.Post.belongsToMany(this.Tag, {as: 'categories', through: this.PostTag, scope: { type: 'category' }});
this.Post.belongsToMany(this.Tag, {as: 'tags', through: this.PostTag, scope: { type: 'tag' }});
});
it('should create, find and include associations with scope values', function() {
......@@ -213,7 +213,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
expect(postCTags[0].get('type')).to.equal('tag');
}).then(function() {
return Promise.join(
self.Post.find({
self.Post.findOne({
where: {
id: self.postA.get('id')
},
......@@ -222,7 +222,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
{model: self.Tag, as: 'categories'}
]
}),
self.Post.find({
self.Post.findOne({
where: {
id: self.postB.get('id')
},
......@@ -231,7 +231,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
{model: self.Tag, as: 'categories'}
]
}),
self.Post.find({
self.Post.findOne({
where: {
id: self.postC.get('id')
},
......@@ -289,7 +289,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
name: DataTypes.STRING
});
this.Post.hasMany(this.Tag, {
this.Post.belongsToMany(this.Tag, {
through: {
model: this.ItemTag,
unique: false,
......@@ -300,7 +300,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
foreignKey: 'taggable_id',
constraints: false
});
this.Tag.hasMany(this.Post, {
this.Tag.belongsToMany(this.Post, {
through: {
model: this.ItemTag,
unique: false
......@@ -308,7 +308,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
foreignKey: 'tag_id'
});
this.Image.hasMany(this.Tag, {
this.Image.belongsToMany(this.Tag, {
through: {
model: this.ItemTag,
unique: false,
......@@ -319,7 +319,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
foreignKey: 'taggable_id',
constraints: false
});
this.Tag.hasMany(this.Image, {
this.Tag.belongsToMany(this.Image, {
through: {
model: this.ItemTag,
unique: false
......@@ -327,7 +327,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
foreignKey: 'tag_id'
});
this.Question.hasMany(this.Tag, {
this.Question.belongsToMany(this.Tag, {
through: {
model: this.ItemTag,
unique: false,
......@@ -338,7 +338,7 @@ describe(Support.getTestDialectTeaser('associations'), function() {
foreignKey: 'taggable_id',
constraints: false
});
this.Tag.hasMany(this.Question, {
this.Tag.belongsToMany(this.Question, {
through: {
model: this.ItemTag,
unique: false
......@@ -412,15 +412,15 @@ describe(Support.getTestDialectTeaser('associations'), function() {
}).sort()).to.deep.equal(['questionTag', 'tagA', 'tagC']);
}).then(function () {
return Promise.join(
self.Post.find({
self.Post.findOne({
where: {},
include: [self.Tag]
}),
self.Image.find({
self.Image.findOne({
where: {},
include: [self.Tag]
}),
self.Question.find({
self.Question.findOne({
where: {},
include: [self.Tag]
})
......
......@@ -15,8 +15,8 @@ if (Support.dialectIsMySQL()) {
, Table1 = this.sequelize.define('wp_table1', {foo: DataTypes.STRING})
, self = this;
Table1.hasMany(Table2);
Table2.hasMany(Table1);
Table1.belongsToMany(Table2, { through: 'wp_table1swp_table2s' });
Table2.belongsToMany(Table1, { through: 'wp_table1swp_table2s' });
return Table1.sync({ force: true }).then(function() {
return Table2.sync({ force: true }).then(function() {
expect(self.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).to.exist;
......@@ -30,8 +30,8 @@ if (Support.dialectIsMySQL()) {
var Table2 = this.sequelize.define('ms_table1', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: DataTypes.STRING});
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'});
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'});
Table1.belongsToMany(Table2, {through: 'table1_to_table2'});
Table2.belongsToMany(Table1, {through: 'table1_to_table2'});
return Table1.sync({ force: true }).then(function() {
return Table2.sync({ force: true });
});
......@@ -52,8 +52,8 @@ if (Support.dialectIsMySQL()) {
this.users = null;
this.tasks = null;
this.User.hasMany(this.Task, {as: 'Tasks', through: 'UserTasks'});
this.Task.hasMany(this.User, {as: 'Users', through: 'UserTasks'});
this.User.belongsToMany(this.Task, {as: 'Tasks', through: 'UserTasks'});
this.Task.belongsToMany(this.User, {as: 'Users', through: 'UserTasks'});
var self = this
, users = []
......
......@@ -149,7 +149,7 @@ if (Support.dialectIsMySQL()) {
expectation: 'SELECT count(*) AS `count` FROM `foo`;',
context: QueryGenerator
}, {
arguments: ['myTable', {where: "foo='bar'"}],
arguments: ['myTable', {where: ["foo='bar'"]}],
expectation: "SELECT * FROM `myTable` WHERE foo='bar';",
context: QueryGenerator
}, {
......@@ -318,8 +318,8 @@ if (Support.dialectIsMySQL()) {
context: QueryGenerator
}, {
title: 'no where arguments (string)',
arguments: ['myTable', {where: ''}],
expectation: 'SELECT * FROM `myTable`;',
arguments: ['myTable', {where: ['']}],
expectation: 'SELECT * FROM `myTable` WHERE 1=1;',
context: QueryGenerator
}, {
title: 'no where arguments (null)',
......
......@@ -16,8 +16,8 @@ if (dialect.match(/^postgres/)) {
var Table2 = this.sequelize.define('wp_table2', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: DataTypes.STRING});
Table1.hasMany(Table2);
Table2.hasMany(Table1);
Table1.belongsToMany(Table2, { through: 'wp_table1swp_table2s' });
Table2.belongsToMany(Table1, { through: 'wp_table1swp_table2s' });
expect(this.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).to.exist;
});
......@@ -28,8 +28,8 @@ if (dialect.match(/^postgres/)) {
var Table2 = this.sequelize.define('ms_table1', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: DataTypes.STRING});
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'});
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'});
Table1.belongsToMany(Table2, {through: 'table1_to_table2'});
Table2.belongsToMany(Table1, {through: 'table1_to_table2'});
});
it('should not use a combined name', function() {
......@@ -107,8 +107,8 @@ if (dialect.match(/^postgres/)) {
this.users = null;
this.tasks = null;
this.User.hasMany(this.Task, {as: 'Tasks', through: 'usertasks'});
this.Task.hasMany(this.User, {as: 'Users', through: 'usertasks'});
this.User.belongsToMany(this.Task, {as: 'Tasks', through: 'usertasks'});
this.Task.belongsToMany(this.User, {as: 'Users', through: 'usertasks'});
for (var i = 0; i < 5; ++i) {
users[users.length] = {id: i + 1, name: 'User' + Math.random()};
......
......@@ -41,7 +41,11 @@ if (dialect.match(/^postgres/)) {
});
it('should be able to search within an array', function() {
return this.User.findAll({where: {email: ['hello', 'world']}, attributes: ['id','username','email','settings','document','phones','emergency_contact','friends']}, {
return this.User.findAll({
where: {
email: ['hello', 'world']
},
attributes: ['id','username','email','settings','document','phones','emergency_contact','friends'],
logging: function (sql) {
expect(sql).to.equal('Executing (default): SELECT "id", "username", "email", "settings", "document", "phones", "emergency_contact", "friends" FROM "Users" AS "User" WHERE "User"."email" = ARRAY[\'hello\',\'world\']::TEXT[];');
}
......@@ -549,14 +553,14 @@ if (dialect.match(/^postgres/)) {
return this.User.create({ username: 'user', email: ['foo@bar.com'], settings: { test: '"value"' }}).then(function(newUser) {
// Check to see if the default value for an hstore field works
expect(newUser.document).to.deep.equal({default: "'value'"});
expect(newUser.document).to.deep.equal({ default: "'value'" });
expect(newUser.settings).to.deep.equal({ test: '"value"' });
// Check to see if updating an hstore field works
return self.User.update({settings: {should: 'update', to: 'this', first: 'place'}}, {where: newUser.identifiers}).then(function() {
return self.User.update({ settings: { should: 'update', to: 'this', first: 'place' }}, { where: newUser.where() }).then(function() {
return newUser.reload().then(function() {
// Postgres always returns keys in alphabetical order (ascending)
expect(newUser.settings).to.deep.equal({first: 'place', should: 'update', to: 'this'});
expect(newUser.settings).to.deep.equal({ first: 'place', should: 'update', to: 'this' });
});
});
});
......@@ -567,9 +571,9 @@ if (dialect.match(/^postgres/)) {
return this.User.create({ username: 'user', email: ['foo@bar.com'], settings: { test: '"value"' }}).then(function(oldUser) {
// Update the user and check that the returned object's fields have been parsed by the hstore library
return self.User.update({settings: {should: 'update', to: 'this', first: 'place'}}, {where: oldUser.identifiers, returning: true }).spread(function(count, users) {
return self.User.update({ settings: { should: 'update', to: 'this', first: 'place' }}, { where: oldUser.where(), returning: true }).spread(function(count, users) {
expect(count).to.equal(1);
expect(users[0].settings).to.deep.equal({should: 'update', to: 'this', first: 'place'});
expect(users[0].settings).to.deep.equal({ should: 'update', to: 'this', first: 'place' });
});
});
});
......@@ -723,7 +727,7 @@ if (dialect.match(/^postgres/)) {
var User = this.User
, period = [new Date(2015, 0, 1), new Date(2015, 11, 31)];
return User.create({ username: 'user', email: ['foo@bar.com'], course_period: period}).then(function(newUser) {
return User.create({ username: 'user', email: ['foo@bar.com'], course_period: period }).then(function(newUser) {
// Check to see if the default value for a range field works
expect(newUser.acceptable_marks.length).to.equal(2);
expect(newUser.acceptable_marks[0]).to.equal(0.65); // lower bound
......@@ -738,7 +742,7 @@ if (dialect.match(/^postgres/)) {
period = [new Date(2015, 1, 1), new Date(2015, 10, 30)];
// Check to see if updating a range field works
return User.update({course_period: period}, {where: newUser.identifiers}).then(function() {
return User.update({course_period: period}, {where: newUser.where()}).then(function() {
return newUser.reload().then(function() {
expect(newUser.course_period[0] instanceof Date).to.be.ok;
expect(newUser.course_period[1] instanceof Date).to.be.ok;
......@@ -760,7 +764,7 @@ if (dialect.match(/^postgres/)) {
course_period: [new Date(2015, 0, 1), new Date(2015, 11, 31)]
}).then(function (oldUser) {
// Update the user and check that the returned object's fields have been parsed by the range parser
return User.update({ course_period: period }, { where: oldUser.identifiers, returning: true })
return User.update({ course_period: period }, { where: oldUser.where(), returning: true })
.spread(function (count, users) {
expect(count).to.equal(1);
expect(users[0].course_period[0] instanceof Date).to.be.ok;
......
......@@ -233,7 +233,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['foo', { attributes: [['count(*)', 'count']] }],
expectation: 'SELECT count(*) AS \"count\" FROM \"foo\";'
}, {
arguments: ['myTable', {where: "foo='bar'"}],
arguments: ['myTable', {where: ["foo='bar'"]}],
expectation: "SELECT * FROM \"myTable\" WHERE foo='bar';"
}, {
arguments: ['myTable', {order: 'id DESC'}],
......@@ -424,7 +424,7 @@ if (dialect.match(/^postgres/)) {
expectation: 'SELECT count(*) AS count FROM foo;',
context: {options: {quoteIdentifiers: false}}
}, {
arguments: ['myTable', {where: "foo='bar'"}],
arguments: ['myTable', {where: ["foo='bar'"]}],
expectation: "SELECT * FROM myTable WHERE foo='bar';",
context: {options: {quoteIdentifiers: false}}
}, {
......
......@@ -296,8 +296,8 @@ if (dialect === 'sqlite') {
context: QueryGenerator
}, {
title: 'no where arguments (string)',
arguments: ['myTable', {where: ''}],
expectation: 'SELECT * FROM `myTable`;',
arguments: ['myTable', {where: ['']}],
expectation: 'SELECT * FROM `myTable` WHERE 1=1;',
context: QueryGenerator
}, {
title: 'no where arguments (null)',
......
......@@ -563,15 +563,12 @@ describe(Support.getTestDialectTeaser('Includes with schemas'), function() {
User.findAll()
]);
}).spread(function(groups, users) {
return users[2].setGroup(groups[1], {
logging: console.log
});
return users[2].setGroup(groups[1]);
}).then(function() {
return User.findAll({
include: [
{model: Group, required: true}
],
logging: console.log
]
}).then(function(users) {
expect(users.length).to.equal(1);
expect(users[0].Group).to.be.ok;
......
......@@ -650,7 +650,7 @@ describe(Support.getTestDialectTeaser('Instance'), function() {
describe('complete', function() {
it('gets triggered if an error occurs', function() {
return this.User.findOne({ where: 'asdasdasd' }).catch(function(err) {
return this.User.findOne({ where: ['asdasdasd'] }).catch(function(err) {
expect(err).to.exist;
expect(err.message).to.exist;
});
......
......@@ -266,7 +266,6 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
return User.create({ username: 'bob', email: 'hello@world.com' }).then(function(user) {
return User
.update({ username: 'toni' }, { where: {id: user.id }})
.catch(function(err) { console.log(err); })
.then(function() {
return User.findById(1).then(function(user) {
expect(user.username).to.equal('toni');
......
......@@ -1449,7 +1449,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
expect(result.username).to.equal('Tony');
return User.destroy({where: {username: ['Tony', 'Max']}, force: true});
}).then(function() {
return self.sequelize.query('SELECT * FROM paranoidusers', null, {raw: true});
return self.sequelize.query('SELECT * FROM paranoidusers', {raw: true});
}).spread(function(users) {
expect(users).to.have.length(1);
expect(users[0].username).to.equal('Tobi');
......
......@@ -69,24 +69,22 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('should be ignored in find, findAll and includes', function() {
return Promise.all([
this.User.find({}, {
this.User.findOne({
logging: this.sqlAssert
}),
this.User.findAll({}, {
this.User.findAll({
logging: this.sqlAssert
}),
this.Task.findAll({
include: [
this.User
]
}, {
],
logging: this.sqlAssert
}),
this.Project.findAll({
include: [
this.User
]
}, {
],
logging: this.sqlAssert
})
]);
......
......@@ -839,9 +839,9 @@ describe(Support.getTestDialectTeaser('Model'), function() {
return User.sync({force: true}).then(function() {
return User.create({myvals: [1, 2, 3, 4], mystr: ['One', 'Two', 'Three', 'Four']}).then(function(user) {
user.myvals = [];
user.myvals = [];
user.mystr = [];
return user.save(undefined, {
return user.save({
logging: function(sql) {
test = true;
expect(sql.indexOf('ARRAY[]::INTEGER[]')).to.be.above(-1);
......
......@@ -197,10 +197,8 @@ describe(Support.getTestDialectTeaser('Model'), function() {
}
}
}).then(function (events) {
var event = events[0];
expect(events.length).to.equal(1);
expect(event.get('data')).to.eql({
expect(events[0].get('data')).to.eql({
name: {
first: 'Marge',
last: 'Simpson'
......@@ -212,36 +210,38 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
it('should be possible to query multiple nested values', function () {
return Promise.join(
this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
}
}),
this.Event.create({
data: {
name: {
first: 'Marge',
last: 'Simpson'
},
employment: 'Housewife'
}
}),
this.Event.create({
data: {
name: {
first: 'Bart',
last: 'Simpson'
},
employment: 'None'
}
})
).bind(this).then(function () {
return this.Event.findAll({
var self = this;
return this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
}
}).then(function() {
return Promise.join(
self.Event.create({
data: {
name: {
first: 'Marge',
last: 'Simpson'
},
employment: 'Housewife'
}
}),
self.Event.create({
data: {
name: {
first: 'Bart',
last: 'Simpson'
},
employment: 'None'
}
})
);
}).then(function () {
return self.Event.findAll({
where: {
data: {
name: {
......
......@@ -214,7 +214,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
});
it('executes a query the internal way', function() {
return this.sequelize.query(this.insertQuery, null, { raw: true });
return this.sequelize.query(this.insertQuery, { raw: true });
});
it('executes a query if only the sql is passed', function() {
......@@ -258,7 +258,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
return self.sequelize.query('DELETE FROM ' + qq(self.User.tableName)).then(function() {
return self.sequelize.query(self.insertQuery);
}).then(function() {
return self.sequelize.query('select username as ' + qq('user.username') + ' from ' + qq(self.User.tableName) + '', null, { raw: true, nest: true });
return self.sequelize.query('select username as ' + qq('user.username') + ' from ' + qq(self.User.tableName) + '', { raw: true, nest: true });
}).then(function(users) {
expect(users.map(function(u) { return u.user; })).to.deep.equal([{'username': 'john'}]);
});
......@@ -285,7 +285,9 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
it('uses the passed model', function() {
return this.sequelize.query(this.insertQuery).bind(this).then(function() {
return this.sequelize.query('SELECT * FROM ' + qq(this.User.tableName) + ';', this.User);
return this.sequelize.query('SELECT * FROM ' + qq(this.User.tableName) + ';', {
model: this.User
});
}).then(function(users) {
expect(users[0].Model).to.equal(this.User);
});
......@@ -294,12 +296,12 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
it('throw an exception if `values` and `options.replacements` are both passed', function() {
var self = this;
expect(function() {
return self.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, null, { raw: true, replacements: [1, 2] });
return self.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, { raw: true, replacements: [1, 2] });
}).to.throw(Error, 'Both `sql.values` and `options.replacements` cannot be set at the same time');
});
it('uses properties `query` and `values` if query is tagged', function() {
return this.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, null, { type: this.sequelize.QueryTypes.SELECT }).then(function(result) {
return this.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, { type: this.sequelize.QueryTypes.SELECT }).then(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
});
});
......@@ -308,76 +310,76 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
var tickChar = (dialect === 'postgres' || dialect === 'mssql') ? '"' : '`'
, sql = 'select 1 as ' + Sequelize.Utils.addTicks('foo.bar.baz', tickChar);
return expect(this.sequelize.query(sql, null, { raw: true, nest: false }).get(0)).to.eventually.deep.equal([{ 'foo.bar.baz': 1 }]);
return expect(this.sequelize.query(sql, { raw: true, nest: false }).get(0)).to.eventually.deep.equal([{ 'foo.bar.baz': 1 }]);
});
it('destructs dot separated attributes when doing a raw query using nest', function() {
var tickChar = (dialect === 'postgres' || dialect === 'mssql') ? '"' : '`'
, sql = 'select 1 as ' + Sequelize.Utils.addTicks('foo.bar.baz', tickChar);
return this.sequelize.query(sql, null, { raw: true, nest: true }).then(function(result) {
return this.sequelize.query(sql, { raw: true, nest: true }).then(function(result) {
expect(result).to.deep.equal([{ foo: { bar: { baz: 1 } } }]);
});
});
it('replaces token with the passed array', function() {
return this.sequelize.query('select ? as foo, ? as bar', null, { type: this.sequelize.QueryTypes.SELECT, replacements: [1, 2] }).then(function(result) {
return this.sequelize.query('select ? as foo, ? as bar', { type: this.sequelize.QueryTypes.SELECT, replacements: [1, 2] }).then(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
});
});
it('replaces named parameters with the passed object', function() {
return expect(this.sequelize.query('select :one as foo, :two as bar', null, { raw: true, replacements: { one: 1, two: 2 }}).get(0))
return expect(this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: { one: 1, two: 2 }}).get(0))
.to.eventually.deep.equal([{ foo: 1, bar: 2 }]);
});
it('replaces named parameters with the passed object and ignore those which does not qualify', function() {
return expect(this.sequelize.query('select :one as foo, :two as bar, \'00:00\' as baz', null, { raw: true, replacements: { one: 1, two: 2 }}).get(0))
return expect(this.sequelize.query('select :one as foo, :two as bar, \'00:00\' as baz', { raw: true, replacements: { one: 1, two: 2 }}).get(0))
.to.eventually.deep.equal([{ foo: 1, bar: 2, baz: '00:00' }]);
});
it('replaces named parameters with the passed object using the same key twice', function() {
return expect(this.sequelize.query('select :one as foo, :two as bar, :one as baz', null, { raw: true, replacements: { one: 1, two: 2 }}).get(0))
return expect(this.sequelize.query('select :one as foo, :two as bar, :one as baz', { raw: true, replacements: { one: 1, two: 2 }}).get(0))
.to.eventually.deep.equal([{ foo: 1, bar: 2, baz: 1 }]);
});
it('replaces named parameters with the passed object having a null property', function() {
return expect(this.sequelize.query('select :one as foo, :two as bar', null, { raw: true, replacements: { one: 1, two: null }}).get(0))
return expect(this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: { one: 1, two: null }}).get(0))
.to.eventually.deep.equal([{ foo: 1, bar: null }]);
});
it('throw an exception when key is missing in the passed object', function() {
var self = this;
expect(function() {
self.sequelize.query('select :one as foo, :two as bar, :three as baz', null, { raw: true, replacements: { one: 1, two: 2 }});
self.sequelize.query('select :one as foo, :two as bar, :three as baz', { raw: true, replacements: { one: 1, two: 2 }});
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g);
});
it('throw an exception with the passed number', function() {
var self = this;
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true, replacements: 2 });
self.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: 2 });
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g);
});
it('throw an exception with the passed empty object', function() {
var self = this;
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true, replacements: {}});
self.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: {}});
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g);
});
it('throw an exception with the passed string', function() {
var self = this;
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true, replacements: 'foobar'});
self.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: 'foobar'});
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g);
});
it('throw an exception with the passed date', function() {
var self = this;
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true, replacements: new Date()});
self.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: new Date()});
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g);
});
......@@ -394,7 +396,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
if (Support.getTestDialect() === 'postgres') {
it('replaces named parameters with the passed object and ignores casts', function() {
return expect(this.sequelize.query('select :one as foo, :two as bar, \'1000\'::integer as baz', null, { raw: true, replacements: { one: 1, two: 2 } }).get(0))
return expect(this.sequelize.query('select :one as foo, :two as bar, \'1000\'::integer as baz', { raw: true, replacements: { one: 1, two: 2 } }).get(0))
.to.eventually.deep.equal([{ foo: 1, bar: 2, baz: 1000 }]);
});
......@@ -1001,12 +1003,12 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
return self.sequelizeWithTransaction.transaction();
}).then(function(t1) {
this.t1 = t1;
return self.sequelizeWithTransaction.query('INSERT INTO ' + qq('TransactionTests') + ' (' + qq('name') + ') VALUES (\'foo\');', null, { transaction: t1 });
return self.sequelizeWithTransaction.query('INSERT INTO ' + qq('TransactionTests') + ' (' + qq('name') + ') VALUES (\'foo\');', { transaction: t1 });
}).then(function() {
return self.sequelizeWithTransaction.transaction();
}).then(function(t2) {
this.t2 = t2;
return self.sequelizeWithTransaction.query('INSERT INTO ' + qq('TransactionTests') + ' (' + qq('name') + ') VALUES (\'bar\');', null, { transaction: t2 });
return self.sequelizeWithTransaction.query('INSERT INTO ' + qq('TransactionTests') + ' (' + qq('name') + ') VALUES (\'bar\');', { transaction: t2 });
}).then(function() {
return expect(count()).to.eventually.equal(0);
}).then(function() {
......
......@@ -7,7 +7,7 @@ before(function() {
if (dialect !== 'postgres' && dialect !== 'postgres-native') {
return;
}
return Support.sequelize.query('CREATE EXTENSION IF NOT EXISTS hstore', null, {raw: true});
return Support.sequelize.query('CREATE EXTENSION IF NOT EXISTS hstore', {raw: true});
});
before(function() {
......@@ -16,7 +16,7 @@ before(function() {
if (dialect !== 'postgres' && dialect !== 'postgres-native') {
return;
}
return Support.sequelize.query('CREATE EXTENSION IF NOT EXISTS btree_gist', null, {raw: true});
return Support.sequelize.query('CREATE EXTENSION IF NOT EXISTS btree_gist', {raw: true});
});
beforeEach(function() {
......
......@@ -47,7 +47,7 @@ if (dialect !== 'sqlite') {
return TimezonedUser.create({});
}).then(function(timezonedUser) {
this.timezonedUser = timezonedUser;
return NormalUser.find(timezonedUser.id);
return NormalUser.findById(timezonedUser.id);
}).then(function(normalUser) {
// Expect 7 hours difference, in milliseconds.
// This difference is expected since two instances, configured for each their timezone is trying to read the same timestamp
......
......@@ -103,10 +103,10 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
var self = this;
return expect(
this.sequelize.transaction().then(function(t) {
return self.sequelize.query('SELECT 1+1', null, {transaction: t, raw: true}).then(function() {
return self.sequelize.query('SELECT 1+1', {transaction: t, raw: true}).then(function() {
return t.commit();
}).then(function() {
return self.sequelize.query('SELECT 1+1', null, {transaction: t, raw: true});
return self.sequelize.query('SELECT 1+1', {transaction: t, raw: true});
});
})
).to.eventually.be.rejected;
......@@ -116,10 +116,10 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
var self = this;
return expect(
this.sequelize.transaction().then(function(t) {
return self.sequelize.query('SELECT 1+1', null, {transaction: t, raw: true}).then(function() {
return self.sequelize.query('SELECT 1+1', {transaction: t, raw: true}).then(function() {
return t.commit();
}).then(function() {
return self.sequelize.query('SELECT 1+1', null, {transaction: t, raw: true});
return self.sequelize.query('SELECT 1+1', {transaction: t, raw: true});
});
})
).to.eventually.be.rejected;
......@@ -127,34 +127,31 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
if (dialect === 'sqlite'){
it('provides persistent transactions', function () {
var sequelize = new Support.Sequelize('database', 'username', 'password', {dialect: 'sqlite'}),
User = sequelize.define('user', {
var sequelize = new Support.Sequelize('database', 'username', 'password', {dialect: 'sqlite'})
, User = sequelize.define('user', {
username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN
});
})
, persistentTransaction;
return sequelize.transaction()
.then(function(t) {
return sequelize.sync({transaction:t})
.then(function( ) {
return t;
});
})
.then(function(t) {
return User.create({}, {transaction:t})
.then(function( ) {
t.commit();
});
})
.then(function( ) {
return sequelize.transaction();
})
.then(function(t) {
return User.findAll({}, {transaction:t});
})
.then(function(users) {
return expect(users.length).to.equal(1);
return sequelize.transaction().then(function(t) {
return sequelize.sync({ transaction:t }).then(function( ) {
return t;
});
}).then(function(t) {
return User.create({}, {transaction:t}).then(function( ) {
return t.commit();
});
}).then(function() {
return sequelize.transaction().then(function(t) {
persistentTransaction = t;
});
}).then(function() {
return User.findAll({transaction: persistentTransaction}).then(function(users) {
expect(users.length).to.equal(1);
return persistentTransaction.commit();
});
});
});
}
......@@ -176,8 +173,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
return User.find({
where: {
username: 'jan'
}
}, {
},
lock: t1.LOCK.UPDATE,
transaction: t1
}).then(function(t1Jan) {
......@@ -188,8 +184,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
User.find({
where: {
username: 'jan'
}
}, {
},
lock: t2.LOCK.UPDATE,
transaction: t2
}).then(function() {
......@@ -201,7 +196,9 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
t1Jan.updateAttributes({
awesome: true
}, { transaction: t1}).then(function() {
}, {
transaction: t1
}).then(function() {
t1Spy();
return Promise.delay(2000).then(function () {
return t1.commit();
......@@ -237,8 +234,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
where: {
username: 'John'
},
include: [Task]
}, {
include: [Task],
lock: t1.LOCK.UPDATE,
transaction: t1
})).to.be.rejectedWith('FOR UPDATE cannot be applied to the nullable side of an outer join');
......@@ -249,8 +245,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
where: {
username: 'John'
},
include: [Task]
}, {
include: [Task],
lock: t1.LOCK.UPDATE,
transaction: t1
});
......@@ -283,8 +278,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
where: {
username: 'John'
},
include: [Task]
}, {
include: [Task],
lock: {
level: t1.LOCK.UPDATE,
of: User
......@@ -330,8 +324,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
return User.find({
where: {
username: 'jan'
}
}, {
},
lock: t1.LOCK.NO_KEY_UPDATE,
transaction: t1
}).then(function(t1Jan) {
......@@ -340,8 +333,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
User.find({
where: {
username: 'jan'
}
}, {
},
lock: t2.LOCK.KEY_SHARE,
transaction: t2
}).then(function() {
......@@ -350,7 +342,9 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
}),
t1Jan.update({
awesome: true
}, { transaction: t1}).then(function() {
}, {
transaction: t1
}).then(function() {
return Promise.delay(2000).then(function () {
t1Spy();
expect(t1Spy).to.have.been.calledAfter(t2Spy);
......@@ -382,8 +376,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
return User.find({
where: {
username: 'jan'
}
}, {
},
lock: t1.LOCK.SHARE,
transaction: t1
}).then(function(t1Jan) {
......@@ -394,10 +387,10 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
User.find({
where: {
username: 'jan'
}
}, { transaction: t2}).then(function(t2Jan) {
},
transaction: t2
}).then(function(t2Jan) {
t2FindSpy();
return t2Jan.updateAttributes({
awesome: false
}, {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!