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

Commit 049c58f4 by Manuel Darveau

Merge remote-tracking branch 'upstream/master'

2 parents f5218ad6 c7d96ad1
Showing with 1237 additions and 1906 deletions
...@@ -12,9 +12,9 @@ Sequelize is a promise-based Node.js/io.js ORM for Postgres, MySQL, MariaDB, SQL ...@@ -12,9 +12,9 @@ Sequelize is a promise-based Node.js/io.js ORM for Postgres, MySQL, MariaDB, SQL
`npm install sequelize` `npm install sequelize`
Will install the latest version of sequelize (currently 2.0). From 3.0.0 and up Sequelize will follow SEMVER. 3.0.0 contains important security fixes so we highly recommend that users upgrade.
[Upgrading to 2.0](https://github.com/sequelize/sequelize/wiki/Upgrading-to-2.0) If you still use 1.7 please prefer to [Upgrading to 2.0](https://github.com/sequelize/sequelize/wiki/Upgrading-to-2.0) and the changelog between 2.0 and 3.0. 2.1 also has a breaking change.
## Features ## Features
......
# Next
- [FEATURE] Add support for keyword `after` in options of a field (useful for migrations), only for MySQL. [#3166](https://github.com/sequelize/sequelize/pull/3166)
- [FEATURE] There's a new sequelize.truncate function to truncate all tables defined through the sequelize models [#2671](https://github.com/sequelize/sequelize/pull/2671)
- [FEATURE] Add support for MySQLs TINYTEXT, MEDIUMTEXT and LONGTEXT. [#3836](https://github.com/sequelize/sequelize/pull/3836)
- [FEATURE] Provide warnings if you misuse data types. [#3839](https://github.com/sequelize/sequelize/pull/3839)
- [FIXED] Fix a case where Postgres arrays containing JSONB type was being generated as JSON type.
- [FIXED] Fix a case where `type` in `sequelize.query` was not being set to raw. [#3800](https://github.com/sequelize/sequelize/pull/3800)
- [FIXED] Fix an issue where include all was not being properly expanded for self-references [#3804](https://github.com/sequelize/sequelize/issues/3804)
- [FIXED] Fix instance.changed regression to not return false negatives for not changed null values [#3812](https://github.com/sequelize/sequelize/issues/3812)
- [FIXED] Fix isEmail validator to allow args: true [#3770](https://github.com/sequelize/sequelize/issues/3770)
- [FIXED] Fix some occasions where `options.logging` was not used correctly
- [FIXED] Fix `Model#destroy()` to correctly use `options.transaction`
- [FIXED] Fix `QueryInterface#showIndex()` to correctly pass on `options.transaction`
# 3.1.1
- [FIXED] Always quote aliases, even when quoteIdentifiers is false [#1589](https://github.com/sequelize/sequelize/issues/1589)
- [FIXED] No longer clones Instances in model finder options
- [FIXED] Fix regression in util.toDefaultValue not returning the data types [#3733](https://github.com/sequelize/sequelize/pull/3733)
# 3.1.0
- [ADDED] It is now possible to defer constraints in PostgreSQL by added a property `deferrable` to the `references` object of a field.
- [FIXED] Fix an issue with the build in isIP validator returning false negatives [#3756](https://github.com/sequelize/sequelize/pull/3756)
# 3.0.1
- [FIXED] `include.attributes = []` will no longer force the inclusion of the primary key, making it possible to write aggregates with includes.
- [CHANGED] The `references` property of model attributes has been transformed to an object: `{type: Sequelize.INTEGER, references: { model: SomeModel, key: 'some_key' }}`. The former format (`references` and `referecesKey`) still exists but is deprecated and will be removed in 4.0.
# 3.0.0
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.
- [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 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 / 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 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] `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
- [BUG] Fix app crash in sqlite while running in special unique constraint errors [#3730](https://github.com/sequelize/sequelize/pull/3730)
- [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.
- The error that is thrown when a column is declared to be an enum but without any values used to "Values for ENUM haven't been defined" and is now "Values for ENUM have not been defined".
# 2.1.3 # 2.1.3
- [BUG] Fix regression introduced in 2.1.2: updatedAt not set anymore [3667](https://github.com/sequelize/sequelize/pull/3667) - [BUG] Fix regression introduced in 2.1.2: updatedAt not set anymore [#3667](https://github.com/sequelize/sequelize/pull/3667)
- [BUG] Fix managed transactions not rolling back if no thenable was provided in the transaction block [3667](https://github.com/sequelize/sequelize/pull/3667) - [BUG] Fix managed transactions not rolling back if no thenable was provided in the transaction block [#3667](https://github.com/sequelize/sequelize/pull/3667)
# 2.1.2 # 2.1.2
- [BUG] `Model.create()/update()` no longer attempts to save undefined fields. - [BUG] `Model.create()/update()` no longer attempts to save undefined fields.
# 2.1.1 # 2.1.1
- [BUG] .get() now passes along options correctly when using a custom getter - [BUG] .get() now passes along options correctly when using a custom getter
- [BUG] Fix managed transactions not rolling back if an error occured the transaction block [3661](https://github.com/sequelize/sequelize/pull/3661) - [BUG] Fix managed transactions not rolling back if an error occured the transaction block [#3661](https://github.com/sequelize/sequelize/pull/3661)
- [BUG] Fix a node-webkit issue [3650](https://github.com/sequelize/sequelize/pull/3650) - [BUG] Fix a node-webkit issue [#3650](https://github.com/sequelize/sequelize/pull/3650)
- [FEATURE] Lock modes in Postgres now support `OF table` - [FEATURE] Lock modes in Postgres now support `OF table`
- [FEATURE] New transaction lock modes `FOR KEY SHARE` and `NO KEY UPDATE` for Postgres 9.3+ - [FEATURE] New transaction lock modes `FOR KEY SHARE` and `NO KEY UPDATE` for Postgres 9.3+
- [FEATURE/REFACTOR] Rewritten scopes with complete support for includes and scopes across associations - [FEATURE/REFACTOR] Rewritten scopes with complete support for includes and scopes across associations
......
<a name="mixin"></a> <a name="mixin"></a>
# Mixin Mixin # Mixin Mixin
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/associations/mixin.js#L95) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/associations/mixin.js#L95)
Creating assocations in sequelize is done by calling one of the belongsTo / hasOne / hasMany functions Creating assocations in sequelize is done by calling one of the belongsTo / hasOne / hasMany functions
on a model (the source), and prodiving another model as the first argument to the function (the target). on a model (the source), and providing another model as the first argument to the function (the target).
* hasOne - adds a foreign key to target * hasOne - adds a foreign key to target
* belongsTo - add a foreign key to source * belongsTo - add a foreign key to source
* hasMany - adds a foreign key to target, unless you also specifiy that target hasMany source, in which case a junction table is created with sourceId and targetId * hasMany - adds a foreign key to target, unless you also specify that target hasMany source, in which case a junction table is created with sourceId and targetId
Creating an association will add a foreign key constraint to the attributes. All associations use `CASCADE` on update and `SET NULL` on delete, except for n:m, which also uses `CASCADE` on delete. Creating an association will add a foreign key constraint to the attributes. All associations use `CASCADE` on update and `SET NULL` on delete, except for n:m, which also uses `CASCADE` on delete.
...@@ -89,7 +89,7 @@ you should either disable some constraints, or rethink your associations complet ...@@ -89,7 +89,7 @@ you should either disable some constraints, or rethink your associations complet
<a name="hasone"></a> <a name="hasone"></a>
## `hasOne(target, [options])` ## `hasOne(target, [options])`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/associations/mixin.js#L145) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/associations/mixin.js#L145)
Creates an association between this (the source) and the provided target. The foreign key is added on the target. Creates an association between this (the source) and the provided target. The foreign key is added on the target.
Example: `User.hasOne(Profile)`. This will add userId to the profile table. Example: `User.hasOne(Profile)`. This will add userId to the profile table.
...@@ -121,7 +121,7 @@ All methods return a promise ...@@ -121,7 +121,7 @@ All methods return a promise
<a name="belongsto"></a> <a name="belongsto"></a>
## `belongsTo(target, [options])` ## `belongsTo(target, [options])`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/associations/mixin.js#L170) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/associations/mixin.js#L170)
Creates an association between this (the source) and the provided target. The foreign key is added on the source. Creates an association between this (the source) and the provided target. The foreign key is added on the source.
Example: `Profile.belongsTo(User)`. This will add userId to the profile table. Example: `Profile.belongsTo(User)`. This will add userId to the profile table.
...@@ -153,7 +153,7 @@ All methods return a promise ...@@ -153,7 +153,7 @@ All methods return a promise
<a name="hasmany"></a> <a name="hasmany"></a>
## `hasMany(target, [options])` ## `hasMany(target, [options])`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/associations/mixin.js#L245) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/associations/mixin.js#L245)
Create an association that is either 1:m or n:m. Create an association that is either 1:m or n:m.
```js ```js
...@@ -192,7 +192,7 @@ Project.hasMany(User, { through: UserProjects }) ...@@ -192,7 +192,7 @@ Project.hasMany(User, { through: UserProjects })
``` ```
```js ```js
jan.addProject(homework, { started: false }) // The homework project is not started yet jan.addProject(homework, { started: false }) // The homework project is not started yet
jan.setProjects([makedinner, doshopping], { started: true}) // Both shopping and dinner has been started jan.setProjects([makedinner, doshopping], { started: true}) // Both shopping and dinner have been started
``` ```
If you want to set several target instances, but with different attributes you have to set the attributes on the instance, using a property with the name of the through model: If you want to set several target instances, but with different attributes you have to set the attributes on the instance, using a property with the name of the through model:
...@@ -236,7 +236,7 @@ user.getProjects().success(function (projects) { ...@@ -236,7 +236,7 @@ user.getProjects().success(function (projects) {
<a name="belongstomany"></a> <a name="belongstomany"></a>
## `belongsToMany(target, [options])` ## `belongsToMany(target, [options])`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/associations/mixin.js#L339) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/associations/mixin.js#L339)
Create an N:M association with a join table Create an N:M association with a join table
```js ```js
......
<a name="deferrable"></a>
## `Deferrable()` -> `object`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/deferrable.js#L39)
A collection of properties related to deferrable constraints. It can be used to
make foreign key constraints deferrable and to set the constaints within a
transaction. This is only supported in PostgreSQL.
The foreign keys can be configured like this. It will create a foreign key
that will check the constraints immediately when the data was inserted.
```js
sequelize.define('Model', {
foreign_id: {
type: Sequelize.INTEGER,
references: {
model: OtherModel,
key: 'id',
deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE
}
}
});
```
The constraints can be configured in a transaction like this. It will
trigger a query once the transaction has been started and set the constraints
to be checked at the very end of the transaction.
```js
sequelize.transaction({
deferrable: Sequelize.Deferrable.SET_DEFERRED
});
```
***
<a name="initially_deferred"></a>
## `INITIALLY_DEFERRED()`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/deferrable.js#L59)
A property that will defer constraints checks to the end of transactions.
***
<a name="initially_immediate"></a>
## `INITIALLY_IMMEDIATE()`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/deferrable.js#L76)
A property that will trigger the constraint checks immediately
***
<a name="not"></a>
## `NOT()`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/deferrable.js#L95)
A property that will set the constraints to not deferred. This is
the default in PostgreSQL and it make it impossible to dynamically
defer the constraints within a transaction.
***
<a name="set_deferred"></a>
## `SET_DEFERRED(constraints)`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/deferrable.js#L114)
A property that will trigger an additional query at the beginning of a
transaction which sets the constraints to deferred.
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| constraints | Array | An array of constraint names. Will defer all constraints by default. |
***
<a name="set_immediate"></a>
## `SET_IMMEDIATE(constraints)`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/deferrable.js#L135)
A property that will trigger an additional query at the beginning of a
transaction which sets the constraints to immediately.
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| constraints | Array | An array of constraint names. Will defer all constraints by default. |
***
_This document is automatically generated based on source code comments. Please do not edit it directly, as your changes will be ignored. Please write on <a href="irc://irc.freenode.net/#sequelizejs">IRC</a>, open an issue or a create a pull request if you feel something can be improved. For help on how to write source code documentation see [JSDoc](http://usejsdoc.org) and [dox](https://github.com/tj/dox)_
<a name="errors"></a> <a name="errors"></a>
# Class Errors # Class Errors
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L11) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L11)
Sequelize provides a host of custom error classes, to allow you to do easier debugging. All of these errors are exposed on the sequelize object and the sequelize constructor. Sequelize provides a host of custom error classes, to allow you to do easier debugging. All of these errors are exposed on the sequelize object and the sequelize constructor.
All sequelize errors inherit from the base JS error object. All sequelize errors inherit from the base JS error object.
...@@ -9,7 +9,7 @@ All sequelize errors inherit from the base JS error object. ...@@ -9,7 +9,7 @@ All sequelize errors inherit from the base JS error object.
<a name="baseerror"></a> <a name="baseerror"></a>
## `new BaseError()` ## `new BaseError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L20) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L20)
The Base Error all Sequelize Errors inherit from. The Base Error all Sequelize Errors inherit from.
__Aliases:__ Error __Aliases:__ Error
...@@ -18,7 +18,7 @@ __Aliases:__ Error ...@@ -18,7 +18,7 @@ __Aliases:__ Error
<a name="validationerror"></a> <a name="validationerror"></a>
## `new ValidationError(message, [errors])` ## `new ValidationError(message, [errors])`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L40) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L41)
Validation Error. Thrown when the sequelize validation has failed. The error contains an `errors` property, Validation Error. Thrown when the sequelize validation has failed. The error contains an `errors` property,
which is an array with 1 or more ValidationErrorItems, one for each validation that failed. which is an array with 1 or more ValidationErrorItems, one for each validation that failed.
...@@ -35,9 +35,16 @@ __Extends:__ BaseError ...@@ -35,9 +35,16 @@ __Extends:__ BaseError
*** ***
<a name="errors"></a>
## `errors`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L49)
An array of ValidationErrorItems
***
<a name="get"></a> <a name="get"></a>
## `get(path)` ## `get(path)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L65) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L70)
Gets all validation error items for the path / field specified. Gets all validation error items for the path / field specified.
...@@ -50,16 +57,9 @@ Gets all validation error items for the path / field specified. ...@@ -50,16 +57,9 @@ Gets all validation error items for the path / field specified.
*** ***
<a name="errors"></a>
## `errors()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L79)
An array of ValidationErrorItems
***
<a name="databaseerror"></a> <a name="databaseerror"></a>
## `new DatabaseError()` ## `new DatabaseError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L86) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L84)
A base class for all database related errors. A base class for all database related errors.
__Extends:__ BaseError __Extends:__ BaseError
...@@ -67,22 +67,50 @@ __Extends:__ BaseError ...@@ -67,22 +67,50 @@ __Extends:__ BaseError
*** ***
<a name="parent"></a> <a name="parent"></a>
## `parent()` ## `parent`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L101) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L92)
The database specific error which triggered this one The database specific error which triggered this one
*** ***
<a name="sql"></a> <a name="sql"></a>
## `sql()` ## `sql`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L107) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L98)
The SQL that triggered the error The SQL that triggered the error
*** ***
<a name="message"></a>
## `message()`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L104)
The message from the DB.
***
<a name="fields"></a>
## `fields()`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L109)
The fields of the unique constraint
***
<a name="value"></a>
## `value()`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L114)
The value(s) which triggered the error
***
<a name="index"></a>
## `index()`
[View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L119)
The name of the index that triggered the error
***
<a name="timeouterror"></a> <a name="timeouterror"></a>
## `new TimeoutError()` ## `new TimeoutError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L114) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L127)
Thrown when a database query times out because of a deadlock Thrown when a database query times out because of a deadlock
__Extends:__ DatabaseError __Extends:__ DatabaseError
...@@ -91,7 +119,7 @@ __Extends:__ DatabaseError ...@@ -91,7 +119,7 @@ __Extends:__ DatabaseError
<a name="uniqueconstrainterror"></a> <a name="uniqueconstrainterror"></a>
## `new UniqueConstraintError()` ## `new UniqueConstraintError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L125) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L138)
Thrown when a unique constraint is violated in the database Thrown when a unique constraint is violated in the database
__Extends:__ DatabaseError __Extends:__ DatabaseError
...@@ -100,7 +128,7 @@ __Extends:__ DatabaseError ...@@ -100,7 +128,7 @@ __Extends:__ DatabaseError
<a name="foreignkeyconstrainterror"></a> <a name="foreignkeyconstrainterror"></a>
## `new ForeignKeyConstraintError()` ## `new ForeignKeyConstraintError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L144) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L157)
Thrown when a foreign key constraint is violated in the database Thrown when a foreign key constraint is violated in the database
__Extends:__ DatabaseError __Extends:__ DatabaseError
...@@ -109,44 +137,16 @@ __Extends:__ DatabaseError ...@@ -109,44 +137,16 @@ __Extends:__ DatabaseError
<a name="exclusionconstrainterror"></a> <a name="exclusionconstrainterror"></a>
## `new ExclusionConstraintError()` ## `new ExclusionConstraintError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L164) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L177)
Thrown when an exclusion constraint is violated in the database Thrown when an exclusion constraint is violated in the database
__Extends:__ DatabaseError __Extends:__ DatabaseError
*** ***
<a name="message"></a>
## `message()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L183)
The message from the DB.
***
<a name="fields"></a>
## `fields()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L189)
The fields of the unique constraint
***
<a name="value"></a>
## `value()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L195)
The value(s) which triggered the error
***
<a name="index"></a>
## `index()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L201)
The name of the index that triggered the error
***
<a name="validationerroritem"></a> <a name="validationerroritem"></a>
## `new ValidationErrorItem(message, type, path, value)` ## `new ValidationErrorItem(message, type, path, value)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L213) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L201)
Validation Error Item Validation Error Item
Instances of this class are included in the `ValidationError.errors` property. Instances of this class are included in the `ValidationError.errors` property.
...@@ -165,7 +165,7 @@ Instances of this class are included in the `ValidationError.errors` property. ...@@ -165,7 +165,7 @@ Instances of this class are included in the `ValidationError.errors` property.
<a name="connectionerror"></a> <a name="connectionerror"></a>
## `new ConnectionError()` ## `new ConnectionError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L225) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L213)
A base class for all connection related errors. A base class for all connection related errors.
__Extends:__ BaseError __Extends:__ BaseError
...@@ -173,15 +173,15 @@ __Extends:__ BaseError ...@@ -173,15 +173,15 @@ __Extends:__ BaseError
*** ***
<a name="parent"></a> <a name="parent"></a>
## `parent()` ## `parent`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L239) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L220)
The connection specific error which triggered this one The connection specific error which triggered this one
*** ***
<a name="connectionrefusederror"></a> <a name="connectionrefusederror"></a>
## `new ConnectionRefusedError()` ## `new ConnectionRefusedError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L246) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L230)
Thrown when a connection to a database is refused Thrown when a connection to a database is refused
__Extends:__ ConnectionError __Extends:__ ConnectionError
...@@ -190,7 +190,7 @@ __Extends:__ ConnectionError ...@@ -190,7 +190,7 @@ __Extends:__ ConnectionError
<a name="accessdeniederror"></a> <a name="accessdeniederror"></a>
## `new AccessDeniedError()` ## `new AccessDeniedError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L257) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L241)
Thrown when a connection to a database is refused due to insufficient privileges Thrown when a connection to a database is refused due to insufficient privileges
__Extends:__ ConnectionError __Extends:__ ConnectionError
...@@ -199,7 +199,7 @@ __Extends:__ ConnectionError ...@@ -199,7 +199,7 @@ __Extends:__ ConnectionError
<a name="hostnotfounderror"></a> <a name="hostnotfounderror"></a>
## `new HostNotFoundError()` ## `new HostNotFoundError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L268) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L252)
Thrown when a connection to a database has a hostname that was not found Thrown when a connection to a database has a hostname that was not found
__Extends:__ ConnectionError __Extends:__ ConnectionError
...@@ -208,7 +208,7 @@ __Extends:__ ConnectionError ...@@ -208,7 +208,7 @@ __Extends:__ ConnectionError
<a name="hostnotreachableerror"></a> <a name="hostnotreachableerror"></a>
## `new HostNotReachableError()` ## `new HostNotReachableError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L279) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L263)
Thrown when a connection to a database has a hostname that was not reachable Thrown when a connection to a database has a hostname that was not reachable
__Extends:__ ConnectionError __Extends:__ ConnectionError
...@@ -217,7 +217,7 @@ __Extends:__ ConnectionError ...@@ -217,7 +217,7 @@ __Extends:__ ConnectionError
<a name="invalidconnectionerror"></a> <a name="invalidconnectionerror"></a>
## `new InvalidConnectionError()` ## `new InvalidConnectionError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L290) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L274)
Thrown when a connection to a database has invalid values for any of the connection parameters Thrown when a connection to a database has invalid values for any of the connection parameters
__Extends:__ ConnectionError __Extends:__ ConnectionError
...@@ -226,7 +226,7 @@ __Extends:__ ConnectionError ...@@ -226,7 +226,7 @@ __Extends:__ ConnectionError
<a name="connectiontimedouterror"></a> <a name="connectiontimedouterror"></a>
## `new ConnectionTimedOutError()` ## `new ConnectionTimedOutError()`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/errors.js#L301) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/errors.js#L285)
Thrown when a connection to a database times out Thrown when a connection to a database times out
__Extends:__ ConnectionError __Extends:__ ConnectionError
......
<a name="hooks"></a> <a name="hooks"></a>
# Mixin Hooks # Mixin Hooks
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L39) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L39)
Hooks are function that are called before and after (bulk-) creation/updating/deletion and validation. Hooks can be added to you models in three ways: Hooks are function that are called before and after (bulk-) creation/updating/deletion and validation. Hooks can be added to you models in three ways:
1. By specifying them as options in `sequelize.define` 1. By specifying them as options in `sequelize.define`
...@@ -38,7 +38,7 @@ Model.afterBulkUpdate(function () {}) ...@@ -38,7 +38,7 @@ Model.afterBulkUpdate(function () {})
<a name="addhook"></a> <a name="addhook"></a>
## `addHook(hooktype, [name], fn)` ## `addHook(hooktype, [name], fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L152) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L152)
Add a hook to the model Add a hook to the model
...@@ -56,7 +56,7 @@ __Aliases:__ hook ...@@ -56,7 +56,7 @@ __Aliases:__ hook
<a name="hashook"></a> <a name="hashook"></a>
## `hasHook(hookType)` ## `hasHook(hookType)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L172) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L172)
Check whether the mode has any hooks of this type Check whether the mode has any hooks of this type
...@@ -72,7 +72,7 @@ __Aliases:__ hasHooks ...@@ -72,7 +72,7 @@ __Aliases:__ hasHooks
<a name="beforevalidate"></a> <a name="beforevalidate"></a>
## `beforeValidate(name, fn)` ## `beforeValidate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L185) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L185)
A hook that is run before validation A hook that is run before validation
**Params:** **Params:**
...@@ -87,7 +87,7 @@ A hook that is run before validation ...@@ -87,7 +87,7 @@ A hook that is run before validation
<a name="aftervalidate"></a> <a name="aftervalidate"></a>
## `afterValidate(name, fn)` ## `afterValidate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L192) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L192)
A hook that is run after validation A hook that is run after validation
**Params:** **Params:**
...@@ -102,7 +102,7 @@ A hook that is run after validation ...@@ -102,7 +102,7 @@ A hook that is run after validation
<a name="beforecreate"></a> <a name="beforecreate"></a>
## `beforeCreate(name, fn)` ## `beforeCreate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L199) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L199)
A hook that is run before creating a single instance A hook that is run before creating a single instance
**Params:** **Params:**
...@@ -117,7 +117,7 @@ A hook that is run before creating a single instance ...@@ -117,7 +117,7 @@ A hook that is run before creating a single instance
<a name="aftercreate"></a> <a name="aftercreate"></a>
## `afterCreate(name, fn)` ## `afterCreate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L206) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L206)
A hook that is run after creating a single instance A hook that is run after creating a single instance
**Params:** **Params:**
...@@ -132,7 +132,7 @@ A hook that is run after creating a single instance ...@@ -132,7 +132,7 @@ A hook that is run after creating a single instance
<a name="beforedestroy"></a> <a name="beforedestroy"></a>
## `beforeDestroy(name, fn)` ## `beforeDestroy(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L215) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L215)
A hook that is run before destroying a single instance A hook that is run before destroying a single instance
**Params:** **Params:**
...@@ -148,7 +148,7 @@ __Aliases:__ beforeDelete ...@@ -148,7 +148,7 @@ __Aliases:__ beforeDelete
<a name="afterdestroy"></a> <a name="afterdestroy"></a>
## `afterDestroy(name, fn)` ## `afterDestroy(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L224) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L224)
A hook that is run after destroying a single instance A hook that is run after destroying a single instance
**Params:** **Params:**
...@@ -164,7 +164,7 @@ __Aliases:__ afterDelete ...@@ -164,7 +164,7 @@ __Aliases:__ afterDelete
<a name="beforeupdate"></a> <a name="beforeupdate"></a>
## `beforeUpdate(name, fn)` ## `beforeUpdate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L231) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L231)
A hook that is run before updating a single instance A hook that is run before updating a single instance
**Params:** **Params:**
...@@ -179,7 +179,7 @@ A hook that is run before updating a single instance ...@@ -179,7 +179,7 @@ A hook that is run before updating a single instance
<a name="afterupdate"></a> <a name="afterupdate"></a>
## `afterUpdate(name, fn)` ## `afterUpdate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L238) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L238)
A hook that is run after updating a single instance A hook that is run after updating a single instance
**Params:** **Params:**
...@@ -194,7 +194,7 @@ A hook that is run after updating a single instance ...@@ -194,7 +194,7 @@ A hook that is run after updating a single instance
<a name="beforebulkcreate"></a> <a name="beforebulkcreate"></a>
## `beforeBulkCreate(name, fn)` ## `beforeBulkCreate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L245) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L245)
A hook that is run before creating instances in bulk A hook that is run before creating instances in bulk
**Params:** **Params:**
...@@ -209,7 +209,7 @@ A hook that is run before creating instances in bulk ...@@ -209,7 +209,7 @@ A hook that is run before creating instances in bulk
<a name="afterbulkcreate"></a> <a name="afterbulkcreate"></a>
## `afterBulkCreate(name, fn)` ## `afterBulkCreate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L252) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L252)
A hook that is run after creating instances in bulk A hook that is run after creating instances in bulk
**Params:** **Params:**
...@@ -224,7 +224,7 @@ A hook that is run after creating instances in bulk ...@@ -224,7 +224,7 @@ A hook that is run after creating instances in bulk
<a name="beforebulkdestroy"></a> <a name="beforebulkdestroy"></a>
## `beforeBulkDestroy(name, fn)` ## `beforeBulkDestroy(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L261) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L261)
A hook that is run before destroying instances in bulk A hook that is run before destroying instances in bulk
**Params:** **Params:**
...@@ -240,7 +240,7 @@ __Aliases:__ beforeBulkDelete ...@@ -240,7 +240,7 @@ __Aliases:__ beforeBulkDelete
<a name="afterbulkdestroy"></a> <a name="afterbulkdestroy"></a>
## `afterBulkDestroy(name, fn)` ## `afterBulkDestroy(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L270) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L270)
A hook that is run after destroying instances in bulk A hook that is run after destroying instances in bulk
**Params:** **Params:**
...@@ -256,7 +256,7 @@ __Aliases:__ afterBulkDelete ...@@ -256,7 +256,7 @@ __Aliases:__ afterBulkDelete
<a name="beforebulkupdate"></a> <a name="beforebulkupdate"></a>
## `beforeBulkUpdate(name, fn)` ## `beforeBulkUpdate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L277) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L277)
A hook that is run after updating instances in bulk A hook that is run after updating instances in bulk
**Params:** **Params:**
...@@ -271,7 +271,7 @@ A hook that is run after updating instances in bulk ...@@ -271,7 +271,7 @@ A hook that is run after updating instances in bulk
<a name="afterbulkupdate"></a> <a name="afterbulkupdate"></a>
## `afterBulkUpdate(name, fn)` ## `afterBulkUpdate(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L284) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L284)
A hook that is run after updating instances in bulk A hook that is run after updating instances in bulk
**Params:** **Params:**
...@@ -286,7 +286,7 @@ A hook that is run after updating instances in bulk ...@@ -286,7 +286,7 @@ A hook that is run after updating instances in bulk
<a name="beforefind"></a> <a name="beforefind"></a>
## `beforeFind(name, fn)` ## `beforeFind(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L291) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L291)
A hook that is run before a find (select) query A hook that is run before a find (select) query
**Params:** **Params:**
...@@ -301,7 +301,7 @@ A hook that is run before a find (select) query ...@@ -301,7 +301,7 @@ A hook that is run before a find (select) query
<a name="beforefindafterexpandincludeall"></a> <a name="beforefindafterexpandincludeall"></a>
## `beforeFindAfterExpandIncludeAll(name, fn)` ## `beforeFindAfterExpandIncludeAll(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L298) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L298)
A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded
**Params:** **Params:**
...@@ -316,7 +316,7 @@ A hook that is run before a find (select) query, after any { include: {all: ...} ...@@ -316,7 +316,7 @@ A hook that is run before a find (select) query, after any { include: {all: ...}
<a name="beforefindafteroptions"></a> <a name="beforefindafteroptions"></a>
## `beforeFindAfterOptions(name, fn)` ## `beforeFindAfterOptions(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L305) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L305)
A hook that is run before a find (select) query, after all option parsing is complete A hook that is run before a find (select) query, after all option parsing is complete
**Params:** **Params:**
...@@ -331,7 +331,7 @@ A hook that is run before a find (select) query, after all option parsing is com ...@@ -331,7 +331,7 @@ A hook that is run before a find (select) query, after all option parsing is com
<a name="afterfind"></a> <a name="afterfind"></a>
## `afterFind(name, fn)` ## `afterFind(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L312) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L312)
A hook that is run after a find (select) query A hook that is run after a find (select) query
**Params:** **Params:**
...@@ -346,7 +346,7 @@ A hook that is run after a find (select) query ...@@ -346,7 +346,7 @@ A hook that is run after a find (select) query
<a name="beforedefine"></a> <a name="beforedefine"></a>
## `beforeDefine(name, fn)` ## `beforeDefine(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L319) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L319)
A hook that is run before a define call A hook that is run before a define call
**Params:** **Params:**
...@@ -361,7 +361,7 @@ A hook that is run before a define call ...@@ -361,7 +361,7 @@ A hook that is run before a define call
<a name="afterdefine"></a> <a name="afterdefine"></a>
## `afterDefine(name, fn)` ## `afterDefine(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L326) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L326)
A hook that is run after a define call A hook that is run after a define call
**Params:** **Params:**
...@@ -376,7 +376,7 @@ A hook that is run after a define call ...@@ -376,7 +376,7 @@ A hook that is run after a define call
<a name="beforeinit"></a> <a name="beforeinit"></a>
## `beforeInit(name, fn)` ## `beforeInit(name, fn)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L333) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L333)
A hook that is run before Sequelize() call A hook that is run before Sequelize() call
**Params:** **Params:**
...@@ -391,7 +391,7 @@ A hook that is run before Sequelize() call ...@@ -391,7 +391,7 @@ A hook that is run before Sequelize() call
<a name="afterinit"></a> <a name="afterinit"></a>
## `afterInit` ## `afterInit`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/hooks.js#L341) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/hooks.js#L341)
A hook that is run after Sequelize() call A hook that is run after Sequelize() call
**Params:** **Params:**
......
<a name="promise"></a>
# Class Promise
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/promise.js#L25)
A slightly modified version of bluebird promises. This means that, on top of the methods below, you can also call all the methods listed on the link below.
The main difference is that sequelize promises allows you to attach a listener that will be called with the generated SQL, each time a query is run.
The sequelize promise class works seamlessly with other A+/thenable libraries, with one exception.
If you want to propagate SQL events across `then`, `all` calls etc., you must use sequelize promises exclusively.
### Mixes:
* https://github.com/petkaantonov/bluebird/blob/master/API.md
***
<a name="on"></a>
## `on(evt, fct)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/promise.js#L119)
Listen for events, event emitter style. Mostly for backwards compat. with EventEmitter
**Deprecated**
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| evt | String | |
| fct | Function | |
***
<a name="emit"></a>
## `emit(type, value(s)*)`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/promise.js#L140)
Emit an event from the emitter
**Deprecated**
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| type | string | The type of event |
| value(s)* | any | All other arguments will be passed to the event listeners |
***
<a name="success"></a>
## `success(onSuccess)` -> `this`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/promise.js#L178)
Listen for success events.
```js
promise.success(function (result) {
//...
});
```
**Deprecated**
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| onSuccess | function | |
__Aliases:__ ok
***
<a name="error"></a>
## `error(onError)` -> `this`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/promise.js#L205)
Listen for error events
```js
promise.error(function (err) {
//...
});
```
**Deprecated**
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| onError | function | |
__Aliases:__ fail, failure
***
<a name="done"></a>
## `done(onDone)` -> `this`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/promise.js#L228)
Listen for both success and error events.
```js
promise.done(function (err, result) {
//...
});
```
**Deprecated** done(fct) is deprecated, done() is regular promise syntax to explicitly signal the end of a promise chain and will not be deprecated
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| onDone | function | |
__Aliases:__ complete
***
<a name="sql"></a>
## `sql(onSQL)` -> `this`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/promise.js#L253)
Attach a function that is called every time the function that created this emitter executes a query.
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| onSQL | function | |
***
<a name="proxy"></a>
## `proxy(promise, [options])` -> `this`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/promise.js#L268)
Proxy every event of this promise to another one.
**Deprecated**
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| promise | SequelizePromise | The promise that should receive the events. |
| [options] | Object | |
| [options.events] | Array | An array of the events to proxy. Defaults to sql, error and success |
***
_This document is automatically generated based on source code comments. Please do not edit it directly, as your changes will be ignored. Please write on <a href="irc://irc.freenode.net/#sequelizejs">IRC</a>, open an issue or a create a pull request if you feel something can be improved. For help on how to write source code documentation see [JSDoc](http://usejsdoc.org) and [dox](https://github.com/tj/dox)_
\ No newline at end of file
<a name="transaction"></a> <a name="transaction"></a>
# Class Transaction # Class Transaction
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/transaction.js#L12) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/transaction.js#L18)
The transaction object is used to identify a running transaction. It is created by calling `Sequelize.transaction()`. The transaction object is used to identify a running transaction. It is created by calling `Sequelize.transaction()`.
To run a query under a transaction, you should pass the transaction in the options object. To run a query under a transaction, you should pass the transaction in the options object.
**Params:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| sequelize | Sequelize | A configured sequelize Instance |
| options | Object | An object with options |
| options.autocommit=true | Boolean | Sets the autocommit property of the transaction. |
| options.isolationLevel=true | String | Sets the isolation level of the transaction. |
| options.deferrable | String | Sets the constraints to be deferred or immediately checked. |
*** ***
<a name="isolation_levels"></a> <a name="isolation_levels"></a>
## `ISOLATION_LEVELS` ## `ISOLATION_LEVELS`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/transaction.js#L45) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/transaction.js#L53)
The possible isolations levels to use when starting a transaction The possible isolations levels to use when starting a transaction.
Can be set per-transaction by passing `options.isolationLevel` to `sequelize.transaction`.
Default to `REPEATABLE_READ` but you can override the default isolation level by passing `options.isolationLevel` in `new Sequelize`.
```js ```js
{ {
...@@ -26,43 +39,47 @@ The possible isolations levels to use when starting a transaction ...@@ -26,43 +39,47 @@ The possible isolations levels to use when starting a transaction
<a name="lock"></a> <a name="lock"></a>
## `LOCK` ## `LOCK`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/transaction.js#L67) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/transaction.js#L97)
Possible options for row locking. Used in conjuction with `find` calls: Possible options for row locking. Used in conjuction with `find` calls:
```js ```js
t1 // is a transaction t1 // is a transaction
t1.LOCK.UPDATE,
t1.LOCK.SHARE,
t1.LOCK.KEY_SHARE, // Postgres 9.3+ only
t1.LOCK.NO_KEY_UPDATE // Postgres 9.3+ only
```
Usage:
```js
t1 // is a transaction
Model.findAll({ Model.findAll({
where: ... where: ...,
}, {
transaction: t1, transaction: t1,
lock: t1.LOCK.UPDATE, lock: t1.LOCK...
lock: t1.LOCK.SHARE, });
lock: t1.LOCK.KEY_SHARE, // Postgres 9.3+ only
lock: t1.LOCK.NO_KEY_SHARE // Postgres 9.3+ only
})
``` ```
Postgres also supports specific locks while eager loading by using `OF`. Postgres also supports specific locks while eager loading by using OF:
```js ```js
UserModel.findAll({ UserModel.findAll({
where: ..., where: ...,
include: [TaskModel, ...] include: [TaskModel, ...],
}, {
transaction: t1, transaction: t1,
lock: { lock: {
level: t1.LOCK..., level: t1.LOCK...,
of: UserModel of: UserModel
} }
}) });
``` ```
UserModel will be locked but TaskModel won't! UserModel will be locked but TaskModel won't!
*** ***
<a name="commit"></a> <a name="commit"></a>
## `commit()` -> `this` ## `commit()` -> `this`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/transaction.js#L77) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/transaction.js#L109)
Commit the transaction Commit the transaction
...@@ -70,7 +87,7 @@ Commit the transaction ...@@ -70,7 +87,7 @@ Commit the transaction
<a name="rollback"></a> <a name="rollback"></a>
## `rollback()` -> `this` ## `rollback()` -> `this`
[View code](https://github.com/sequelize/sequelize/blob/421f0f34356a3484b0f26e92e7fd133af6d3f6db/lib/transaction.js#L98) [View code](https://github.com/sequelize/sequelize/blob/cc8687539fe96f7f64887a04ddf5d48f159f5e92/lib/transaction.js#L130)
Rollback (abort) the transaction Rollback (abort) the transaction
......
...@@ -23,12 +23,11 @@ if (program.file) { ...@@ -23,12 +23,11 @@ if (program.file) {
{file:'lib/sequelize.js', output: 'sequelize'}, {file:'lib/sequelize.js', output: 'sequelize'},
{file:'lib/instance.js', output: 'instance'}, {file:'lib/instance.js', output: 'instance'},
{file:'lib/model.js', output: 'model'}, {file:'lib/model.js', output: 'model'},
{file:'lib/querying.js', output: 'querying'},
{file:'lib/hooks.js', output: 'hooks'}, {file:'lib/hooks.js', output: 'hooks'},
{file:'lib/associations/mixin.js', output: 'associations'}, {file:'lib/associations/mixin.js', output: 'associations'},
{file:'lib/promise.js', output: 'promise'},
{file:'lib/transaction.js', output: 'transaction'}, {file:'lib/transaction.js', output: 'transaction'},
{file:'lib/data-types.js', output: 'datatypes'} {file:'lib/data-types.js', output: 'datatypes'},
{file:'lib/deferrable.js', output: 'deferrable'}
]; ];
} }
...@@ -53,29 +52,14 @@ Comment.prototype.hasTag = function(tagName) { ...@@ -53,29 +52,14 @@ Comment.prototype.hasTag = function(tagName) {
}; };
Comment.prototype.getName = function () { Comment.prototype.getName = function () {
var tag; var tag = (['name', 'class', 'property', 'method']).reduce(function (tag, tagName) {
return tag || this.getTag(tagName);
tag = this.getTag('name'); }.bind(this), null);
if (tag) { if (tag) {
return tag.string; return tag.string;
} }
tag = this.getTag('class');
if (tag) {
return tag.string;
}
tag = this.getTag('property');
if (tag) {
return tag.types[0];
}
tag = this.getTag('method');
if (tag) {
return tag.string;
}
return this.data.ctx.name; return this.data.ctx.name;
}; };
...@@ -281,6 +265,7 @@ new git.Repo(path.dirname(__filename) + '/..', function (err, repo) { ...@@ -281,6 +265,7 @@ new git.Repo(path.dirname(__filename) + '/..', function (err, repo) {
fs.readFile(file.file, function (err, code) { fs.readFile(file.file, function (err, code) {
obj = dox.parseComments(code.toString(), { raw: true}); obj = dox.parseComments(code.toString(), { raw: true});
path = program.out + '/' + file.output + '.md'; path = program.out + '/' + file.output + '.md';
console.log(path) console.log(path)
var output = parseComments(obj, file.file); var output = parseComments(obj, file.file);
......
...@@ -143,10 +143,10 @@ Defining `through` is required. Sequelize would previously attempt to autogenera ...@@ -143,10 +143,10 @@ 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`. 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: 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. We will also manually define the foreign keys to use:
```js ```js
User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks' }) User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId' })
Project.belongsToMany(User, { as: 'Workers', through: 'worker_tasks' }) Project.belongsToMany(User, { as: 'Workers', through: 'worker_tasks', foreignKey: 'projectId' })
``` ```
Of course you can also define self references with belongsToMany: Of course you can also define self references with belongsToMany:
...@@ -211,39 +211,43 @@ this.Comment = this.sequelize.define('comment', { ...@@ -211,39 +211,43 @@ this.Comment = this.sequelize.define('comment', {
this.Post.hasMany(this.Comment, { this.Post.hasMany(this.Comment, {
foreignKey: 'commentable_id', foreignKey: 'commentable_id',
constraints: false,
scope: { scope: {
commentable: 'post' commentable: 'post'
} }
}); });
this.Comment.belongsTo(this.Post, { this.Comment.belongsTo(this.Post, {
foreignKey: 'commentable_id', foreignKey: 'commentable_id',
constraints: false,
as: 'post' as: 'post'
}); });
this.Image.hasMany(this.Comment, { this.Image.hasMany(this.Comment, {
foreignKey: 'commentable_id', foreignKey: 'commentable_id',
constraints: false,
scope: { scope: {
commentable: 'image' commentable: 'image'
} }
}); });
this.Comment.belongsTo(this.Image, { this.Comment.belongsTo(this.Image, {
foreignKey: 'commentable_id', foreignKey: 'commentable_id',
constraints: false,
as: 'image' as: 'image'
}); });
``` ```
Note that the Image -> Comment and Post -> Comment relations define a scope, `commentable: 'image'` and `commentable: 'post'` respectively. This scope is automatically applied when using the association functions: `constraints: false,` disables references constraints - since the `commentable_id` column references several tables, we cannot add a `REFERENCES` constraint to it. Note that the Image -> Comment and Post -> Comment relations define a scope, `commentable: 'image'` and `commentable: 'post'` respectively. This scope is automatically applied when using the association functions:
```js ```js
Image.getComments() image.getComments()
SELECT * FROM comments WHERE commentable_id = 42 AND commentable = 'image'; SELECT * FROM comments WHERE commentable_id = 42 AND commentable = 'image';
Image.createComment({ image.createComment({
title: 'Awesome!' title: 'Awesome!'
}) })
INSERT INTO comments (title, commentable_id, commentable) VALUES ('Awesome!', 'image', 42); INSERT INTO comments (title, commentable_id, commentable) VALUES ('Awesome!', 'image', 42);
Image.addComment(comment); image.addComment(comment);
UPDATE comments SET commentable_id = 42, commentable = 'image' UPDATE comments SET commentable_id = 42, commentable = 'image'
``` ```
...@@ -586,8 +590,10 @@ Series = sequelize.define('Series', { ...@@ -586,8 +590,10 @@ Series = sequelize.define('Series', {
// Set FK relationship (hasMany) with `Trainer` // Set FK relationship (hasMany) with `Trainer`
trainer_id: { trainer_id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
references: "Trainers", references: {
referencesKey: "id" model: "Trainers",
key: "id"
}
} }
}) })
   
...@@ -605,8 +611,10 @@ Video = sequelize.define('Video', { ...@@ -605,8 +611,10 @@ Video = sequelize.define('Video', {
// set relationship (hasOne) with `Series` // set relationship (hasOne) with `Series`
series_id: { series_id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
references: Series, // Can be both a string representing the table name, or a reference to the model references: {
referencesKey: "id" model: Series, // Can be both a string representing the table name, or a reference to the model
key: "id"
}
} }
}); });
   
......
...@@ -7,8 +7,7 @@ $ npm install --save sequelize ...@@ -7,8 +7,7 @@ $ npm install --save sequelize
# And one of the following: # And one of the following:
$ npm install --save pg pg-hstore $ npm install --save pg pg-hstore
$ npm install --save mysql $ npm install --save mysql // For both mysql and mariadb dialects
$ npm install --save mariasql
$ npm install --save sqlite3 $ npm install --save sqlite3
$ npm install --save tedious // MSSQL $ npm install --save tedious // MSSQL
``` ```
......
...@@ -115,7 +115,7 @@ User.beforeCreate(function(user) { ...@@ -115,7 +115,7 @@ User.beforeCreate(function(user) {
}) })
``` ```
This example will emit an error: This example will return an error:
```js ```js
User.create({username: 'Not a Boss', accessLevel: 20}).catch(function(err) { User.create({username: 'Not a Boss', accessLevel: 20}).catch(function(err) {
...@@ -123,7 +123,7 @@ User.create({username: 'Not a Boss', accessLevel: 20}).catch(function(err) { ...@@ -123,7 +123,7 @@ User.create({username: 'Not a Boss', accessLevel: 20}).catch(function(err) {
}) })
``` ```
The following example would emit a success event: The following example would return successful:
```js ```js
User.create({username: 'Boss', accessLevel: 20}).then(function(user) { User.create({username: 'Boss', accessLevel: 20}).then(function(user) {
...@@ -254,8 +254,7 @@ User.hook('afterCreate', function(user, options) { ...@@ -254,8 +254,7 @@ User.hook('afterCreate', function(user, options) {
sequelize.transaction(function(t) { sequelize.transaction(function(t) {
User.create({ User.create({
username: 'someguy', username: 'someguy',
mood: 'happy' mood: 'happy',
}, {
transaction: t transaction: t
}); });
}); });
......
...@@ -274,7 +274,7 @@ Person.create({ ...@@ -274,7 +274,7 @@ Person.create({
If you need to get your instance in sync&comma; you can use the method`reload`&period; It will fetch the current data from the database and overwrite the attributes of the model on which the method has been called on&period; If you need to get your instance in sync&comma; you can use the method`reload`&period; It will fetch the current data from the database and overwrite the attributes of the model on which the method has been called on&period;
```js ```js
Person.find({ where: { name: 'john' } }).then(function(person) { Person.findOne({ where: { name: 'john' } }).then(function(person) {
person.name = 'jane' person.name = 'jane'
console.log(person.name) // 'jane' console.log(person.name) // 'jane'
   
...@@ -291,23 +291,23 @@ In order to increment values of an instance without running into concurrency iss ...@@ -291,23 +291,23 @@ In order to increment values of an instance without running into concurrency iss
First of all you can define a field and the value you want to add to it&period; First of all you can define a field and the value you want to add to it&period;
```js ```js
User.find(1).then(function(user) { User.findById(1).then(function(user) {
user.increment('my-integer-field', 2).then(/* ... */) user.increment('my-integer-field', {by: 2}).then(/* ... */)
}) })
``` ```
Second&comma; you can define multiple fields and the value you want to add to them&period; Second&comma; you can define multiple fields and the value you want to add to them&period;
```js ```js
User.find(1).then(function(user) { User.findById(1).then(function(user) {
user.increment([ 'my-integer-field', 'my-very-other-field' ], 2).then(/* ... */) user.increment([ 'my-integer-field', 'my-very-other-field' ], {by: 2}).then(/* ... */)
}) })
``` ```
Third&comma; you can define an object containing fields and its increment values&period; Third&comma; you can define an object containing fields and its increment values&period;
```js ```js
User.find(1).then(function(user) { User.findById(1).then(function(user) {
user.increment({ user.increment({
'my-integer-field': 2, 'my-integer-field': 2,
'my-very-other-field': 3 'my-very-other-field': 3
...@@ -322,23 +322,23 @@ In order to decrement values of an instance without running into concurrency iss ...@@ -322,23 +322,23 @@ In order to decrement values of an instance without running into concurrency iss
First of all you can define a field and the value you want to add to it&period; First of all you can define a field and the value you want to add to it&period;
```js ```js
User.find(1).then(function(user) { User.findById(1).then(function(user) {
user.decrement('my-integer-field', 2).then(/* ... */) user.decrement('my-integer-field', {by: 2}).then(/* ... */)
}) })
``` ```
Second&comma; you can define multiple fields and the value you want to add to them&period; Second&comma; you can define multiple fields and the value you want to add to them&period;
```js ```js
User.find(1).then(function(user) { User.findById(1).then(function(user) {
user.decrement([ 'my-integer-field', 'my-very-other-field' ], 2).then(/* ... */) user.decrement([ 'my-integer-field', 'my-very-other-field' ], {by: 2}).then(/* ... */)
}) })
``` ```
Third&comma; you can define an object containing fields and its decrement values&period; Third&comma; you can define an object containing fields and its decrement values&period;
```js ```js
User.find(1).then(function(user) { User.findById(1).then(function(user) {
user.decrement({ user.decrement({
'my-integer-field': 2, 'my-integer-field': 2,
'my-very-other-field': 3 'my-very-other-field': 3
......
...@@ -55,14 +55,12 @@ The following skeleton shows a typical migration file&period; All migrations are ...@@ -55,14 +55,12 @@ The following skeleton shows a typical migration file&period; All migrations are
```js ```js
module.exports = { module.exports = {
up: function(migration, DataTypes, done) { up: function(migration, DataTypes) {
// logic for transforming into the new state // 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 // 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&period; The `Da ...@@ -71,17 +69,8 @@ The passed `migration` object can be used to modify the database&period; The `Da
```js ```js
module.exports = { module.exports = {
up: function(migration, DataTypes, done) { up: function(migration, DataTypes) {
migration.dropAllTables().complete(done) return migration.dropAllTables();
 
// equals:
migration.dropAllTables().complete(function(err) {
if (err) {
done(err)
} else {
done(null)
}
})
} }
} }
``` ```
...@@ -155,7 +144,7 @@ migration.renameTable('Person', 'User') ...@@ -155,7 +144,7 @@ migration.renameTable('Person', 'User')
This method returns the name of all existing tables in the database&period; This method returns the name of all existing tables in the database&period;
```js ```js
migration.showAllTables().success(function(tableNames) {}) migration.showAllTables().then(function(tableNames) {})
``` ```
### describeTable&lpar;tableName&comma; options&rpar; ### describeTable&lpar;tableName&comma; options&rpar;
...@@ -163,7 +152,7 @@ migration.showAllTables().success(function(tableNames) {}) ...@@ -163,7 +152,7 @@ migration.showAllTables().success(function(tableNames) {})
This method returns an array of hashes containing information about all attributes in the table&period; This method returns an array of hashes containing information about all attributes in the table&period;
```js ```js
migration.describeTable('Person').success(function(attributes) { migration.describeTable('Person').then(function(attributes) {
/* /*
attributes will be something like: attributes will be something like:
   
...@@ -284,25 +273,8 @@ migration.removeIndex('Person', ['firstname', 'lastname']) ...@@ -284,25 +273,8 @@ migration.removeIndex('Person', ['firstname', 'lastname'])
``` ```
## Programmatic use ## Programmatic use
Sequelize has a [sister library](https://github.com/sequelize/umzug) for programmatically handling execution and logging of migration tasks.
If you need to interact with the migrator within your code, you can easily achieve that via `sequelize.getMigrator`. You can specify the path to your migrations as well as a pattern which represents the files that contain the migrations.
```js
var migrator = sequelize.getMigrator({
path: process.cwd() + '/database/migrations',
filesFilter: /\.coffee$/
})
```
Once you have a migrator object, you can run its migration with `migrator.migrate`. By default, this will execute all the up methods within your pending migrations. If you want to rollback a migration, just call it like this:
```js
migrator
.migrate({ method: 'down' })
.success(function() {
// The migrations have been executed!
})
```
[0]: http://gulpjs.com/ [0]: http://gulpjs.com/
[1]: https://github.com/sequelize/cli [1]: https://github.com/sequelize/cli
......
...@@ -5,18 +5,18 @@ Finder methods are designed to get data from the database&period; The returned d ...@@ -5,18 +5,18 @@ Finder methods are designed to get data from the database&period; The returned d
### find - Search for one specific element in the database ### find - Search for one specific element in the database
```js ```js
// search for known ids // search for known ids
Project.find(123).then(function(project) { Project.findById(123).then(function(project) {
// project will be an instance of Project and stores the content of the table entry // project will be an instance of Project and stores the content of the table entry
// with id 123. if such an entry is not defined you will get null // with id 123. if such an entry is not defined you will get null
}) })
// search for attributes // search for attributes
Project.find({ where: {title: 'aProject'} }).then(function(project) { Project.findOne({ where: {title: 'aProject'} }).then(function(project) {
// project will be the first entry of the Projects table with the title 'aProject' || null // project will be the first entry of the Projects table with the title 'aProject' || null
}) })
Project.find({ Project.findOne({
where: {title: 'aProject'}, where: {title: 'aProject'},
attributes: ['id', ['name', 'title']] attributes: ['id', ['name', 'title']]
}).then(function(project) { }).then(function(project) {
...@@ -168,7 +168,7 @@ Project.findAll({ ...@@ -168,7 +168,7 @@ Project.findAll({
It's possible to do complex where queries with multiple levels of nested AND, OR and NOT conditions. In order to do that you can use `$or`, `$and` or `$not`: It's possible to do complex where queries with multiple levels of nested AND, OR and NOT conditions. In order to do that you can use `$or`, `$and` or `$not`:
```js ```js
Project.find({ Project.findOne({
where: { where: {
name: 'a project', name: 'a project',
$or: [ $or: [
...@@ -178,7 +178,7 @@ Project.find({ ...@@ -178,7 +178,7 @@ Project.find({
} }
}) })
Project.find({ Project.findOne({
where: { where: {
name: 'a project', name: 'a project',
id: { id: {
...@@ -206,7 +206,7 @@ LIMIT 1; ...@@ -206,7 +206,7 @@ LIMIT 1;
`$not` example: `$not` example:
```js ```js
Project.find({ Project.findOne({
where: { where: {
name: 'a project', name: 'a project',
$not: [ $not: [
...@@ -229,7 +229,7 @@ WHERE ( ...@@ -229,7 +229,7 @@ WHERE (
LIMIT 1; LIMIT 1;
``` ```
### Manipulating the dataset with limit&comma; offset&comma; order and group ### Manipulating the dataset with limit, offset, order and group
To get more relevant data&comma; you can use limit&comma; offset&comma; order and grouping&colon; To get more relevant data&comma; you can use limit&comma; offset&comma; order and grouping&colon;
...@@ -257,7 +257,7 @@ Project.findAll({group: 'name'}) ...@@ -257,7 +257,7 @@ Project.findAll({group: 'name'})
Notice how in the two examples above&comma; the string provided is inserted verbatim into the query&comma; i&period;e&period; column names are not escaped&period; When you provide a string to order &sol; group&comma; this will always be the case. If you want to escape column names&comma; you should provide an array of arguments&comma; even though you only want to order &sol; group by a single column Notice how in the two examples above&comma; the string provided is inserted verbatim into the query&comma; i&period;e&period; column names are not escaped&period; When you provide a string to order &sol; group&comma; this will always be the case. If you want to escape column names&comma; you should provide an array of arguments&comma; even though you only want to order &sol; group by a single column
```js ```js
something.find({ something.findOne({
order: [ order: [
'name', 'name',
// will return `name` // will return `name`
...@@ -387,7 +387,7 @@ Task.belongsTo(User) ...@@ -387,7 +387,7 @@ Task.belongsTo(User)
User.hasMany(Task) User.hasMany(Task)
User.hasMany(Tool, { as: 'Instruments' }) User.hasMany(Tool, { as: 'Instruments' })
sequelize.sync().done(function() { sequelize.sync().then(function() {
// this is where we continue ... // this is where we continue ...
}) })
``` ```
...@@ -468,6 +468,53 @@ User.findAll({ include: [{ model: Tool, as: 'Instruments' }] }).then(function(us ...@@ -468,6 +468,53 @@ User.findAll({ include: [{ model: Tool, as: 'Instruments' }] }).then(function(us
}) })
``` ```
When eager loading we can also filter the associated model using `where`. This will return all `User`s in which the `where` clause of `Tool` model matches rows.
```js
User.findAll({
include: [{
model: Tool,
as: 'Instruments',
where: { name: { $like: '%ooth%' } }
}]
}).then(function(users) {
console.log(JSON.stringify(users))
/*
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"UserId": 1
}]
}],
[{
"name": "John Smith",
"id": 2,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"UserId": 1
}]
}],
*/
})
```
When an eager loaded model is filtered using `include.where` then `include.required` is implicitly set to
`true`. This means that an inner join is done returning parent models with any matching children.
### Including everything ### Including everything
To include all attributes, you can pass a single object with `all: true`: To include all attributes, you can pass a single object with `all: true`:
...@@ -503,7 +550,8 @@ Company.findAll({ ...@@ -503,7 +550,8 @@ Company.findAll({
``` ```
### Nested eager loading ### Nested eager loading
You can used nested eager loading to load all related models of a related model: You can use nested eager loading to load all related models of a related model:
```js ```js
User.findAll({ User.findAll({
include: [ include: [
...@@ -535,6 +583,28 @@ User.findAll({ ...@@ -535,6 +583,28 @@ User.findAll({
}) })
``` ```
This will produce an outer join. However, a `where` clause on a related model will create an inner join and return only the instances that have matching sub-models. To return all parent instances, you should add `required: false`.
```js
User.findAll({
include: [{
model: Tool,
as: 'Instruments',
include: [{
model: Teacher,
where: {
school: "Woodstock Music School"
},
required: false
}]
}]
}).then(function(users) {
/* ... */
})
```
The query above will return all users, and all their instruments, but only those teachers associated with `Woodstock Music School`.
Include all also supports nested loading: Include all also supports nested loading:
```js ```js
......
...@@ -62,7 +62,7 @@ $like: { $any: ['cat', 'hat']} ...@@ -62,7 +62,7 @@ $like: { $any: ['cat', 'hat']}
$overlap: [1, 2] // && [1, 2] (PG array overlap operator) $overlap: [1, 2] // && [1, 2] (PG array overlap operator)
$contains: [1, 2] // @> [1, 2] (PG array contains operator) $contains: [1, 2] // @> [1, 2] (PG array contains operator)
$contained: [1, 2] // <@ [1, 2] (PG array contained by 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 ### Combinations
...@@ -158,7 +158,7 @@ Project.findAll({ offset: 5, limit: 5 }) ...@@ -158,7 +158,7 @@ Project.findAll({ offset: 5, limit: 5 })
`order` takes an array of items to order the query by. Generally you will want to use a tuple/array of either attribute, direction or just direction to ensure proper escaping. `order` takes an array of items to order the query by. Generally you will want to use a tuple/array of either attribute, direction or just direction to ensure proper escaping.
```js ```js
something.find({ something.findOne({
order: [ order: [
// Will escape username and validate DESC against a list of valid direction parameters // Will escape username and validate DESC against a list of valid direction parameters
['username', 'DESC'], ['username', 'DESC'],
......
...@@ -19,11 +19,11 @@ 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) 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 ```js
// Callee is the model definition. This allows you to easily map a query to a predefined model // 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 // Each record will now be a instance of Project
}) })
``` ```
......
...@@ -13,6 +13,8 @@ Notice how the callback passed to `transaction` returns a promise chain, and doe ...@@ -13,6 +13,8 @@ Notice how the callback passed to `transaction` returns a promise chain, and doe
```js ```js
return sequelize.transaction(function (t) { return sequelize.transaction(function (t) {
// chain all your queries here. make sure you return them.
return User.create({ return User.create({
firstName: 'Abraham', firstName: 'Abraham',
lastName: 'Lincoln' lastName: 'Lincoln'
...@@ -22,6 +24,7 @@ return sequelize.transaction(function (t) { ...@@ -22,6 +24,7 @@ return sequelize.transaction(function (t) {
lastName: 'Boothe' lastName: 'Boothe'
}, {transaction: t}); }, {transaction: t});
}); });
}).then(function (result) { }).then(function (result) {
// Transaction has been committed // Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback // result is whatever the result of the promise chain returned to the transaction callback
...@@ -31,6 +34,8 @@ return sequelize.transaction(function (t) { ...@@ -31,6 +34,8 @@ return sequelize.transaction(function (t) {
}); });
``` ```
### Throw errors to rollback
When using the managed transaction you should _never_ commit or rollback the transaction manually. If all queries are successful, but you still want to rollback the transaction (for example because of a validation failure) you should throw an error to break and reject the chain: When using the managed transaction you should _never_ commit or rollback the transaction manually. If all queries are successful, but you still want to rollback the transaction (for example because of a validation failure) you should throw an error to break and reject the chain:
```js ```js
...@@ -45,6 +50,8 @@ return sequelize.transaction(function (t) { ...@@ -45,6 +50,8 @@ return sequelize.transaction(function (t) {
}); });
``` ```
### Automatically pass transactions to all queries
In the examples above, the transaction is still manually passed, by passing `{ transaction: t }` as the second argument. To automatically pass the transaction to all queries you must install the [continuation local storage](https://github.com/othiym23/node-continuation-local-storage) (CLS) module and instantiate a namespace in your own code: In the examples above, the transaction is still manually passed, by passing `{ transaction: t }` as the second argument. To automatically pass the transaction to all queries you must install the [continuation local storage](https://github.com/othiym23/node-continuation-local-storage) (CLS) module and instantiate a namespace in your own code:
```js ```js
...@@ -67,11 +74,11 @@ CLS works like a thread-local storage for callbacks. What this means in practice ...@@ -67,11 +74,11 @@ CLS works like a thread-local storage for callbacks. What this means in practice
```js ```js
sequelize.transaction(function (t1) { sequelize.transaction(function (t1) {
namespace.get('transaction') === t1; namespace.get('transaction') === t1; // true
}); });
sequelize.transaction(function (t2) { sequelize.transaction(function (t2) {
namespace.get('transaction') === t2; namespace.get('transaction') === t2; // true
}); });
``` ```
...@@ -84,20 +91,47 @@ sequelize.transaction(function (t1) { ...@@ -84,20 +91,47 @@ sequelize.transaction(function (t1) {
}); });
``` ```
If you want to execute queries inside the callback without using the transaction you can pass `{ transaction: null }`, or another transaction if you have several concurrent ones: # Concurrent/Partial transactions
You can have concurrent transactions within a sequence of queries or have some of them excluded from any transactions. Use the `{transaction: }` option to control which transaction a query belong to:
### Without CLS enabled
```js ```js
sequelize.transaction(function (t1) { sequelize.transaction(function (t1) {
sequelize.transaction(function (t2) { return sequelize.transaction(function (t2) {
// By default queries here will use t2 // With CLS enable, queries here will by default use t2
// Pass in the `transaction` option to define/alter the transaction they belong to.
return Promise.all([ return Promise.all([
User.create({ name: 'Bob' }, { transaction: null }), User.create({ name: 'Bob' }, { transaction: null }),
User.create({ name: 'Mallory' }, { transaction: t1 }) User.create({ name: 'Mallory' }, { transaction: t1 }),
User.create({ name: 'John' }) // this would default to t2
]); ]);
}); });
}); });
``` ```
# Isolation levels
The possible isolations levels to use when starting a transaction:
```js
Sequelize.Transaction.READ_UNCOMMITTED // "READ UNCOMMITTED"
Sequelize.Transaction.READ_COMMITTED // "READ COMMITTED"
Sequelize.Transaction.REPEATABLE_READ // "REPEATABLE READ"
Sequelize.Transaction.SERIALIZABLE // "SERIALIZABLE"
```
By default, sequelize uses "REPEATABLE READ". If you want to use a different isolation level, pass in the desired level as the first argument:
```js
return sequelize.transaction({
isolationLevel: Sequelize.Transaction.SERIALIZABLE
}, function (t) {
// your transactions
});
```
# Unmanaged transaction (then-callback) # Unmanaged transaction (then-callback)
Unmanaged transactions force you to manually rollback or commit the transaction. If you don't do that, the transaction will hang until it times out. To start an unmanaged transaction, call `sequelize.transaction()` without a callback (you can still pass an options object) and call `then` on the returned promise. Unmanaged transactions force you to manually rollback or commit the transaction. If you don't do that, the transaction will hang until it times out. To start an unmanaged transaction, call `sequelize.transaction()` without a callback (you can still pass an options object) and call `then` on the returned promise.
...@@ -110,7 +144,7 @@ return sequelize.transaction().then(function (t) { ...@@ -110,7 +144,7 @@ return sequelize.transaction().then(function (t) {
return user.addSibling({ return user.addSibling({
firstName: 'Lisa', firstName: 'Lisa',
lastName: 'Simpson' lastName: 'Simpson'
}, {transction: t}); }, {transaction: t});
}).then(function () { }).then(function () {
t.commit(); t.commit();
}).catch(function (err) { }).catch(function (err) {
...@@ -119,7 +153,57 @@ return sequelize.transaction().then(function (t) { ...@@ -119,7 +153,57 @@ return sequelize.transaction().then(function (t) {
}); });
``` ```
# Using transactions with other sequelize methods # Options
The `transaction` method can be called with an options object as the first argument, that
allows the configuration of the transaction.
```js
return sequelize.transaction({ /* options */ });
```
The following options (with it's default values) are available:
```js
{
autocommit: true,
isolationLevel: 'REPEATABLE_READ',
deferrable: 'NOT DEFERRABLE' // implicit default of postgres
}
```
The `isolationLevel` can either be set globally when initializing the Sequelize instance or
locally for every transaction:
```js
// globally
new Sequelize('db', 'user', 'pw', {
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
// locally
sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
```
The `deferrable` option triggers an additional query after the transaction start
that optionally set the constraint checks to be deferred or immediate. Please note
that this is only supported in PostgreSQL.
```js
sequelize.transaction({
// to defer all constraints:
deferrable: Sequelize.Deferrable.SET_DEFERRED,
// to defer a specific constraint:
deferrable: Sequelize.Deferrable.SET_DEFERRED(['some_constraint']),
// to not defer constraints:
deferrable: Sequelize.Deferrable.SET_IMMEDIATE
})
```
# Usage with other sequelize methods
The `transaction` option goes with most other options, which are usually the first argument of a method. The `transaction` option goes with most other options, which are usually the first argument of a method.
For methods that take values, like `.create`, `.update()`, `.updateAttributes()` etc. `transaction` should be passed to the option in the second argument. For methods that take values, like `.create`, `.update()`, `.updateAttributes()` etc. `transaction` should be passed to the option in the second argument.
......
...@@ -108,7 +108,15 @@ var sequelize = new Sequelize('database', 'username', 'password', { ...@@ -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) // 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 // 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
}) })
``` ```
...@@ -228,26 +236,26 @@ Here is how it works: ...@@ -228,26 +236,26 @@ Here is how it works:
```js ```js
// Arguments for raw queries // Arguments for raw queries
sequelize.query('your query', [, callee], [, options], [, replacements]) sequelize.query('your query', [, options])
// Quick example // Quick example
sequelize.query("SELECT * FROM myTable").success(function(myTableRows) { sequelize.query("SELECT * FROM myTable").then(function(myTableRows) {
console.log(myTableRows) console.log(myTableRows)
}) })
// Callee is the model definition. This allows you to easily map a query // If you want to return sequelize instances use the model options.
// to a predefined model for sequelizejs e.g: // This allows you to easily map a query to a predefined model for sequelizejs e.g:
sequelize sequelize
.query('SELECT * FROM projects', Projects) .query('SELECT * FROM projects', { model: Projects })
.success(function(projects){ .then(function(projects){
// Each record will now be mapped to the project's DAO-Factory. // Each record will now be mapped to the project's model.
console.log(projects) console.log(projects)
}) })
// Options is an object with the following keys: // Options is an object with the following keys:
sequelize sequelize
.query('SELECT 1', null, { .query('SELECT 1', {
// A function (or false) for logging your queries // A function (or false) for logging your queries
// Will get called for every SQL query that gets send // Will get called for every SQL query that gets send
// to the server. // to the server.
...@@ -265,8 +273,8 @@ sequelize ...@@ -265,8 +273,8 @@ sequelize
// Even if we declared a callee here, the raw: true would // Even if we declared a callee here, the raw: true would
// supersede and return a raw object. // supersede and return a raw object.
sequelize sequelize
.query('SELECT * FROM projects', null, { raw: true }) .query('SELECT * FROM projects', { raw: true })
.success(function(projects) { .then(function(projects) {
console.log(projects) console.log(projects)
}) })
``` ```
...@@ -274,7 +282,7 @@ sequelize ...@@ -274,7 +282,7 @@ sequelize
Replacements in a query can be done in two different ways, either using Replacements in a query can be done in two different ways, either using
named parameters (starting with `:`), or unnamed, represented by a ? 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 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. * If an object is passed, `:key` will be replaced with the keys from that object.
...@@ -284,19 +292,19 @@ will be thrown. ...@@ -284,19 +292,19 @@ will be thrown.
```js ```js
sequelize sequelize
.query( .query(
'SELECT * FROM projects WHERE status = ?', null, 'SELECT * FROM projects WHERE status = ?',
{ raw: true }, ['active'] { raw: true, replacements: ['active']
) )
.success(function(projects) { .then(function(projects) {
console.log(projects) console.log(projects)
}) })
sequelize sequelize
.query( .query(
'SELECT * FROM projects WHERE status = :status ', null, 'SELECT * FROM projects WHERE status = :status ',
{ raw: true }, { status: 'active' } { raw: true, replacements: { status: 'active' } }
) )
.success(function(projects) { .then(function(projects) {
console.log(projects) console.log(projects)
}) })
``` ```
...@@ -304,7 +312,7 @@ sequelize ...@@ -304,7 +312,7 @@ sequelize
**One note:** If the attribute names of the table contain dots, the resulting objects will be nested: **One note:** If the attribute names of the table contain dots, the resulting objects will be nested:
```js ```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)) console.log(JSON.stringify(rows))
/* /*
......
/*
Title: Working with associations
This example demonstrates the use of associations.
First of all, Person is getting associated via many-to-many with other Person objects (e.g. Person.hasMany('brothers')).
Afterwards a Person becomes associated with a 'father' and a mother using a one-to-one association created by hasOneAndBelongsTo.
The last association has the type many-to-one and is defined by the function hasManyAndBelongsTo.
The rest of the example is about setting and getting the associated data.
*/
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
, Person = sequelize.define('Person', { name: Sequelize.STRING })
, Pet = sequelize.define('Pet', { name: Sequelize.STRING })
Person.hasMany(Person, {as: 'Brothers'})
Person.hasMany(Person, {as: 'Sisters'})
Person.hasOne(Person, {as: 'Father', foreignKey: 'FatherId'})
Person.hasOne(Person, {as: 'Mother', foreignKey: 'MotherId'})
Person.hasMany(Pet)
var chainer = new Sequelize.Utils.QueryChainer
, person = Person.build({ name: 'Luke' })
, mother = Person.build({ name: 'Jane' })
, father = Person.build({ name: 'John' })
, brother = Person.build({ name: 'Brother' })
, sister = Person.build({ name: 'Sister' })
, pet = Pet.build({ name: 'Bob' })
sequelize.sync({force:true}).on('success', function() {
chainer
.add(person.save())
.add(mother.save())
.add(father.save())
.add(brother.save())
.add(sister.save())
.add(pet.save())
chainer.run().on('success', function() {
person.setMother(mother).on('success', function() { person.getMother().on('success', function(mom) {
console.log('my mom: ', mom.name)
})})
person.setFather(father).on('success', function() { person.getFather().on('success', function(dad) {
console.log('my dad: ', dad.name)
})})
person.setBrothers([brother]).on('success', function() { person.getBrothers().on('success', function(brothers) {
console.log("my brothers: " + brothers.map(function(b) { return b.name }))
})})
person.setSisters([sister]).on('success', function() { person.getSisters().on('success', function(sisters) {
console.log("my sisters: " + sisters.map(function(s) { return s.name }))
})})
person.setPets([pet]).on('success', function() { person.getPets().on('success', function(pets) {
console.log("my pets: " + pets.map(function(p) { return p.name }))
})})
}).on('failure', function(err) {
console.log(err)
})
})
\ No newline at end of file
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
var Person = sequelize.define('Person', { name: Sequelize.STRING })
, chainer = new Sequelize.Utils.QueryChainer
sequelize.sync({force: true}).on('success', function() {
var count = 10,
queries = []
for(var i = 0; i < count; i++)
chainer.add(Person.create({name: 'someone' + (i % 3)}))
console.log("Begin to save " + count + " items!")
chainer.run().on('success', function() {
console.log("finished")
Person.count().on('success', function(count) {
console.log("Counted " + count + " elements!")
})
Person.count({where: {name: 'someone2'}}).on('success', function(count) {
console.log("Counted " + count + " elements with name = someone2!")
})
})
})
\ No newline at end of file
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
var Person = sequelize.define('Person',
{ name: Sequelize.STRING,
age : Sequelize.INTEGER
})
, chainer = new Sequelize.Utils.QueryChainer
sequelize.sync({force: true}).on('success', function() {
var count = 10,
queries = []
for(var i = 0; i < count; i++)
chainer.add(Person.create({name: 'someone' + (i % 3), age : i+5}))
console.log("Begin to save " + count + " items!")
chainer.run().on('success', function() {
console.log("finished")
Person.max('age').on('success', function(max) {
console.log("Oldest person: " + max)
});
Person.min('age').on('success', function(min) {
console.log("Youngest person: " + min)
});
})
})
\ No newline at end of file
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false, host: config.host})
, QueryChainer = Sequelize.Utils.QueryChainer
, sys = require("sys")
var Person = sequelize.define('person', { name: Sequelize.STRING })
Person.sync({force: true}).on('success', function() {
var start = Date.now()
, count = 10000
, done = 0
var createPerson = function() {
Person.create({name: 'someone'}).on('success', function() {
if(++done == count) {
var duration = (Date.now() - start)
console.log("\nFinished creation of " + count + " people. Took: " + duration + "ms (avg: " + (duration/count) + "ms)")
start = Date.now()
console.log("Will now read them from the database:")
Person.findAll().on('success', function(people) {
console.log("Reading " + people.length + " items took: " + (Date.now() - start) + "ms")
})
} else {
(done % 100 == 0) && sys.print('.')
}
}).on('failure', function(err) {
console.log(err)
})
}
console.log('Creating people :)')
for(var i = 0; i < count; i++) {
createPerson()
}
}).on('failure', function(err) {
console.log(err)
})
\ No newline at end of file
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
var Person = sequelize.define('Person',
{ name: Sequelize.STRING,
age : Sequelize.INTEGER,
gender: Sequelize.ENUM('male', 'female')
})
, chainer = new Sequelize.Utils.QueryChainer
sequelize.sync({force: true}).on('success', function() {
var count = 10,
queries = []
for(var i = 0; i < count; i++)
chainer.add(Person.create({name: 'someone' + (i % 3), age : i+5, gender: (i % 2 == 0) ? 'male' : 'female'}))
console.log("Begin to save " + count + " items!")
chainer.run().on('success', function() {
console.log("finished")
Person.sum('age').on('success', function(sum) {
console.log("Sum of all peoples' ages: " + sum)
});
Person.sum('age', { where: { 'gender': 'male' } }).on('success', function(sum) {
console.log("Sum of all males' ages: " + sum)
});
})
})
/*
Title: Default values
This example demonstrates the use of default values for defined model fields. Instead of just specifying the datatype,
you have to pass a hash with a type and a default. You also might want to specify either an attribute can be null or not!
*/
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../test/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
var User = sequelize.define('User', {
name: { type: Sequelize.STRING, allowNull: false},
isAdmin: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: false }
})
, user = User.build({ name: 'Someone' })
sequelize.sync({force: true}).on('success', function() {
user.save().on('success', function(user) {
console.log("user.isAdmin should be the default value (false): ", user.isAdmin)
user.updateAttributes({ isAdmin: true }).on('success', function(user) {
console.log("user.isAdmin was overwritten to true: " + user.isAdmin)
})
})
}).on('failure', function(err) {
console.log(err)
})
\ No newline at end of file
/*jslint node:true */
"use strict";
var Sequelize = require('sequelize');
// initialize database connection
var sequelize = new Sequelize('testsequelize', 'testsequelize', 'testsequelize', {
dialect: 'postgres',
port: 5432,
define: {
freezeTableName: true
}
});
// load models
var models = [
'Trainer',
'Series',
'Video'
];
models.forEach(function(model) {
module.exports[model] = sequelize.import(__dirname + '/' + model);
});
// describe relationships
(function(m) {
m.Series.hasOne(m.Video);
m.Trainer.hasMany(m.Series);
})(module.exports);
// export connection
module.exports.sequelize = sequelize;
\ No newline at end of file
/*jslint node:true */
"use strict";
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Series', {
title: {
type: DataTypes.STRING
},
sub_title: {
type: DataTypes.STRING
},
description: {
type: DataTypes.TEXT
},
// Set FK relationship (hasMany) with `Trainer`
trainer_id: {
type: DataTypes.INTEGER,
references: "Trainer",
referencesKey: 'id'
}
}, {
// don't need timestamp attributes for this model
timestamps: false,
underscored: true
});
};
/*jslint node:true */
"use strict";
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Trainer', {
first_name: {
type: DataTypes.STRING
},
last_name: {
type: DataTypes.STRING
}
}, {
// don't need timestamp attributes for this model
timestamps: false,
underscored: true
});
};
/*jslint node:true */
"use strict";
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Video', {
title: {
type: DataTypes.STRING
},
sequence: {
type: DataTypes.INTEGER
},
description: {
type: DataTypes.TEXT
},
// set relationship (hasOne) with `Series`
series_id: {
type: DataTypes.INTEGER,
references: "Series",
referencesKey: 'id'
}
}, {
// don't need timestamp attributes for this model
timestamps: false,
underscored: true
});
};
Results after syncing the dabatase
----------------------------------
After syncing the database, you'll see in the console the following:
```
Executing: CREATE TABLE IF NOT EXISTS "Trainer" ("first_name" VARCHAR(255), "last_name" VARCHAR(255), "id" SERIAL , PRIMARY KEY ("id"));
Executing: CREATE TABLE IF NOT EXISTS "Series" ("title" VARCHAR(255), "sub_title" VARCHAR(255), "description" TEXT, "trainer_id" INTEGER REFERENCES "Trainer" ("id"), "id" SERIAL , PRIMARY KEY ("id"));
Executing: CREATE TABLE IF NOT EXISTS "Video" ("title" VARCHAR(255), "sequence" INTEGER, "description" TEXT, "series_id" INTEGER REFERENCES "Series" ("id"), "id" SERIAL , PRIMARY KEY ("id"));
```
Notice in the `Video` that `series_id` field has a referential integrity to `Series`:
```
"series_id" INTEGER REFERENCES "Series" ("id")
```
This is the output when describing the table's structure of the Postgres database:
**Trainer** table:
```
testsequelize=> \d+ "Trainer";
Table "public.Trainer"
Column | Type | Modifiers |
------------+------------------------+--------------------------------------------------------+
first_name | character varying(255) | |
last_name | character varying(255) | |
id | integer | not null default nextval('"Trainer_id_seq"'::regclass) |
Indexes:
"Trainer_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE ""Series"" CONSTRAINT "Series_trainer_id_fkey" FOREIGN KEY (trainer_id) REFERENCES "Trainer"(id)
Has OIDs: no
```
**Series** table:
```
testsequelize=> \d+ "Series";
Table "public.Series"
Column | Type | Modifiers |
-----------------+------------------------+-------------------------------------------------------+
title | character varying(255) | |
sub_title | character varying(255) | |
description | text | |
trainer_id | integer | |
id | integer | not null default nextval('"Series_id_seq"'::regclass) |
Indexes:
"Series_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"Series_trainer_id_fkey" FOREIGN KEY (trainer_id) REFERENCES "Trainer"(id)
Referenced by:
TABLE ""Video"" CONSTRAINT "Video_series_id_fkey" FOREIGN KEY (series_id) REFERENCES "Series"(id)
Has OIDs: no
```
**Video** table:
```
testsequelize=> \d+ "Video";
Table "public.Video"
Column | Type | Modifiers |
-------------+------------------------+------------------------------------------------------+
title | character varying(255) | |
sequence | integer | |
description | text | |
series_id | integer | |
id | integer | not null default nextval('"Video_id_seq"'::regclass) |
Indexes:
"Video_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"Video_series_id_fkey" FOREIGN KEY (series_id) REFERENCES "Series"(id)
Has OIDs: no
```
/*
Title: Default values
This example demonstrates the use of default values for defined model fields. Instead of just specifying the datatype,
you have to pass a hash with a type and a default. You also might want to specify either an attribute can be null or not!
*/
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
var User = sequelize.define('User', {
name: { type: Sequelize.STRING, allowNull: false},
isAdmin: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: false }
})
, user = User.build({ name: 'Someone' })
sequelize.sync({force: true}).on('success', function() {
user.save().on('success', function(user) {
console.log("user.isAdmin should be the default value (false): ", user.isAdmin)
user.updateAttributes({ isAdmin: true }).on('success', function(user) {
console.log("user.isAdmin was overwritten to true: " + user.isAdmin)
})
})
}).on('failure', function(err) {
console.log(err)
})
\ No newline at end of file
/*
Title: Defining class and instance methods
This example shows the usage of the classMethods and instanceMethods option for Models.
*/
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
// model definition
var Task = sequelize.define("Task", {
name: Sequelize.STRING,
deadline: Sequelize.DATE,
importance: Sequelize.INTEGER
}, {
classMethods: {
setImportance: function(newImportance, callback) {
Task.findAll().on('success', function(allTasks) {
var chainer = new Sequelize.Utils.QueryChainer
allTasks.forEach(function(task) {
chainer.add(task.updateAttributes({ importance: newImportance }))
})
chainer.run().on('success', function() {
callback && callback()
})
})
}
},
instanceMethods: {
passedDeadline: function() {
return (this.deadline < new Date())
}
}
})
// instance creation
var task1 = Task.build({
name: 'Choose a nice MySQL connector',
deadline: new Date(Date.parse("Jul 8, 2100")),
importance: 10
}),
task2 = Task.build({
name: 'Build the rest',
deadline: new Date(Date.parse("Jul 8, 2005")),
importance: 90
})
Task.sync({force: true}).on('success', function() {
new Sequelize.Utils.QueryChainer([task1.save(), task2.save()]).run().on('success', function() {
console.log("should be false: " + task1.passedDeadline())
console.log("should be true: " + task2.passedDeadline())
console.log("should be 10: " + task1.importance)
Task.setImportance(30, function() {
Task.findAll().on('success', function(tasks) {
tasks.forEach(function(task) {
console.log("should be 30: " + task.importance)
})
})
})
})
})
\ No newline at end of file
/*
Title: PostgreSQL JSON Data-Type
An example of using PostgreSQL's JSON data-type.
In this example we create a single table that can handle multiple types of different media and it's metadata.
This example uses the promise API preferred in 2.0 and above.
*/
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../test/config/config")
, sequelize = new Sequelize(config.postgres.database, config.postgres.username, config.postgres.password, {
dialect: 'postgres',
logging: false
});
var Content = sequelize.define('Content', {
title: { type: Sequelize.STRING },
type: { type: Sequelize.STRING },
metadata: { type: Sequelize.JSON }
})
, movie = Content.build({
title: 'Grave of the Fireflies',
type: 'Movie',
metadata: {
director: 'Isao Takahata',
language: 'Japanese',
year: 1988
}
})
, episode = Content.build({
title: 'Chapter 3',
type: 'Episode',
metadata: {
season: 1,
episode: 3,
language: 'English',
seriesTitle: 'House of Cards',
genres: ['Drama', 'Political thriller']
}
});
sequelize.sync({ force: true })
.then(function() {
return sequelize.Promise.all([
movie.save(),
episode.save()
]);
})
.then(function() {
console.log('=====================================');
console.log('Searching for any content in Japanese');
console.log('-------------------------------------');
// Using nested object query syntax
return Content.find({ where: Sequelize.json({ metadata: { language: 'Japanese' } }) })
.then(function(content) {
console.log('Result:', content.dataValues);
console.log('=====================================');
})
})
.then(function() {
console.log('=====================================');
console.log('Searching for any content in English');
console.log('-------------------------------------');
// Using the postgres json syntax
return Content.find({ where: Sequelize.json("metadata->>'language'", 'English') })
.then(function(content) {
console.log('Result:', content.dataValues);
console.log('=====================================');
})
})
.then(function() {
console.log('===========================================');
console.log('Searching for series named "House of Cards"');
console.log('-------------------------------------------');
return Content.find({ where: Sequelize.json('metadata.seriesTitle', 'House of Cards') })
.then(function(content) {
console.log('Result:', content.dataValues);
console.log('===========================================');
})
});
\ No newline at end of file
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {
// use other database server or port
host: 'my.srv.tld',
port: 12345,
// disable logging
logging: false
})
, Smth = sequelize.define('Smth', {foo: Sequelize.STRING})
sequelize.sync({force: true}).on('success', function() {
console.log('Hey we established the connection successfully! Woot!')
}).on('failure', function(err) {
console.log(err)
})
\ No newline at end of file
module.exports = function(sequelize, DataTypes) {
return sequelize.define("Project", {
name: DataTypes.STRING,
description: DataTypes.TEXT
})
}
\ No newline at end of file
module.exports = function(sequelize, DataTypes) {
return sequelize.define("Task", {
name: DataTypes.STRING,
deadline: DataTypes.DATE,
importance: DataTypes.INTEGER
})
}
\ No newline at end of file
var Sequelize = require(__dirname + "/../../index")
, config = require(__dirname + "/../../spec/config/config")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
, Project = sequelize.import(__dirname + "/Project")
, Task = sequelize.import(__dirname + "/Task")
Project.hasMany(Task)
Task.belongsTo(Project)
sequelize.sync({force: true}).on('success', function() {
Project
.create({ name: 'Sequelize', description: 'A nice MySQL ORM for NodeJS' })
.on('success', function(project) {
Task.create({ name: 'Choose a nice MySQL connector', deadline: new Date(), importance: 10 })
.on('success', function(task1) {
Task.create({ name: 'Build the rest', deadline: new Date(), importance: 90 })
.on('success', function(task2) {
project.setTasks([task1, task2]).on('success', function(tasks) {
console.log(project)
console.log(tasks)
})
})
})
})
})
\ No newline at end of file
var Association = function() { var Association = function () {};
};
module.exports = Association; module.exports = Association;
\ No newline at end of file
...@@ -5,16 +5,9 @@ var Utils = require('./../utils') ...@@ -5,16 +5,9 @@ var Utils = require('./../utils')
, _ = require('lodash') , _ = require('lodash')
, Association = require('./base') , Association = require('./base')
, CounterCache = require('../plugins/counter-cache') , CounterCache = require('../plugins/counter-cache')
, util = require('util') , util = require('util');
, deprecatedSeen = {}
, deprecated = function(message) {
if (deprecatedSeen[message]) return;
console.warn(message);
deprecatedSeen[message] = true;
};
module.exports = (function() { var BelongsToMany = function(source, target, options) {
var BelongsToMany = function(source, target, options) {
Association.call(this); Association.call(this);
this.associationType = 'BelongsToMany'; this.associationType = 'BelongsToMany';
...@@ -22,7 +15,7 @@ module.exports = (function() { ...@@ -22,7 +15,7 @@ module.exports = (function() {
this.target = target; this.target = target;
this.targetAssociation = null; this.targetAssociation = null;
this.options = options || {}; this.options = options || {};
this.sequelize = source.daoFactoryManager.sequelize; this.sequelize = source.modelManager.sequelize;
this.through = options.through; this.through = options.through;
this.scope = options.scope; this.scope = options.scope;
this.isMultiAssociation = true; this.isMultiAssociation = true;
...@@ -34,18 +27,11 @@ module.exports = (function() { ...@@ -34,18 +27,11 @@ module.exports = (function() {
this.isSelfAssociation ? (this.as || this.target.tableName) : this.target.tableName this.isSelfAssociation ? (this.as || this.target.tableName) : this.target.tableName
); );
/* if (this.through === undefined || this.through === true || this.through === null) {
* Normalize through throw new Error('belongsToMany must be given a through option, either a string or a model');
*/
if (this.through === undefined) {
this.through = true;
}
if (this.through === true) {
deprecated('belongsToMany: not defining through or defining through as true has been deprecated, please provide a string or a model');
} }
if (this.through !== null && !this.through.model) { if (!this.through.model) {
this.through = { this.through = {
model: this.through model: this.through
}; };
...@@ -107,30 +93,6 @@ module.exports = (function() { ...@@ -107,30 +93,6 @@ module.exports = (function() {
} }
/* /*
* Determine associationAccessor, especially for include options to identify the correct model
*/
this.associationAccessor = this.as;
if (!this.associationAccessor) {
if (typeof this.through.model === 'string') {
this.associationAccessor = this.through.model;
} else if (Object(this.through.model) === this.through.model) {
this.associationAccessor = this.through.model.tableName;
} else {
this.associationAccessor = this.combinedTableName;
}
}
/*
* If we are double linked, and through is either default or a string, we create the through model and set it on both associations
*/
if (this.through.model === true) {
this.through.model = this.combinedTableName;
if (!this.options.through) this.options.through = {};
this.options.through.model = this.through.model;
}
/*
* Find paired association (if exists) * Find paired association (if exists)
*/ */
_.each(this.target.associations, function(association) { _.each(this.target.associations, function(association) {
...@@ -192,6 +154,8 @@ module.exports = (function() { ...@@ -192,6 +154,8 @@ module.exports = (function() {
this.options.name = this.target.options.name; this.options.name = this.target.options.name;
} }
this.associationAccessor = this.as;
// Get singular and plural names, trying to uppercase the first letter, unless the model forbids it // Get singular and plural names, trying to uppercase the first letter, unless the model forbids it
var plural = Utils.uppercaseFirst(this.options.name.plural) var plural = Utils.uppercaseFirst(this.options.name.plural)
, singular = Utils.uppercaseFirst(this.options.name.singular); , singular = Utils.uppercaseFirst(this.options.name.singular);
...@@ -211,13 +175,13 @@ module.exports = (function() { ...@@ -211,13 +175,13 @@ module.exports = (function() {
if (this.options.counterCache) { if (this.options.counterCache) {
new CounterCache(this, this.options.counterCache !== true ? this.options.counterCache : {}); new CounterCache(this, this.options.counterCache !== true ? this.options.counterCache : {});
} }
}; };
util.inherits(BelongsToMany, Association); util.inherits(BelongsToMany, Association);
// the id is in the target table // the id is in the target table
// or in an extra table which connects two tables // or in an extra table which connects two tables
BelongsToMany.prototype.injectAttributes = function() { BelongsToMany.prototype.injectAttributes = function() {
var self = this; var self = this;
this.identifier = this.foreignKey; this.identifier = this.foreignKey;
...@@ -260,8 +224,10 @@ module.exports = (function() { ...@@ -260,8 +224,10 @@ module.exports = (function() {
} }
if (this.options.constraints !== false) { if (this.options.constraints !== false) {
sourceAttribute.references = this.source.getTableName(); sourceAttribute.references = {
sourceAttribute.referencesKey = sourceKeyField; model: this.source.getTableName(),
key: sourceKeyField
};
// For the source attribute the passed option is the priority // For the source attribute the passed option is the priority
sourceAttribute.onDelete = this.options.onDelete || this.through.model.rawAttributes[this.identifier].onDelete; sourceAttribute.onDelete = this.options.onDelete || this.through.model.rawAttributes[this.identifier].onDelete;
sourceAttribute.onUpdate = this.options.onUpdate || this.through.model.rawAttributes[this.identifier].onUpdate; sourceAttribute.onUpdate = this.options.onUpdate || this.through.model.rawAttributes[this.identifier].onUpdate;
...@@ -269,9 +235,10 @@ module.exports = (function() { ...@@ -269,9 +235,10 @@ module.exports = (function() {
if (!sourceAttribute.onDelete) sourceAttribute.onDelete = 'CASCADE'; if (!sourceAttribute.onDelete) sourceAttribute.onDelete = 'CASCADE';
if (!sourceAttribute.onUpdate) sourceAttribute.onUpdate = 'CASCADE'; if (!sourceAttribute.onUpdate) sourceAttribute.onUpdate = 'CASCADE';
targetAttribute.references = {
targetAttribute.references = this.target.getTableName(); model: this.target.getTableName(),
targetAttribute.referencesKey = targetKeyField; key: targetKeyField
};
// But the for target attribute the previously defined option is the priority (since it could've been set by another belongsToMany call) // But the for target attribute the previously defined option is the priority (since it could've been set by another belongsToMany call)
targetAttribute.onDelete = this.through.model.rawAttributes[this.foreignIdentifier].onDelete || this.options.onDelete; targetAttribute.onDelete = this.through.model.rawAttributes[this.foreignIdentifier].onDelete || this.options.onDelete;
targetAttribute.onUpdate = this.through.model.rawAttributes[this.foreignIdentifier].onUpdate || this.options.onUpdate; targetAttribute.onUpdate = this.through.model.rawAttributes[this.foreignIdentifier].onUpdate || this.options.onUpdate;
...@@ -290,19 +257,18 @@ module.exports = (function() { ...@@ -290,19 +257,18 @@ module.exports = (function() {
this.paired.foreignIdentifierField = this.through.model.rawAttributes[this.paired.foreignIdentifier].field || this.paired.foreignIdentifier; this.paired.foreignIdentifierField = this.through.model.rawAttributes[this.paired.foreignIdentifier].field || this.paired.foreignIdentifier;
} }
this.through.model.init(this.through.model.daoFactoryManager); this.through.model.init(this.through.model.modelManager);
Helpers.checkNamingCollision(this); Helpers.checkNamingCollision(this);
return this; return this;
}; };
BelongsToMany.prototype.injectGetter = function(obj) { BelongsToMany.prototype.injectGetter = function(obj) {
var association = this; var association = this;
obj[this.accessors.get] = function(options, queryOptions) { obj[this.accessors.get] = function(options) {
options = association.target.__optClone(options) || {}; options = association.target.__optClone(options) || {};
queryOptions = queryOptions || {};
var instance = this var instance = this
, through = association.through , through = association.through
...@@ -357,11 +323,11 @@ module.exports = (function() { ...@@ -357,11 +323,11 @@ module.exports = (function() {
} }
} }
return model.findAll(options, queryOptions); return model.findAll(options);
}; };
obj[this.accessors.hasSingle] = obj[this.accessors.hasAll] = function(instances, options) { obj[this.accessors.hasSingle] = obj[this.accessors.hasAll] = function(instances, options) {
var where = { $or: [] }; var where = {};
if (!Array.isArray(instances)) { if (!Array.isArray(instances)) {
instances = [instances]; instances = [instances];
...@@ -370,13 +336,17 @@ module.exports = (function() { ...@@ -370,13 +336,17 @@ module.exports = (function() {
options = options || {}; options = options || {};
options.scope = false; options.scope = false;
instances.forEach(function(instance) { _.defaults(options, {
raw: true
});
where.$or = instances.map(function (instance) {
if (instance instanceof association.target.Instance) { if (instance instanceof association.target.Instance) {
where.$or.push(instance.primaryKeyValues); return instance.where();
} else { } else {
var _where = {}; var $where = {};
_where[association.target.primaryKeyAttribute] = instance; $where[association.target.primaryKeyAttribute] = instance;
where.$or.push(_where); return $where;
} }
}); });
...@@ -387,18 +357,15 @@ module.exports = (function() { ...@@ -387,18 +357,15 @@ module.exports = (function() {
] ]
}; };
return this[association.accessors.get]( return this[association.accessors.get](options).then(function(associatedObjects) {
options,
{ raw: true }
).then(function(associatedObjects) {
return associatedObjects.length === instances.length; return associatedObjects.length === instances.length;
}); });
}; };
return this; return this;
}; };
BelongsToMany.prototype.injectSetter = function(obj) { BelongsToMany.prototype.injectSetter = function(obj) {
var association = this var association = this
, primaryKeyAttribute = association.target.primaryKeyAttribute; , primaryKeyAttribute = association.target.primaryKeyAttribute;
...@@ -407,8 +374,7 @@ module.exports = (function() { ...@@ -407,8 +374,7 @@ module.exports = (function() {
var instance = this; var instance = this;
return instance[association.accessors.get]({ return instance[association.accessors.get]({
scope: false scope: false,
}, {
transaction: options.transaction, transaction: options.transaction,
logging: options.logging logging: options.logging
}).then(function(oldAssociatedObjects) { }).then(function(oldAssociatedObjects) {
...@@ -533,9 +499,7 @@ module.exports = (function() { ...@@ -533,9 +499,7 @@ module.exports = (function() {
if (!newInstance) return Utils.Promise.resolve(); if (!newInstance) return Utils.Promise.resolve();
if (association.through && association.through.scope) { if (association.through && association.through.scope) {
Object.keys(association.through.scope).forEach(function (attribute) { _.assign(additionalAttributes, association.through.scope);
additionalAttributes[attribute] = association.through.scope[attribute];
});
} }
var instance = this var instance = this
...@@ -627,9 +591,7 @@ module.exports = (function() { ...@@ -627,9 +591,7 @@ module.exports = (function() {
} }
if (association.through.scope) { if (association.through.scope) {
Object.keys(association.through.scope).forEach(function (attribute) { _.assign(attributes, association.through.scope);
attributes[attribute] = association.through.scope[attribute];
});
} }
return attributes; return attributes;
...@@ -658,8 +620,7 @@ module.exports = (function() { ...@@ -658,8 +620,7 @@ module.exports = (function() {
return instance[association.accessors.get]({ return instance[association.accessors.get]({
scope: false, scope: false,
where: newInstance.primaryKeyValues where: newInstance.where(),
}, {
transaction: (additionalAttributes || {}).transaction, transaction: (additionalAttributes || {}).transaction,
logging: options.logging logging: options.logging
}).then(function(currentAssociatedObjects) { }).then(function(currentAssociatedObjects) {
...@@ -690,9 +651,7 @@ module.exports = (function() { ...@@ -690,9 +651,7 @@ module.exports = (function() {
} else { } else {
attributes = Utils._.defaults(attributes, newInstance[association.through.model.name], additionalAttributes); attributes = Utils._.defaults(attributes, newInstance[association.through.model.name], additionalAttributes);
if (association.through.scope) { if (association.through.scope) {
Object.keys(association.through.scope).forEach(function (attribute) { _.assign(attributes, association.through.scope);
attributes[attribute] = association.through.scope[attribute];
});
} }
return association.through.model.create(attributes, options); return association.through.model.create(attributes, options);
...@@ -708,9 +667,9 @@ module.exports = (function() { ...@@ -708,9 +667,9 @@ module.exports = (function() {
}; };
return this; return this;
}; };
BelongsToMany.prototype.injectCreator = function(obj) { BelongsToMany.prototype.injectCreator = function(obj) {
var association = this; var association = this;
obj[this.accessors.create] = function(values, options) { obj[this.accessors.create] = function(values, options) {
...@@ -741,7 +700,6 @@ module.exports = (function() { ...@@ -741,7 +700,6 @@ module.exports = (function() {
}; };
return this; return this;
}; };
return BelongsToMany; module.exports = BelongsToMany;
})();
...@@ -6,8 +6,7 @@ var Utils = require('./../utils') ...@@ -6,8 +6,7 @@ var Utils = require('./../utils')
, Association = require('./base') , Association = require('./base')
, util = require('util'); , util = require('util');
module.exports = (function() { var BelongsTo = function(source, target, options) {
var BelongsTo = function(source, target, options) {
Association.call(this); Association.call(this);
this.associationType = 'BelongsTo'; this.associationType = 'BelongsTo';
...@@ -67,12 +66,12 @@ module.exports = (function() { ...@@ -67,12 +66,12 @@ module.exports = (function() {
set: 'set' + singular, set: 'set' + singular,
create: 'create' + singular create: 'create' + singular
}; };
}; };
util.inherits(BelongsTo, Association); util.inherits(BelongsTo, Association);
// the id is in the source table // the id is in the source table
BelongsTo.prototype.injectAttributes = function() { BelongsTo.prototype.injectAttributes = function() {
var newAttributes = {}; var newAttributes = {};
newAttributes[this.identifier] = Utils._.defaults(this.foreignKeyAttribute, { type: this.options.keyType || this.target.rawAttributes[this.targetIdentifier].type }); newAttributes[this.identifier] = Utils._.defaults(this.foreignKeyAttribute, { type: this.options.keyType || this.target.rawAttributes[this.targetIdentifier].type });
...@@ -85,16 +84,15 @@ module.exports = (function() { ...@@ -85,16 +84,15 @@ module.exports = (function() {
this.identifierField = this.source.rawAttributes[this.identifier].field || this.identifier; this.identifierField = this.source.rawAttributes[this.identifier].field || this.identifier;
// Sync attributes and setters/getters to DAO prototype
this.source.refreshAttributes(); this.source.refreshAttributes();
Helpers.checkNamingCollision(this); Helpers.checkNamingCollision(this);
return this; return this;
}; };
// Add getAssociation method to the prototype of the model instance // Add getAssociation method to the prototype of the model instance
BelongsTo.prototype.injectGetter = function(instancePrototype) { BelongsTo.prototype.injectGetter = function(instancePrototype) {
var association = this; var association = this;
instancePrototype[this.accessors.get] = function(options) { instancePrototype[this.accessors.get] = function(options) {
...@@ -125,10 +123,10 @@ module.exports = (function() { ...@@ -125,10 +123,10 @@ module.exports = (function() {
}; };
return this; return this;
}; };
// Add setAssociaton method to the prototype of the model instance // Add setAssociaton method to the prototype of the model instance
BelongsTo.prototype.injectSetter = function(instancePrototype) { BelongsTo.prototype.injectSetter = function(instancePrototype) {
var association = this; var association = this;
instancePrototype[this.accessors.set] = function(associatedInstance, options) { instancePrototype[this.accessors.set] = function(associatedInstance, options) {
...@@ -155,10 +153,10 @@ module.exports = (function() { ...@@ -155,10 +153,10 @@ module.exports = (function() {
}; };
return this; return this;
}; };
// Add createAssociation method to the prototype of the model instance // Add createAssociation method to the prototype of the model instance
BelongsTo.prototype.injectCreator = function(instancePrototype) { BelongsTo.prototype.injectCreator = function(instancePrototype) {
var association = this; var association = this;
instancePrototype[this.accessors.create] = function(values, fieldsOrOptions) { instancePrototype[this.accessors.create] = function(values, fieldsOrOptions) {
...@@ -168,6 +166,7 @@ module.exports = (function() { ...@@ -168,6 +166,7 @@ module.exports = (function() {
if ((fieldsOrOptions || {}).transaction instanceof Transaction) { if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
options.transaction = fieldsOrOptions.transaction; options.transaction = fieldsOrOptions.transaction;
} }
options.logging = (fieldsOrOptions || {}).logging;
return association.target.create(values, fieldsOrOptions).then(function(newAssociatedObject) { return association.target.create(values, fieldsOrOptions).then(function(newAssociatedObject) {
return instance[association.accessors.set](newAssociatedObject, options); return instance[association.accessors.set](newAssociatedObject, options);
...@@ -175,7 +174,6 @@ module.exports = (function() { ...@@ -175,7 +174,6 @@ module.exports = (function() {
}; };
return this; return this;
}; };
return BelongsTo; module.exports = BelongsTo;
})();
'use strict';
var Utils = require('./../utils');
module.exports = (function() {
var HasManyDoubleLinked = function(association, instance) {
this.association = association;
this.instance = instance;
// Alias the quoting methods for code brevity
this.QueryInterface = instance.QueryInterface;
};
HasManyDoubleLinked.prototype.injectGetter = function(options, queryOptions) {
var self = this
, through = self.association.through
, scopeWhere
, throughWhere;
if (this.association.scope) {
scopeWhere = {};
Object.keys(this.association.scope).forEach(function (attribute) {
scopeWhere[attribute] = this.association.scope[attribute];
}.bind(this));
}
options.where = new Utils.and([
scopeWhere,
options.where
]);
if (Object(through.model) === through.model) {
throughWhere = {};
throughWhere[self.association.identifier] = self.instance.get(self.association.source.primaryKeyAttribute);
if (through && through.scope) {
Object.keys(through.scope).forEach(function (attribute) {
throughWhere[attribute] = through.scope[attribute];
}.bind(this));
}
options.include = options.include || [];
options.include.push({
model: through.model,
as: through.model.name,
attributes: options.joinTableAttributes,
association: {
isSingleAssociation: true,
source: self.association.target,
target: self.association.source,
identifier: self.association.foreignIdentifier,
identifierField: self.association.foreignIdentifierField
},
required: true,
where: throughWhere,
_pseudo: true
});
}
return self.association.target.findAll(options, queryOptions);
};
HasManyDoubleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) {
var self = this
, targetAssociation = self.association.targetAssociation
, foreignIdentifier = self.association.foreignIdentifier
, sourceKeys = Object.keys(self.association.source.primaryKeys)
, targetKeys = Object.keys(self.association.target.primaryKeys)
, obsoleteAssociations = []
, changedAssociations = []
, options = defaultAttributes
, promises = []
, unassociatedObjects;
// Don't try to insert the transaction as an attribute in the through table
defaultAttributes = Utils._.omit(defaultAttributes, ['transaction', 'hooks', 'individualHooks', 'ignoreDuplicates', 'validate', 'fields', 'logging']);
unassociatedObjects = newAssociations.filter(function(obj) {
return !Utils._.find(oldAssociations, function(old) {
return (!!obj[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : (!!obj[targetKeys[0]] ? obj[targetKeys[0]] === old[targetKeys[0]] : obj.id === old.id));
});
});
oldAssociations.forEach(function(old) {
var newObj = Utils._.find(newAssociations, function(obj) {
return (!!obj[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : (!!obj[targetKeys[0]] ? obj[targetKeys[0]] === old[targetKeys[0]] : obj.id === old.id));
});
if (!newObj) {
obsoleteAssociations.push(old);
} else if (Object(targetAssociation.through.model) === targetAssociation.through.model) {
var throughAttributes = newObj[self.association.through.model.name];
// Quick-fix for subtle bug when using existing objects that might have the through model attached (not as an attribute object)
if (throughAttributes instanceof self.association.through.model.Instance) {
throughAttributes = {};
}
var changedAssociation = {
where: {},
attributes: Utils._.defaults({}, throughAttributes, defaultAttributes)
};
changedAssociation.where[self.association.identifier] = self.instance[sourceKeys[0]] || self.instance.id;
changedAssociation.where[foreignIdentifier] = newObj[targetKeys[0]] || newObj.id;
if (Object.keys(changedAssociation.attributes).length) {
changedAssociations.push(changedAssociation);
}
}
});
if (obsoleteAssociations.length > 0) {
var foreignIds = obsoleteAssociations.map(function(associatedObject) {
return ((targetKeys.length === 1) ? associatedObject[targetKeys[0]] : associatedObject.id);
});
var where = {};
where[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id);
where[foreignIdentifier] = foreignIds;
promises.push(self.association.through.model.destroy(Utils._.extend(options, {
where: where
})));
}
if (unassociatedObjects.length > 0) {
var bulk = unassociatedObjects.map(function(unassociatedObject) {
var attributes = {};
attributes[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id);
attributes[foreignIdentifier] = ((targetKeys.length === 1) ? unassociatedObject[targetKeys[0]] : unassociatedObject.id);
if (Object(targetAssociation.through.model) === targetAssociation.through.model) {
attributes = Utils._.defaults(attributes, unassociatedObject[targetAssociation.through.model.name], defaultAttributes);
}
if (this.association.through.scope) {
Object.keys(this.association.through.scope).forEach(function (attribute) {
attributes[attribute] = this.association.through.scope[attribute];
}.bind(this));
}
return attributes;
}.bind(this));
promises.push(self.association.through.model.bulkCreate(bulk, options));
}
if (changedAssociations.length > 0) {
changedAssociations.forEach(function(assoc) {
promises.push(self.association.through.model.update(assoc.attributes, Utils._.extend(options, {
where: assoc.where
})));
});
}
return Utils.Promise.all(promises);
};
HasManyDoubleLinked.prototype.injectAdder = function(newAssociation, additionalAttributes, exists) {
additionalAttributes = additionalAttributes || {};
var attributes = {}
, targetAssociation = this.association.targetAssociation
, foreignIdentifier = targetAssociation.identifier
, options = additionalAttributes;
var sourceKeys = Object.keys(this.association.source.primaryKeys);
var targetKeys = Object.keys(this.association.target.primaryKeys);
// Don't try to insert the transaction as an attribute in the through table
additionalAttributes = Utils._.omit(additionalAttributes, ['transaction', 'hooks', 'individualHooks', 'ignoreDuplicates', 'validate', 'fields']);
attributes[this.association.identifier] = ((sourceKeys.length === 1) ? this.instance[sourceKeys[0]] : this.instance.id);
attributes[foreignIdentifier] = ((targetKeys.length === 1) ? newAssociation[targetKeys[0]] : newAssociation.id);
if (exists) {
var where = attributes;
attributes = Utils._.defaults({}, newAssociation[targetAssociation.through.model.name], additionalAttributes);
if (Object.keys(attributes).length) {
return targetAssociation.through.model.update(attributes, Utils._.extend(options, {
where: where
}));
} else {
return Utils.Promise.resolve();
}
} else {
attributes = Utils._.defaults(attributes, newAssociation[targetAssociation.through.model.name], additionalAttributes);
if (this.association.through.scope) {
Object.keys(this.association.through.scope).forEach(function (attribute) {
attributes[attribute] = this.association.through.scope[attribute];
}.bind(this));
}
return this.association.through.model.create(attributes, options);
}
};
return HasManyDoubleLinked;
})();
'use strict'; 'use strict';
var Utils = require('./../utils'); var Utils = require('./../utils')
, _ = require('lodash');
module.exports = (function() { var HasManySingleLinked = function(association, instance) {
var HasManySingleLinked = function(association, instance) {
this.association = association; this.association = association;
this.instance = instance; this.instance = instance;
this.target = this.association.target; this.target = this.association.target;
this.source = this.association.source; this.source = this.association.source;
}; };
HasManySingleLinked.prototype.injectGetter = function(options, queryOptions) { HasManySingleLinked.prototype.injectGetter = function(options) {
var scopeWhere = this.association.scope ? {} : null; var scopeWhere = this.association.scope ? {} : null;
if (this.association.scope) { if (this.association.scope) {
Object.keys(this.association.scope).forEach(function (attribute) { Object.keys(this.association.scope).forEach(function (attribute) {
...@@ -38,10 +38,10 @@ module.exports = (function() { ...@@ -38,10 +38,10 @@ module.exports = (function() {
} }
} }
return model.all(options, queryOptions); return model.all(options);
}; };
HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) { HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) {
var self = this var self = this
, primaryKeys , primaryKeys
, primaryKey , primaryKey
...@@ -104,9 +104,7 @@ module.exports = (function() { ...@@ -104,9 +104,7 @@ module.exports = (function() {
update = {}; update = {};
update[self.association.identifier] = (newAssociations.length < 1 ? null : self.instance[pkey] || self.instance.id); update[self.association.identifier] = (newAssociations.length < 1 ? null : self.instance[pkey] || self.instance.id);
if (this.association.scope) { if (this.association.scope) {
Object.keys(this.association.scope).forEach(function (attribute) { _.assign(update, this.association.scope);
update[attribute] = this.association.scope[attribute];
}.bind(this));
} }
updateWhere[primaryKey] = unassociatedIds; updateWhere[primaryKey] = unassociatedIds;
...@@ -121,9 +119,9 @@ module.exports = (function() { ...@@ -121,9 +119,9 @@ module.exports = (function() {
} }
return Utils.Promise.all(promises); return Utils.Promise.all(promises);
}; };
HasManySingleLinked.prototype.injectAdder = function(newAssociation, options) { HasManySingleLinked.prototype.injectAdder = function(newAssociation, options) {
newAssociation.set(this.association.identifier, this.instance.get(this.instance.Model.primaryKeyAttribute)); newAssociation.set(this.association.identifier, this.instance.get(this.instance.Model.primaryKeyAttribute));
if (this.association.scope) { if (this.association.scope) {
Object.keys(this.association.scope).forEach(function (attribute) { Object.keys(this.association.scope).forEach(function (attribute) {
...@@ -132,7 +130,6 @@ module.exports = (function() { ...@@ -132,7 +130,6 @@ module.exports = (function() {
} }
return newAssociation.save(options); return newAssociation.save(options);
}; };
return HasManySingleLinked; module.exports = HasManySingleLinked;
})();
...@@ -5,13 +5,12 @@ var Utils = require('./../utils') ...@@ -5,13 +5,12 @@ var Utils = require('./../utils')
, Association = require('./base') , Association = require('./base')
, util = require('util'); , util = require('util');
module.exports = (function() { var HasOne = function(srcModel, targetModel, options) {
var HasOne = function(srcDAO, targetDAO, options) {
Association.call(this); Association.call(this);
this.associationType = 'HasOne'; this.associationType = 'HasOne';
this.source = srcDAO; this.source = srcModel;
this.target = targetDAO; this.target = targetModel;
this.options = options; this.options = options;
this.isSingleAssociation = true; this.isSingleAssociation = true;
this.isSelfAssociation = (this.source === this.target); this.isSelfAssociation = (this.source === this.target);
...@@ -65,12 +64,12 @@ module.exports = (function() { ...@@ -65,12 +64,12 @@ module.exports = (function() {
set: 'set' + singular, set: 'set' + singular,
create: 'create' + singular create: 'create' + singular
}; };
}; };
util.inherits(HasOne, Association); util.inherits(HasOne, Association);
// the id is in the target table // the id is in the target table
HasOne.prototype.injectAttributes = function() { HasOne.prototype.injectAttributes = function() {
var newAttributes = {} var newAttributes = {}
, keyType = this.source.rawAttributes[this.sourceIdentifier].type; , keyType = this.source.rawAttributes[this.sourceIdentifier].type;
...@@ -85,15 +84,15 @@ module.exports = (function() { ...@@ -85,15 +84,15 @@ module.exports = (function() {
} }
Helpers.addForeignKeyConstraints(this.target.rawAttributes[this.identifier], this.source, this.target, this.options); Helpers.addForeignKeyConstraints(this.target.rawAttributes[this.identifier], this.source, this.target, this.options);
// Sync attributes and setters/getters to DAO prototype // Sync attributes and setters/getters to Model prototype
this.target.refreshAttributes(); this.target.refreshAttributes();
Helpers.checkNamingCollision(this); Helpers.checkNamingCollision(this);
return this; return this;
}; };
HasOne.prototype.injectGetter = function(instancePrototype) { HasOne.prototype.injectGetter = function(instancePrototype) {
var association = this; var association = this;
instancePrototype[this.accessors.get] = function(options) { instancePrototype[this.accessors.get] = function(options) {
...@@ -124,9 +123,9 @@ module.exports = (function() { ...@@ -124,9 +123,9 @@ module.exports = (function() {
}; };
return this; return this;
}; };
HasOne.prototype.injectSetter = function(instancePrototype) { HasOne.prototype.injectSetter = function(instancePrototype) {
var association = this; var association = this;
instancePrototype[this.accessors.set] = function(associatedInstance, options) { instancePrototype[this.accessors.set] = function(associatedInstance, options) {
...@@ -160,9 +159,9 @@ module.exports = (function() { ...@@ -160,9 +159,9 @@ module.exports = (function() {
}; };
return this; return this;
}; };
HasOne.prototype.injectCreator = function(instancePrototype) { HasOne.prototype.injectCreator = function(instancePrototype) {
var association = this; var association = this;
instancePrototype[this.accessors.create] = function(values, options) { instancePrototype[this.accessors.create] = function(values, options) {
...@@ -176,7 +175,6 @@ module.exports = (function() { ...@@ -176,7 +175,6 @@ module.exports = (function() {
}; };
return this; return this;
}; };
return HasOne; module.exports = HasOne;
})();
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
var Utils = require('./../utils'); var Utils = require('./../utils');
module.exports = { function checkNamingCollision (association) {
checkNamingCollision: function (association) {
if (association.source.rawAttributes.hasOwnProperty(association.as)) { if (association.source.rawAttributes.hasOwnProperty(association.as)) {
throw new Error( throw new Error(
'Naming collision between attribute \'' + association.as + 'Naming collision between attribute \'' + association.as +
...@@ -11,9 +10,9 @@ module.exports = { ...@@ -11,9 +10,9 @@ module.exports = {
'. To remedy this, change either foreignKey or as in your association definition' '. To remedy this, change either foreignKey or as in your association definition'
); );
} }
}, }
addForeignKeyConstraints: function(newAttribute, source, target, options) { function addForeignKeyConstraints (newAttribute, source, target, options) {
// FK constraints are opt-in: users must either set `foreignKeyConstraints` // FK constraints are opt-in: users must either set `foreignKeyConstraints`
// on the association, or request an `onDelete` or `onUpdate` behaviour // on the association, or request an `onDelete` or `onUpdate` behaviour
...@@ -26,22 +25,27 @@ module.exports = { ...@@ -26,22 +25,27 @@ module.exports = {
if (primaryKeys.length === 1) { if (primaryKeys.length === 1) {
if (!!source.options.schema) { if (!!source.options.schema) {
newAttribute.references = source.daoFactoryManager.sequelize.queryInterface.QueryGenerator.addSchema({ newAttribute.references = {
model: source.modelManager.sequelize.queryInterface.QueryGenerator.addSchema({
tableName: source.tableName, tableName: source.tableName,
options: { options: {
schema: source.options.schema, schema: source.options.schema,
schemaDelimiter: source.options.schemaDelimiter schemaDelimiter: source.options.schemaDelimiter
} }
}); })
};
} else { } else {
newAttribute.references = source.tableName; newAttribute.references = { model: source.tableName };
} }
newAttribute.referencesKey = primaryKeys[0]; newAttribute.references.key = primaryKeys[0];
newAttribute.onDelete = options.onDelete; newAttribute.onDelete = options.onDelete;
newAttribute.onUpdate = options.onUpdate; newAttribute.onUpdate = options.onUpdate;
} }
} }
} }
module.exports = {
checkNamingCollision: checkNamingCollision,
addForeignKeyConstraints: addForeignKeyConstraints
}; };
...@@ -12,7 +12,7 @@ var Utils = require('./../utils') ...@@ -12,7 +12,7 @@ var Utils = require('./../utils')
* *
* * hasOne - adds a foreign key to target * * hasOne - adds a foreign key to target
* * belongsTo - add a foreign key to source * * belongsTo - add a foreign key to source
* * hasMany - adds a foreign key to target, unless you also specifiy that target hasMany source, in which case a junction table is created with sourceId and targetId * * hasMany - adds a foreign key to target, unless you also specify that target hasMany source, in which case a junction table is created with sourceId and targetId
* *
* Creating an association will add a foreign key constraint to the attributes. All associations use `CASCADE` on update and `SET NULL` on delete, except for n:m, which also uses `CASCADE` on delete. * Creating an association will add a foreign key constraint to the attributes. All associations use `CASCADE` on update and `SET NULL` on delete, except for n:m, which also uses `CASCADE` on delete.
* *
...@@ -222,7 +222,7 @@ Mixin.belongsTo = singleLinked(BelongsTo); ...@@ -222,7 +222,7 @@ Mixin.belongsTo = singleLinked(BelongsTo);
* *
* Similarily, when fetching through a join table with custom attributes, these attributes will be available as an object with the name of the through model. * Similarily, when fetching through a join table with custom attributes, these attributes will be available as an object with the name of the through model.
* ```js * ```js
* user.getProjects().success(function (projects) { * user.getProjects().then(function (projects) {
* var p1 = projects[0] * var p1 = projects[0]
* p1.userprojects.started // Is this project started yet? * p1.userprojects.started // Is this project started yet?
* }) * })
......
'use strict'; 'use strict';
var util = require('util') var util = require('util')
, _ = require('lodash'); , _ = require('lodash')
, warnings = {};
/** /**
* A convenience class holding commonly used data types. The datatypes are used when definining a new model using `Sequelize.define`, like this: * A convenience class holding commonly used data types. The datatypes are used when definining a new model using `Sequelize.define`, like this:
...@@ -38,12 +39,20 @@ var ABSTRACT = function(options) { ...@@ -38,12 +39,20 @@ var ABSTRACT = function(options) {
}; };
ABSTRACT.prototype.dialectTypes = '';
ABSTRACT.prototype.toString = function() { ABSTRACT.prototype.toString = function() {
return this.toSql(); return this.toSql();
}; };
ABSTRACT.prototype.toSql = function() { ABSTRACT.prototype.toSql = function() {
return this.key; return this.key;
}; };
ABSTRACT.prototype.warn = function(text) {
if (!warnings[text]) {
warnings[text] = true;
console.warn('>> WARNING:', text, '\n>> Check:', this.dialectTypes);
}
};
/** /**
* A variable length string. Default length 255 * A variable length string. Default length 255
...@@ -101,18 +110,32 @@ CHAR.prototype.toSql = function() { ...@@ -101,18 +110,32 @@ CHAR.prototype.toSql = function() {
return 'CHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : ''); return 'CHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : '');
}; };
/** /**
* An unlimited length text column * An (un)limited length text column. Available lengths: `tiny`, `medium`, `long`
* @property TEXT * @property TEXT
*/ */
var TEXT = function(options) { var TEXT = function(length) {
var options = typeof length === 'object' && length || {
length: length
};
if (!(this instanceof TEXT)) return new TEXT(options); if (!(this instanceof TEXT)) return new TEXT(options);
this._length = options.length || '';
}; };
util.inherits(TEXT, ABSTRACT); util.inherits(TEXT, ABSTRACT);
TEXT.prototype.key = TEXT.key = 'TEXT'; TEXT.prototype.key = TEXT.key = 'TEXT';
TEXT.prototype.toSql = function() {
switch (this._length.toLowerCase()) {
case 'tiny':
return 'TINYTEXT';
case 'medium':
return 'MEDIUMTEXT';
case 'long':
return 'LONGTEXT';
default:
return this.key;
}
};
var NUMBER = function(options) { var NUMBER = function(options) {
this.options = options; this.options = options;
......
'use strict';
var util = require('util');
/**
* A collection of properties related to deferrable constraints. It can be used to
* make foreign key constraints deferrable and to set the constaints within a
* transaction. This is only supported in PostgreSQL.
*
* The foreign keys can be configured like this. It will create a foreign key
* that will check the constraints immediately when the data was inserted.
*
* ```js
* sequelize.define('Model', {
* foreign_id: {
* type: Sequelize.INTEGER,
* references: {
* model: OtherModel,
* key: 'id',
* deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE
* }
* }
* });
* ```
*
* The constraints can be configured in a transaction like this. It will
* trigger a query once the transaction has been started and set the constraints
* to be checked at the very end of the transaction.
*
* ```js
* sequelize.transaction({
* deferrable: Sequelize.Deferrable.SET_DEFERRED
* });
* ```
*
* @return {object}
*/
var Deferrable = module.exports = {
INITIALLY_DEFERRED: INITIALLY_DEFERRED,
INITIALLY_IMMEDIATE: INITIALLY_IMMEDIATE,
NOT: NOT,
SET_DEFERRED: SET_DEFERRED,
SET_IMMEDIATE: SET_IMMEDIATE
};
function ABSTRACT () {}
ABSTRACT.prototype.toString = function () {
return this.toSql.apply(this, arguments);
};
/**
* A property that will defer constraints checks to the end of transactions.
*
* @property INITIALLY_DEFERRED
*/
function INITIALLY_DEFERRED () {
if (!(this instanceof INITIALLY_DEFERRED)) {
return new INITIALLY_DEFERRED();
}
}
util.inherits(INITIALLY_DEFERRED, ABSTRACT);
INITIALLY_DEFERRED.prototype.toSql = function () {
return 'DEFERRABLE INITIALLY DEFERRED';
};
/**
* A property that will trigger the constraint checks immediately
*
* @property INITIALLY_IMMEDIATE
*/
function INITIALLY_IMMEDIATE () {
if (!(this instanceof INITIALLY_IMMEDIATE)) {
return new INITIALLY_IMMEDIATE();
}
}
util.inherits(INITIALLY_IMMEDIATE, ABSTRACT);
INITIALLY_IMMEDIATE.prototype.toSql = function () {
return 'DEFERRABLE INITIALLY IMMEDIATE';
};
/**
* A property that will set the constraints to not deferred. This is
* the default in PostgreSQL and it make it impossible to dynamically
* defer the constraints within a transaction.
*
* @property NOT
*/
function NOT () {
if (!(this instanceof NOT)) {
return new NOT();
}
}
util.inherits(NOT, ABSTRACT);
NOT.prototype.toSql = function () {
return 'NOT DEFERRABLE';
};
/**
* A property that will trigger an additional query at the beginning of a
* transaction which sets the constraints to deferred.
*
* @param {Array} constraints An array of constraint names. Will defer all constraints by default.
* @property SET_DEFERRED
*/
function SET_DEFERRED (constraints) {
if (!(this instanceof SET_DEFERRED)) {
return new SET_DEFERRED(constraints);
}
this.constraints = constraints;
}
util.inherits(SET_DEFERRED, ABSTRACT);
SET_DEFERRED.prototype.toSql = function (queryGenerator) {
return queryGenerator.setDeferredQuery(this.constraints);
};
/**
* A property that will trigger an additional query at the beginning of a
* transaction which sets the constraints to immediately.
*
* @param {Array} constraints An array of constraint names. Will defer all constraints by default.
* @property SET_IMMEDIATE
*/
function SET_IMMEDIATE (constraints) {
if (!(this instanceof SET_IMMEDIATE)) {
return new SET_IMMEDIATE(constraints);
}
this.constraints = constraints;
}
util.inherits(SET_IMMEDIATE, ABSTRACT);
SET_IMMEDIATE.prototype.toSql = function (queryGenerator) {
return queryGenerator.setImmediateQuery(this.constraints);
};
Object.keys(Deferrable).forEach(function (key) {
var DeferrableType = Deferrable[key];
DeferrableType.toString = function () {
var instance = new DeferrableType();
return instance.toString.apply(instance, arguments);
};
});
...@@ -46,7 +46,9 @@ AbstractDialect.prototype.supports = { ...@@ -46,7 +46,9 @@ AbstractDialect.prototype.supports = {
using: true, using: true,
}, },
joinTableDependent: true, joinTableDependent: true,
indexViaAlter: false,
JSON: false, JSON: false,
deferrableConstraints: false
}; };
module.exports = AbstractDialect; module.exports = AbstractDialect;
...@@ -4,6 +4,8 @@ var BaseTypes = require('../../data-types') ...@@ -4,6 +4,8 @@ var BaseTypes = require('../../data-types')
, util = require('util') , util = require('util')
, _ = require('lodash'); , _ = require('lodash');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://msdn.microsoft.com/en-us/library/ms187752%28v=sql.110%29.aspx';
var STRING = function() { var STRING = function() {
if (!(this instanceof STRING)) return new STRING(); if (!(this instanceof STRING)) return new STRING();
BaseTypes.STRING.apply(this, arguments); BaseTypes.STRING.apply(this, arguments);
...@@ -18,6 +20,19 @@ STRING.prototype.toSql = function() { ...@@ -18,6 +20,19 @@ STRING.prototype.toSql = function() {
} }
}; };
BaseTypes.TEXT.prototype.toSql = function() {
// TEXT is deprecated in mssql and it would normally be saved as a non-unicode string.
// Using unicode is just future proof
if (this._length) {
if (this._length.toLowerCase() === 'tiny') { // tiny = 2^8
this.warn('MSSQL does not support TEXT with the `length` = `tiny` option. `NVARCHAR(256)` will be used instead.');
return 'NVARCHAR(256)';
}
this.warn('MSSQL does not support TEXT with the `length` option. `NVARCHAR(MAX)` will be used instead.');
}
return 'NVARCHAR(MAX)';
};
var BOOLEAN = function() { var BOOLEAN = function() {
if (!(this instanceof BOOLEAN)) return new BOOLEAN(); if (!(this instanceof BOOLEAN)) return new BOOLEAN();
BaseTypes.BOOLEAN.apply(this, arguments); BaseTypes.BOOLEAN.apply(this, arguments);
...@@ -28,13 +43,14 @@ BOOLEAN.prototype.toSql = function() { ...@@ -28,13 +43,14 @@ BOOLEAN.prototype.toSql = function() {
return 'BIT'; return 'BIT';
}; };
var BLOB = function() { BaseTypes.BLOB.prototype.toSql = function() {
if (!(this instanceof BLOB)) return new BLOB(); if (this._length) {
BaseTypes.BLOB.apply(this, arguments); if (this._length.toLowerCase() === 'tiny') { // tiny = 2^8
}; this.warn('MSSQL does not support BLOB with the `length` = `tiny` option. `VARBINARY(256)` will be used instead.');
util.inherits(BLOB, BaseTypes.BLOB); return 'VARBINARY(256)';
}
BLOB.prototype.toSql = function() { this.warn('MSSQL does not support BLOB with the `length` option. `VARBINARY(MAX)` will be used instead.');
}
return 'VARBINARY(MAX)'; return 'VARBINARY(MAX)';
}; };
...@@ -68,13 +84,85 @@ DATE.prototype.toSql = function() { ...@@ -68,13 +84,85 @@ DATE.prototype.toSql = function() {
return 'DATETIME2'; return 'DATETIME2';
}; };
var INTEGER = function() {
if (!(this instanceof INTEGER)) return new INTEGER();
BaseTypes.INTEGER.apply(this, arguments);
// MSSQL does not support any options for integer
if (this._length || this.options.length || this._unsigned || this._zerofill) {
this.warn('MSSQL does not support INTEGER with options. Plain `INTEGER` will be used instead.');
this._length = undefined;
this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
}
};
util.inherits(INTEGER, BaseTypes.INTEGER);
var BIGINT = function() {
if (!(this instanceof BIGINT)) return new BIGINT();
BaseTypes.BIGINT.apply(this, arguments);
// MSSQL does not support any options for bigint
if (this._length || this.options.length || this._unsigned || this._zerofill) {
this.warn('MSSQL does not support BIGINT with options. Plain `BIGINT` will be used instead.');
this._length = undefined;
this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
}
};
util.inherits(BIGINT, BaseTypes.BIGINT);
var REAL = function() {
if (!(this instanceof REAL)) return new REAL();
BaseTypes.REAL.apply(this, arguments);
// MSSQL does not support any options for real
if (this._length || this.options.length || this._unsigned || this._zerofill) {
this.warn('MSSQL does not support REAL with options. Plain `REAL` will be used instead.');
this._length = undefined;
this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
}
};
util.inherits(REAL, BaseTypes.REAL);
var FLOAT = function() {
if (!(this instanceof FLOAT)) return new FLOAT();
BaseTypes.FLOAT.apply(this, arguments);
// MSSQL does only support lengths as option.
// Values between 1-24 result in 7 digits precision (4 bytes storage size)
// Values between 25-53 result in 15 digits precision (8 bytes storage size)
// If decimals are provided remove these and print a warning
if (this._decimals) {
this.warn('MSSQL does not support Float with decimals. Plain `FLOAT` will be used instead.');
this._length = undefined;
this.options.length = undefined;
}
if (this._unsigned) {
this.warn('MSSQL does not support Float unsigned. `UNSIGNED` was removed.');
this._unsigned = undefined;
}
if (this._zerofill) {
this.warn('MSSQL does not support Float zerofill. `ZEROFILL` was removed.');
this._zerofill = undefined;
}
};
util.inherits(FLOAT, BaseTypes.FLOAT);
module.exports = { module.exports = {
BOOLEAN: BOOLEAN, BOOLEAN: BOOLEAN,
STRING: STRING, STRING: STRING,
BLOB: BLOB,
UUID: UUID, UUID: UUID,
DATE: DATE, DATE: DATE,
NOW: NOW NOW: NOW,
INTEGER: INTEGER,
BIGINT: BIGINT,
REAL: REAL,
FLOAT: FLOAT
}; };
_.forIn(module.exports, function (DataType, key) { _.forIn(module.exports, function (DataType, key) {
......
...@@ -45,7 +45,8 @@ MssqlDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.support ...@@ -45,7 +45,8 @@ MssqlDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.support
parser: false, parser: false,
type: true, type: true,
using: false, using: false,
} },
NUMERIC: true
}); });
MssqlDialect.prototype.Query = Query; MssqlDialect.prototype.Query = Query;
......
...@@ -6,8 +6,12 @@ var Utils = require('../../utils') ...@@ -6,8 +6,12 @@ var Utils = require('../../utils')
, Model = require('../../model') , Model = require('../../model')
, AbstractQueryGenerator = require('../abstract/query-generator'); , AbstractQueryGenerator = require('../abstract/query-generator');
module.exports = (function() { /* istanbul ignore next */
var QueryGenerator = { var throwMethodUndefined = function(methodName) {
throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.');
};
var QueryGenerator = {
options: {}, options: {},
dialect: 'mssql', dialect: 'mssql',
...@@ -333,12 +337,16 @@ module.exports = (function() { ...@@ -333,12 +337,16 @@ module.exports = (function() {
} }
// handle self referential constraints // handle self referential constraints
if (attribute.Model && attribute.Model.tableName === attribute.references) { if (attribute.references) {
attribute = Utils.formatReferences(attribute);
if (attribute.Model && attribute.Model.tableName === attribute.references.model) {
this.sequelize.log('MSSQL does not support self referencial constraints, ' this.sequelize.log('MSSQL does not support self referencial constraints, '
+ 'we will remove it but we recommend restructuring your query'); + 'we will remove it but we recommend restructuring your query');
attribute.onDelete = ''; attribute.onDelete = '';
attribute.onUpdate = ''; attribute.onUpdate = '';
} }
}
var template; var template;
...@@ -380,10 +388,10 @@ module.exports = (function() { ...@@ -380,10 +388,10 @@ module.exports = (function() {
} }
if (attribute.references) { if (attribute.references) {
template += ' REFERENCES ' + this.quoteTable(attribute.references); template += ' REFERENCES ' + this.quoteTable(attribute.references.model);
if (attribute.referencesKey) { if (attribute.references.key) {
template += ' (' + this.quoteIdentifier(attribute.referencesKey) + ')'; template += ' (' + this.quoteIdentifier(attribute.references.key) + ')';
} else { } else {
template += ' (' + this.quoteIdentifier('id') + ')'; template += ' (' + this.quoteIdentifier('id') + ')';
} }
...@@ -410,12 +418,14 @@ module.exports = (function() { ...@@ -410,12 +418,14 @@ module.exports = (function() {
attribute = attributes[key]; attribute = attributes[key];
if (attribute.references) { if (attribute.references) {
if (existingConstraints.indexOf(attribute.references.toString()) !== -1) { attribute = Utils.formatReferences(attributes[key]);
if (existingConstraints.indexOf(attribute.references.model.toString()) !== -1) {
// no cascading constraints to a table more than once // no cascading constraints to a table more than once
attribute.onDelete = ''; attribute.onDelete = '';
attribute.onUpdate = ''; attribute.onUpdate = '';
} else { } else {
existingConstraints.push(attribute.references.toString()); existingConstraints.push(attribute.references.model.toString());
// NOTE: this really just disables cascading updates for all // NOTE: this really just disables cascading updates for all
// definitions. Can be made more robust to support the // definitions. Can be made more robust to support the
...@@ -570,7 +580,8 @@ module.exports = (function() { ...@@ -570,7 +580,8 @@ module.exports = (function() {
if (options.limit || options.offset) { if (options.limit || options.offset) {
if (!options.order || (options.include && !subQueryOrder.length)) { if (!options.order || (options.include && !subQueryOrder.length)) {
fragment += ' ORDER BY ' + this.quoteIdentifier(model.primaryKeyAttribute); fragment += (options.order && !isSubQuery) ? ', ' : ' ORDER BY ';
fragment += this.quoteIdentifier(model.primaryKeyAttribute);
} }
if (options.offset || options.limit) { if (options.offset || options.limit) {
...@@ -585,36 +596,14 @@ module.exports = (function() { ...@@ -585,36 +596,14 @@ module.exports = (function() {
return fragment; return fragment;
}, },
findAssociation: function() {
throwMethodUndefined('findAssociation');
},
getAssociationFilterDAO: function() {
throwMethodUndefined('getAssociationFilterDAO');
},
getAssociationFilterColumn: function() {
throwMethodUndefined('getAssociationFilterColumn');
},
getConditionalJoins: function() {
throwMethodUndefined('getConditionalJoins');
},
booleanValue: function(value) { booleanValue: function(value) {
return !!value ? 1 : 0; return !!value ? 1 : 0;
} }
}; };
// private methods // private methods
function wrapSingleQuote(identifier){ function wrapSingleQuote(identifier){
return Utils.addTicks(identifier, "'"); return Utils.addTicks(identifier, "'");
} }
/* istanbul ignore next */
var throwMethodUndefined = function(methodName) {
throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.');
};
return Utils._.extend(Utils._.clone(AbstractQueryGenerator), QueryGenerator); module.exports = Utils._.extend(Utils._.clone(AbstractQueryGenerator), QueryGenerator);
})();
...@@ -4,10 +4,10 @@ var Utils = require('../../utils') ...@@ -4,10 +4,10 @@ var Utils = require('../../utils')
, AbstractQuery = require('../abstract/query') , AbstractQuery = require('../abstract/query')
, sequelizeErrors = require('../../errors.js'); , sequelizeErrors = require('../../errors.js');
module.exports = (function() { var Query = function(connection, sequelize, options) {
var Query = function(connection, sequelize, callee, options) {
this.connection = connection; this.connection = connection;
this.callee = callee; this.instance = options.instance;
this.model = options.model;
this.sequelize = sequelize; this.sequelize = sequelize;
this.options = Utils._.extend({ this.options = Utils._.extend({
logging: console.log, logging: console.log,
...@@ -16,15 +16,15 @@ module.exports = (function() { ...@@ -16,15 +16,15 @@ module.exports = (function() {
}, options || {}); }, options || {});
this.checkLoggingOption(); this.checkLoggingOption();
}; };
Utils.inherit(Query, AbstractQuery); Utils.inherit(Query, AbstractQuery);
Query.prototype.getInsertIdField = function() { Query.prototype.getInsertIdField = function() {
return 'id'; return 'id';
}; };
Query.prototype.run = function(sql) { Query.prototype.run = function(sql) {
var self = this; var self = this;
this.sql = sql; this.sql = sql;
...@@ -83,9 +83,9 @@ module.exports = (function() { ...@@ -83,9 +83,9 @@ module.exports = (function() {
}); });
return promise; return promise;
}; };
/** /**
* High level function that handles the results of a query execution. * High level function that handles the results of a query execution.
* *
* *
...@@ -101,12 +101,12 @@ module.exports = (function() { ...@@ -101,12 +101,12 @@ module.exports = (function() {
* *
* @param {Array} data - The result of the query execution. * @param {Array} data - The result of the query execution.
*/ */
Query.prototype.formatResults = function(data) { Query.prototype.formatResults = function(data) {
var result = this.callee; var result = this.instance;
if (this.isInsertQuery(data)) { if (this.isInsertQuery(data)) {
this.handleInsertQuery(data); this.handleInsertQuery(data);
if (!this.callee) { if (!this.instance) {
if (this.options.plain) { if (this.options.plain) {
// NOTE: super contrived. This just passes the newly added query-interface // NOTE: super contrived. This just passes the newly added query-interface
// test returning only the PK. There isn't a way in MSSQL to identify // test returning only the PK. There isn't a way in MSSQL to identify
...@@ -154,25 +154,25 @@ module.exports = (function() { ...@@ -154,25 +154,25 @@ module.exports = (function() {
} }
return result; return result;
}; };
Query.prototype.handleShowTablesQuery = function(results) { Query.prototype.handleShowTablesQuery = function(results) {
return results.map(function(resultSet) { return results.map(function(resultSet) {
return { return {
tableName: resultSet.TABLE_NAME, tableName: resultSet.TABLE_NAME,
schema: resultSet.TABLE_SCHEMA schema: resultSet.TABLE_SCHEMA
}; };
}); });
}; };
Query.prototype.formatError = function (err) { Query.prototype.formatError = function (err) {
var match; var match;
match = err.message.match(/Violation of UNIQUE KEY constraint '((.|\s)*)'. Cannot insert duplicate key in object '.*'. The duplicate key value is \((.*)\)./); match = err.message.match(/Violation of UNIQUE KEY constraint '((.|\s)*)'. Cannot insert duplicate key in object '.*'. The duplicate key value is \((.*)\)./);
match = match || err.message.match(/Cannot insert duplicate key row in object .* with unique index '(.*)'/); match = match || err.message.match(/Cannot insert duplicate key row in object .* with unique index '(.*)'/);
if (match && match.length > 1) { if (match && match.length > 1) {
var fields = {} var fields = {}
, message = 'Validation error' , message = 'Validation error'
, uniqueKey = this.callee.__options.uniqueKeys[match[1]]; , uniqueKey = this.model.uniqueKeys[match[1]];
if (!!uniqueKey.msg) message = uniqueKey.msg; if (!!uniqueKey.msg) message = uniqueKey.msg;
if (!!match[2]) { if (!!match[2]) {
...@@ -209,9 +209,9 @@ module.exports = (function() { ...@@ -209,9 +209,9 @@ module.exports = (function() {
} }
return new sequelizeErrors.DatabaseError(err); return new sequelizeErrors.DatabaseError(err);
}; };
Query.prototype.isShowOrDescribeQuery = function() { Query.prototype.isShowOrDescribeQuery = function() {
var result = false; var result = false;
result = result || (this.sql.toLowerCase().indexOf("select c.column_name as 'name', c.data_type as 'type', c.is_nullable as 'isnull'") === 0); /* jshint ignore: line */ result = result || (this.sql.toLowerCase().indexOf("select c.column_name as 'name', c.data_type as 'type', c.is_nullable as 'isnull'") === 0); /* jshint ignore: line */
...@@ -219,13 +219,13 @@ module.exports = (function() { ...@@ -219,13 +219,13 @@ module.exports = (function() {
result = result || (this.sql.toLowerCase().indexOf('exec sys.sp_helpindex @objname') === 0); result = result || (this.sql.toLowerCase().indexOf('exec sys.sp_helpindex @objname') === 0);
return result; return result;
}; };
Query.prototype.isShowIndexesQuery = function () { Query.prototype.isShowIndexesQuery = function () {
return this.sql.toLowerCase().indexOf('exec sys.sp_helpindex @objname') === 0; return this.sql.toLowerCase().indexOf('exec sys.sp_helpindex @objname') === 0;
}; };
Query.prototype.handleShowIndexesQuery = function (data) { Query.prototype.handleShowIndexesQuery = function (data) {
// Group by index name, and collect all fields // Group by index name, and collect all fields
data = Utils._.foldl(data, function (acc, item) { data = Utils._.foldl(data, function (acc, item) {
if (!(item.index_name in acc)) { if (!(item.index_name in acc)) {
...@@ -260,27 +260,26 @@ module.exports = (function() { ...@@ -260,27 +260,26 @@ module.exports = (function() {
type: undefined, type: undefined,
}; };
}); });
}; };
Query.prototype.handleInsertQuery = function(results, metaData) { Query.prototype.handleInsertQuery = function(results, metaData) {
if (this.callee) { if (this.instance) {
// add the inserted row id to the instance // add the inserted row id to the instance
var autoIncrementField = this.callee.Model.autoIncrementField var autoIncrementField = this.model.autoIncrementField
, autoIncrementFieldAlias = null , autoIncrementFieldAlias = null
, id = null; , id = null;
if (this.callee.Model.rawAttributes.hasOwnProperty(autoIncrementField) && if (this.model.rawAttributes.hasOwnProperty(autoIncrementField) &&
this.callee.Model.rawAttributes[autoIncrementField].field !== undefined) this.model.rawAttributes[autoIncrementField].field !== undefined)
autoIncrementFieldAlias = this.callee.Model.rawAttributes[autoIncrementField].field ; autoIncrementFieldAlias = this.model.rawAttributes[autoIncrementField].field ;
id = id || (results && results[0][this.getInsertIdField()]); id = id || (results && results[0][this.getInsertIdField()]);
id = id || (metaData && metaData[this.getInsertIdField()]); id = id || (metaData && metaData[this.getInsertIdField()]);
id = id || (results && results[0][autoIncrementField]); id = id || (results && results[0][autoIncrementField]);
id = id || (autoIncrementFieldAlias && results && results[0][autoIncrementFieldAlias]); id = id || (autoIncrementFieldAlias && results && results[0][autoIncrementFieldAlias]);
this.callee[autoIncrementField] = id; this.instance[autoIncrementField] = id;
} }
}; };
return Query; module.exports = Query;
})();
...@@ -4,6 +4,8 @@ var BaseTypes = require('../../data-types') ...@@ -4,6 +4,8 @@ var BaseTypes = require('../../data-types')
, util = require('util') , util = require('util')
, _ = require('lodash'); , _ = require('lodash');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://dev.mysql.com/doc/refman/5.7/en/data-types.html';
var UUID = function() { var UUID = function() {
if (!(this instanceof UUID)) return new UUID(); if (!(this instanceof UUID)) return new UUID();
BaseTypes.UUID.apply(this, arguments); BaseTypes.UUID.apply(this, arguments);
......
...@@ -31,6 +31,7 @@ MysqlDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.support ...@@ -31,6 +31,7 @@ MysqlDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.support
type: true, type: true,
using: 1, using: 1,
}, },
indexViaAlter: true,
NUMERIC: true NUMERIC: true
}); });
......
...@@ -3,8 +3,7 @@ ...@@ -3,8 +3,7 @@
var Utils = require('../../utils') var Utils = require('../../utils')
, DataTypes = require('../../data-types'); , DataTypes = require('../../data-types');
module.exports = (function() { var QueryGenerator = {
var QueryGenerator = {
dialect: 'mysql', dialect: 'mysql',
createSchema: function() { createSchema: function() {
...@@ -291,11 +290,16 @@ module.exports = (function() { ...@@ -291,11 +290,16 @@ module.exports = (function() {
template += ' PRIMARY KEY'; template += ' PRIMARY KEY';
} }
if (attribute.after) {
template += ' AFTER ' + this.quoteIdentifier(attribute.after);
}
if (attribute.references) { if (attribute.references) {
template += ' REFERENCES ' + this.quoteTable(attribute.references); attribute = Utils.formatReferences(attribute);
template += ' REFERENCES ' + this.quoteTable(attribute.references.model);
if (attribute.referencesKey) { if (attribute.references.key) {
template += ' (' + this.quoteIdentifier(attribute.referencesKey) + ')'; template += ' (' + this.quoteIdentifier(attribute.references.key) + ')';
} else { } else {
template += ' (' + this.quoteIdentifier('id') + ')'; template += ' (' + this.quoteIdentifier('id') + ')';
} }
...@@ -367,7 +371,6 @@ module.exports = (function() { ...@@ -367,7 +371,6 @@ module.exports = (function() {
dropForeignKeyQuery: function(tableName, foreignKey) { dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';'; return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';';
} }
}; };
return Utils._.extend(Utils._.clone(require('../abstract/query-generator')), QueryGenerator); module.exports = Utils._.extend(Utils._.clone(require('../abstract/query-generator')), QueryGenerator);
})();
...@@ -5,10 +5,10 @@ var Utils = require('../../utils') ...@@ -5,10 +5,10 @@ var Utils = require('../../utils')
, uuid = require('node-uuid') , uuid = require('node-uuid')
, sequelizeErrors = require('../../errors.js'); , sequelizeErrors = require('../../errors.js');
module.exports = (function() { var Query = function(connection, sequelize, options) {
var Query = function(connection, sequelize, callee, options) {
this.connection = connection; this.connection = connection;
this.callee = callee; this.instance = options.instance;
this.model = options.model;
this.sequelize = sequelize; this.sequelize = sequelize;
this.uuid = uuid.v4(); this.uuid = uuid.v4();
this.options = Utils._.extend({ this.options = Utils._.extend({
...@@ -18,10 +18,10 @@ module.exports = (function() { ...@@ -18,10 +18,10 @@ module.exports = (function() {
}, options || {}); }, options || {});
this.checkLoggingOption(); this.checkLoggingOption();
}; };
Utils.inherit(Query, AbstractQuery); Utils.inherit(Query, AbstractQuery);
Query.prototype.run = function(sql) { Query.prototype.run = function(sql) {
var self = this; var self = this;
this.sql = sql; this.sql = sql;
...@@ -40,7 +40,7 @@ module.exports = (function() { ...@@ -40,7 +40,7 @@ module.exports = (function() {
}); });
return promise; return promise;
}; };
/** /**
* High level function that handles the results of a query execution. * High level function that handles the results of a query execution.
...@@ -58,13 +58,13 @@ module.exports = (function() { ...@@ -58,13 +58,13 @@ module.exports = (function() {
* *
* @param {Array} data - The result of the query execution. * @param {Array} data - The result of the query execution.
*/ */
Query.prototype.formatResults = function(data) { Query.prototype.formatResults = function(data) {
var result = this.callee; var result = this.instance;
if (this.isInsertQuery(data)) { if (this.isInsertQuery(data)) {
this.handleInsertQuery(data); this.handleInsertQuery(data);
if (!this.callee) { if (!this.instance) {
result = data[this.getInsertIdField()]; result = data[this.getInsertIdField()];
} }
} }
...@@ -100,10 +100,10 @@ module.exports = (function() { ...@@ -100,10 +100,10 @@ module.exports = (function() {
} }
return result; return result;
}; };
Query.prototype.formatError = function (err) { Query.prototype.formatError = function (err) {
var match; var match;
switch (err.errno || err.code) { switch (err.errno || err.code) {
...@@ -113,7 +113,7 @@ module.exports = (function() { ...@@ -113,7 +113,7 @@ module.exports = (function() {
var values = match ? match[1].split('-') : undefined var values = match ? match[1].split('-') : undefined
, fields = {} , fields = {}
, message = 'Validation error' , message = 'Validation error'
, uniqueKey = this.callee && this.callee.__options.uniqueKeys[match[2]]; , uniqueKey = this.model && this.model.uniqueKeys[match[2]];
if (!!uniqueKey) { if (!!uniqueKey) {
if (!!uniqueKey.msg) message = uniqueKey.msg; if (!!uniqueKey.msg) message = uniqueKey.msg;
...@@ -156,9 +156,9 @@ module.exports = (function() { ...@@ -156,9 +156,9 @@ module.exports = (function() {
default: default:
return new sequelizeErrors.DatabaseError(err); return new sequelizeErrors.DatabaseError(err);
} }
}; };
Query.prototype.handleShowIndexesQuery = function (data) { Query.prototype.handleShowIndexesQuery = function (data) {
// Group by index name, and collect all fields // Group by index name, and collect all fields
data = Utils._.foldl(data, function (acc, item) { data = Utils._.foldl(data, function (acc, item) {
if (!(item.Key_name in acc)) { if (!(item.Key_name in acc)) {
...@@ -186,7 +186,6 @@ module.exports = (function() { ...@@ -186,7 +186,6 @@ module.exports = (function() {
type: item.Index_type, type: item.Index_type,
}; };
}); });
}; };
return Query; module.exports = Query;
})();
...@@ -4,6 +4,8 @@ var BaseTypes = require('../../data-types') ...@@ -4,6 +4,8 @@ var BaseTypes = require('../../data-types')
, util = require('util') , util = require('util')
, _ = require('lodash'); , _ = require('lodash');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'http://www.postgresql.org/docs/9.4/static/datatype.html';
var STRING = function() { var STRING = function() {
if (!(this instanceof STRING)) return new STRING(); if (!(this instanceof STRING)) return new STRING();
BaseTypes.STRING.apply(this, arguments); BaseTypes.STRING.apply(this, arguments);
...@@ -17,6 +19,14 @@ STRING.prototype.toSql = function() { ...@@ -17,6 +19,14 @@ STRING.prototype.toSql = function() {
return BaseTypes.STRING.prototype.toSql.call(this); return BaseTypes.STRING.prototype.toSql.call(this);
}; };
BaseTypes.TEXT.prototype.toSql = function() {
if (this._length) {
this.warn('PostgreSQL does not support TEXT with options. Plain `TEXT` will be used instead.');
this._length = undefined;
}
return 'TEXT';
};
var CHAR = function() { var CHAR = function() {
if (!(this instanceof CHAR)) return new CHAR(); if (!(this instanceof CHAR)) return new CHAR();
BaseTypes.CHAR.apply(this, arguments); BaseTypes.CHAR.apply(this, arguments);
...@@ -55,10 +65,13 @@ var INTEGER = function() { ...@@ -55,10 +65,13 @@ var INTEGER = function() {
BaseTypes.INTEGER.apply(this, arguments); BaseTypes.INTEGER.apply(this, arguments);
// POSTGRES does not support any parameters for integer // POSTGRES does not support any parameters for integer
this._length = null; if (this._length || this.options.length || this._unsigned || this._zerofill) {
this.options.length = null; this.warn('PostgreSQL does not support INTEGER with options. Plain `INTEGER` will be used instead.');
this._unsigned = null; this._length = undefined;
this._zerofill = null; this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
}
}; };
util.inherits(INTEGER, BaseTypes.INTEGER); util.inherits(INTEGER, BaseTypes.INTEGER);
...@@ -67,10 +80,13 @@ var BIGINT = function() { ...@@ -67,10 +80,13 @@ var BIGINT = function() {
BaseTypes.BIGINT.apply(this, arguments); BaseTypes.BIGINT.apply(this, arguments);
// POSTGRES does not support any parameters for bigint // POSTGRES does not support any parameters for bigint
this._length = null; if (this._length || this.options.length || this._unsigned || this._zerofill) {
this.options.length = null; this.warn('PostgreSQL does not support BIGINT with options. Plain `BIGINT` will be used instead.');
this._unsigned = null; this._length = undefined;
this._zerofill = null; this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
}
}; };
util.inherits(BIGINT, BaseTypes.BIGINT); util.inherits(BIGINT, BaseTypes.BIGINT);
...@@ -79,10 +95,13 @@ var REAL = function() { ...@@ -79,10 +95,13 @@ var REAL = function() {
BaseTypes.REAL.apply(this, arguments); BaseTypes.REAL.apply(this, arguments);
// POSTGRES does not support any parameters for real // POSTGRES does not support any parameters for real
this._length = null; if (this._length || this.options.length || this._unsigned || this._zerofill) {
this.options.length = null; this.warn('PostgreSQL does not support REAL with options. Plain `REAL` will be used instead.');
this._unsigned = null; this._length = undefined;
this._zerofill = null; this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
}
}; };
util.inherits(REAL, BaseTypes.REAL); util.inherits(REAL, BaseTypes.REAL);
...@@ -91,10 +110,13 @@ var DOUBLE = function() { ...@@ -91,10 +110,13 @@ var DOUBLE = function() {
BaseTypes.DOUBLE.apply(this, arguments); BaseTypes.DOUBLE.apply(this, arguments);
// POSTGRES does not support any parameters for double // POSTGRES does not support any parameters for double
this._length = null; if (this._length || this.options.length || this._unsigned || this._zerofill) {
this.options.length = null; this.warn('PostgreSQL does not support DOUBLE with options. Plain `DOUBLE` will be used instead.');
this._unsigned = null; this._length = undefined;
this._zerofill = null; this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
}
}; };
util.inherits(DOUBLE, BaseTypes.DOUBLE); util.inherits(DOUBLE, BaseTypes.DOUBLE);
...@@ -102,25 +124,32 @@ var FLOAT = function() { ...@@ -102,25 +124,32 @@ var FLOAT = function() {
if (!(this instanceof FLOAT)) return new FLOAT(); if (!(this instanceof FLOAT)) return new FLOAT();
BaseTypes.FLOAT.apply(this, arguments); BaseTypes.FLOAT.apply(this, arguments);
// POSTGRES does not support any parameters for float // POSTGRES does only support lengths as parameter.
this._length = null; // Values between 1-24 result in REAL
this.options.length = null; // Values between 25-53 result in DOUBLE PRECISION
this._unsigned = null; // If decimals are provided remove these and print a warning
this._zerofill = null; if (this._decimals) {
this.warn('PostgreSQL does not support FLOAT with decimals. Plain `FLOAT` will be used instead.');
this._length = undefined;
this.options.length = undefined;
this._decimals = undefined;
}
if (this._unsigned) {
this.warn('PostgreSQL does not support FLOAT unsigned. `UNSIGNED` was removed.');
this._unsigned = undefined;
}
if (this._zerofill) {
this.warn('PostgreSQL does not support FLOAT zerofill. `ZEROFILL` was removed.');
this._zerofill = undefined;
}
}; };
util.inherits(FLOAT, BaseTypes.FLOAT); util.inherits(FLOAT, BaseTypes.FLOAT);
FLOAT.prototype.toSql = function() { BaseTypes.BLOB.prototype.toSql = function() {
return 'DOUBLE PRECISION'; if (this._length) {
}; this.warn('PostgreSQL does not support BLOB (BYTEA) with options. Plain `BYTEA` will be used instead.');
this._length = undefined;
var BLOB = function() { }
if (!(this instanceof BLOB)) return new BLOB();
BaseTypes.BLOB.apply(this, arguments);
};
util.inherits(BLOB, BaseTypes.BLOB);
BLOB.prototype.toSql = function() {
return 'BYTEA'; return 'BYTEA';
}; };
...@@ -131,7 +160,6 @@ module.exports = { ...@@ -131,7 +160,6 @@ module.exports = {
DATE: DATE, DATE: DATE,
INTEGER: INTEGER, INTEGER: INTEGER,
BIGINT: BIGINT, BIGINT: BIGINT,
BLOB: BLOB,
REAL: REAL, REAL: REAL,
'DOUBLE PRECISION': DOUBLE, 'DOUBLE PRECISION': DOUBLE,
FLOAT: FLOAT FLOAT: FLOAT
......
...@@ -2,15 +2,17 @@ ...@@ -2,15 +2,17 @@
var hstore = require('pg-hstore')({sanitize : true}); var hstore = require('pg-hstore')({sanitize : true});
module.exports = { function stringify (data) {
stringify: function(data) { if (data === null) return null;
if(data === null) return null;
return hstore.stringify(data); return hstore.stringify(data);
}, }
parse: function(value) {
if(value === null) return null;
function parse (value) {
if (value === null) return null;
return hstore.parse(value); return hstore.parse(value);
} }
module.exports = {
stringify: stringify,
parse: parse
}; };
...@@ -44,7 +44,8 @@ PostgresDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supp ...@@ -44,7 +44,8 @@ PostgresDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supp
NUMERIC: true, NUMERIC: true,
ARRAY: true, ARRAY: true,
JSON: true, JSON: true,
JSONB: true JSONB: true,
deferrableConstraints: true
}); });
PostgresDialect.prototype.Query = Query; PostgresDialect.prototype.Query = Query;
......
...@@ -10,8 +10,7 @@ var Utils = require('../../utils') ...@@ -10,8 +10,7 @@ var Utils = require('../../utils')
, AbstractQueryGenerator = require('../abstract/query-generator') , AbstractQueryGenerator = require('../abstract/query-generator')
, primaryKeys = {}; , primaryKeys = {};
module.exports = (function() { var QueryGenerator = {
var QueryGenerator = {
options: {}, options: {},
dialect: 'postgres', dialect: 'postgres',
...@@ -281,7 +280,7 @@ module.exports = (function() { ...@@ -281,7 +280,7 @@ module.exports = (function() {
language = language || 'plpgsql'; language = language || 'plpgsql';
returns = returns || 'SETOF ' + this.quoteTable(tableName); returns = returns || 'SETOF ' + this.quoteTable(tableName);
var query = 'CREATE OR REPLACE FUNCTION pg_temp.<%= fnName %>() RETURNS <%= returns %> AS $$ BEGIN <%= body %> END; $$ LANGUAGE <%= language %>; SELECT * FROM pg_temp.<%= fnName %>();'; var query = 'CREATE OR REPLACE FUNCTION pg_temp.<%= fnName %>() RETURNS <%= returns %> AS $func$ BEGIN <%= body %> END; $func$ LANGUAGE <%= language %>; SELECT * FROM pg_temp.<%= fnName %>();';
return Utils._.template(query)({ return Utils._.template(query)({
fnName: fnName, fnName: fnName,
...@@ -426,6 +425,10 @@ module.exports = (function() { ...@@ -426,6 +425,10 @@ module.exports = (function() {
}, },
showIndexesQuery: function(tableName) { showIndexesQuery: function(tableName) {
if (!Utils._.isString(tableName)) {
tableName = tableName.tableName;
}
// This is ARCANE! // This is ARCANE!
var query = 'SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, ' + var query = 'SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, ' +
'array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) ' + 'array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) ' +
...@@ -506,11 +509,12 @@ module.exports = (function() { ...@@ -506,11 +509,12 @@ module.exports = (function() {
} }
if (attribute.references) { if (attribute.references) {
attribute = Utils.formatReferences(attribute);
template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)'; template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)';
replacements.referencesTable = this.quoteTable(attribute.references); replacements.referencesTable = this.quoteTable(attribute.references.model);
if (attribute.referencesKey) { if (attribute.references.key) {
replacements.referencesKey = this.quoteIdentifiers(attribute.referencesKey); replacements.referencesKey = this.quoteIdentifiers(attribute.references.key);
} else { } else {
replacements.referencesKey = this.quoteIdentifier('id'); replacements.referencesKey = this.quoteIdentifier('id');
} }
...@@ -524,11 +528,40 @@ module.exports = (function() { ...@@ -524,11 +528,40 @@ module.exports = (function() {
template += ' ON UPDATE <%= onUpdateAction %>'; template += ' ON UPDATE <%= onUpdateAction %>';
replacements.onUpdateAction = attribute.onUpdate.toUpperCase(); replacements.onUpdateAction = attribute.onUpdate.toUpperCase();
} }
if (attribute.references.deferrable) {
template += ' <%= deferrable %>';
replacements.deferrable = attribute.references.deferrable.toString(this);
}
} }
return Utils._.template(template)(replacements); return Utils._.template(template)(replacements);
}, },
deferConstraintsQuery: function (options) {
return options.deferrable.toString(this);
},
setConstraintQuery: function (columns, type) {
var columnFragment = 'ALL';
if (columns) {
columnFragment = columns.map(function (column) {
return this.quoteIdentifier(column);
}.bind(this)).join(', ');
}
return 'SET CONSTRAINTS ' + columnFragment + ' ' + type;
},
setDeferredQuery: function (columns) {
return this.setConstraintQuery(columns, 'DEFERRED');
},
setImmediateQuery: function (columns) {
return this.setConstraintQuery(columns, 'IMMEDIATE');
},
attributesToSQL: function(attributes, options) { attributesToSQL: function(attributes, options) {
var result = {} var result = {}
, key , key
...@@ -596,11 +629,11 @@ module.exports = (function() { ...@@ -596,11 +629,11 @@ module.exports = (function() {
createFunction: function(functionName, params, returnType, language, body, options) { createFunction: function(functionName, params, returnType, language, body, options) {
var sql = ['CREATE FUNCTION <%= functionName %>(<%= paramList %>)' var sql = ['CREATE FUNCTION <%= functionName %>(<%= paramList %>)'
, 'RETURNS <%= returnType %> AS $$' , 'RETURNS <%= returnType %> AS $func$'
, 'BEGIN' , 'BEGIN'
, '\t<%= body %>' , '\t<%= body %>'
, 'END;' , 'END;'
, "$$ language '<%= language %>'<%= options %>;" , "$func$ language '<%= language %>'<%= options %>;"
].join('\n'); ].join('\n');
return Utils._.template(sql)({ return Utils._.template(sql)({
...@@ -857,12 +890,13 @@ module.exports = (function() { ...@@ -857,12 +890,13 @@ module.exports = (function() {
else if (DataTypes.ARRAY.is(field.type, DataTypes.RANGE)) { // escape array of ranges else if (DataTypes.ARRAY.is(field.type, DataTypes.RANGE)) { // escape array of ranges
return 'ARRAY[' + Utils._.map(value, function(v){return "'" + range.stringify(v) + "'";}).join(',') + ']::' + field.type.toString(); return 'ARRAY[' + Utils._.map(value, function(v){return "'" + range.stringify(v) + "'";}).join(',') + ']::' + field.type.toString();
} }
} else if (field && (field.type instanceof DataTypes.JSON || field.type instanceof DataTypes.JSONB)) { } else if (field && field.type instanceof DataTypes.JSON) {
value = JSON.stringify(value); value = JSON.stringify(value);
} else if (Array.isArray(value) && field && DataTypes.ARRAY.is(field.type, DataTypes.JSON)) { } else if (Array.isArray(value) && field && DataTypes.ARRAY.is(field.type, DataTypes.JSON)) {
var jsonType = field.type.type; // type may be JSON or JSONB
return 'ARRAY[' + value.map(function (v) { return 'ARRAY[' + value.map(function (v) {
return SqlString.escape(JSON.stringify(v), false, this.options.timezone, this.dialect, field); return SqlString.escape(JSON.stringify(v), false, this.options.timezone, this.dialect, field);
}, this).join(',') + ']::JSON[]'; }, this).join(',') + ']::' + jsonType.key + '[]';
} }
return SqlString.escape(value, false, this.options.timezone, this.dialect, field); return SqlString.escape(value, false, this.options.timezone, this.dialect, field);
...@@ -904,7 +938,6 @@ module.exports = (function() { ...@@ -904,7 +938,6 @@ module.exports = (function() {
return AbstractQueryGenerator.setAutocommitQuery.call(this, value, options); return AbstractQueryGenerator.setAutocommitQuery.call(this, value, options);
} }
}; };
return Utils._.extend(Utils._.clone(AbstractQueryGenerator), QueryGenerator); module.exports = Utils._.extend(Utils._.clone(AbstractQueryGenerator), QueryGenerator);
})();
...@@ -66,11 +66,11 @@ function dialectSpecificFieldDatatypeMap (options, prefix) { ...@@ -66,11 +66,11 @@ function dialectSpecificFieldDatatypeMap (options, prefix) {
return fields; return fields;
} }
module.exports = (function() { var Query = function(client, sequelize, options) {
var Query = function(client, sequelize, callee, options) {
this.client = client; this.client = client;
this.sequelize = sequelize; this.sequelize = sequelize;
this.callee = callee; this.instance = options.instance;
this.model = options.model;
this.options = Utils._.extend({ this.options = Utils._.extend({
logging: console.log, logging: console.log,
plain: false, plain: false,
...@@ -78,13 +78,12 @@ module.exports = (function() { ...@@ -78,13 +78,12 @@ module.exports = (function() {
}, options || {}); }, options || {});
this.checkLoggingOption(); this.checkLoggingOption();
}; };
Utils.inherit(Query, AbstractQuery); Utils.inherit(Query, AbstractQuery);
Query.prototype.parseDialectSpecificFields = parseDialectSpecificFields; Query.prototype.parseDialectSpecificFields = parseDialectSpecificFields;
Query.prototype.run = function(sql) { Query.prototype.run = function(sql) {
/* jshint -W027 */
this.sql = sql; this.sql = sql;
var self = this var self = this
...@@ -200,11 +199,11 @@ module.exports = (function() { ...@@ -200,11 +199,11 @@ module.exports = (function() {
// Postgres will treat tables as case-insensitive, so fix the case // Postgres will treat tables as case-insensitive, so fix the case
// of the returned values to match attributes // of the returned values to match attributes
if (self.options.raw === false && self.sequelize.options.quoteIdentifiers === false) { if (self.options.raw === false && self.sequelize.options.quoteIdentifiers === false) {
var attrsMap = Utils._.reduce(self.callee.attributes, function(m, v, k) { m[k.toLowerCase()] = k; return m; }, {}); var attrsMap = Utils._.reduce(self.model.attributes, function(m, v, k) { m[k.toLowerCase()] = k; return m; }, {});
rows.forEach(function(row) { rows.forEach(function(row) {
Utils._.keys(row).forEach(function(key) { Utils._.keys(row).forEach(function(key) {
var targetAttr = attrsMap[key]; var targetAttr = attrsMap[key];
if (targetAttr !== key) { if (typeof targetAttr === 'string' && targetAttr !== key) {
row[targetAttr] = row[key]; row[targetAttr] = row[key];
delete row[key]; delete row[key];
} }
...@@ -212,9 +211,9 @@ module.exports = (function() { ...@@ -212,9 +211,9 @@ module.exports = (function() {
}); });
} }
if (!!self.callee && rows.length > 0) { if (!!self.model && rows.length > 0) {
dialectSpecificFields = dialectSpecificFieldDatatypeMap({ dialectSpecificFields = dialectSpecificFieldDatatypeMap({
callee: self.callee, callee: self.model,
include: self.options.include, include: self.options.include,
types: dialectSpecificTypes types: dialectSpecificTypes
}); });
...@@ -271,9 +270,9 @@ module.exports = (function() { ...@@ -271,9 +270,9 @@ module.exports = (function() {
return parseInt(result.rowCount, 10); return parseInt(result.rowCount, 10);
} }
if (!!self.callee && rows.length > 0) { if (!!self.model && rows.length > 0) {
dialectSpecificFields = dialectSpecificFieldDatatypeMap({ dialectSpecificFields = dialectSpecificFieldDatatypeMap({
callee: self.callee, callee: self.model,
types: dialectSpecificTypes types: dialectSpecificTypes
}); });
...@@ -295,10 +294,10 @@ module.exports = (function() { ...@@ -295,10 +294,10 @@ module.exports = (function() {
} else if (self.isUpsertQuery()) { } else if (self.isUpsertQuery()) {
return rows[0].sequelize_upsert; return rows[0].sequelize_upsert;
} else if (self.isInsertQuery() || self.isUpdateQuery()) { } else if (self.isInsertQuery() || self.isUpdateQuery()) {
if (!!self.callee && self.callee.dataValues) { if (self.instance && self.instance.dataValues) {
if (!!self.callee.Model) { if (self.model) {
dialectSpecificFields = dialectSpecificFieldDatatypeMap({ dialectSpecificFields = dialectSpecificFieldDatatypeMap({
callee: self.callee.Model, callee: self.model,
types: dialectSpecificTypes types: dialectSpecificTypes
}); });
...@@ -316,16 +315,16 @@ module.exports = (function() { ...@@ -316,16 +315,16 @@ module.exports = (function() {
if (rows[0].hasOwnProperty(key)) { if (rows[0].hasOwnProperty(key)) {
var record = rows[0][key]; var record = rows[0][key];
var attr = Utils._.find(self.callee.Model.rawAttributes, function (attribute) { var attr = Utils._.find(self.model.rawAttributes, function (attribute) {
return attribute.fieldName === key || attribute.field === key; return attribute.fieldName === key || attribute.field === key;
}); });
self.callee.dataValues[attr && attr.fieldName || key] = record; self.instance.dataValues[attr && attr.fieldName || key] = record;
} }
} }
} }
return self.callee || (rows && ((self.options.plain && rows[0]) || rows)) || undefined; return self.instance || (rows && ((self.options.plain && rows[0]) || rows)) || undefined;
} else if (self.isVersionQuery()) { } else if (self.isVersionQuery()) {
return results[0].version; return results[0].version;
} else if (self.isRawQuery()) { } else if (self.isRawQuery()) {
...@@ -336,9 +335,9 @@ module.exports = (function() { ...@@ -336,9 +335,9 @@ module.exports = (function() {
}); });
return promise; return promise;
}; };
Query.prototype.formatError = function (err) { Query.prototype.formatError = function (err) {
var match var match
, table , table
, index , index
...@@ -378,8 +377,8 @@ module.exports = (function() { ...@@ -378,8 +377,8 @@ module.exports = (function() {
field + ' must be unique', 'unique violation', field, value)); field + ' must be unique', 'unique violation', field, value));
}); });
if (this.callee && this.callee.__options && this.callee.__options.uniqueKeys) { if (this.model && this.model.uniqueKeys) {
Utils._.forOwn(this.callee.__options.uniqueKeys, function(constraint) { Utils._.forOwn(this.model.uniqueKeys, function(constraint) {
if (Utils._.isEqual(constraint.fields, Object.keys(fields)) && !!constraint.msg) { if (Utils._.isEqual(constraint.fields, Object.keys(fields)) && !!constraint.msg) {
message = constraint.msg; message = constraint.msg;
return false; return false;
...@@ -420,15 +419,14 @@ module.exports = (function() { ...@@ -420,15 +419,14 @@ module.exports = (function() {
default: default:
return new sequelizeErrors.DatabaseError(err); return new sequelizeErrors.DatabaseError(err);
} }
}; };
Query.prototype.isForeignKeysQuery = function() { Query.prototype.isForeignKeysQuery = function() {
return /SELECT conname as constraint_name, pg_catalog\.pg_get_constraintdef\(r\.oid, true\) as condef FROM pg_catalog\.pg_constraint r WHERE r\.conrelid = \(SELECT oid FROM pg_class WHERE relname = '.*' LIMIT 1\) AND r\.contype = 'f' ORDER BY 1;/.test(this.sql); return /SELECT conname as constraint_name, pg_catalog\.pg_get_constraintdef\(r\.oid, true\) as condef FROM pg_catalog\.pg_constraint r WHERE r\.conrelid = \(SELECT oid FROM pg_class WHERE relname = '.*' LIMIT 1\) AND r\.contype = 'f' ORDER BY 1;/.test(this.sql);
}; };
Query.prototype.getInsertIdField = function() { Query.prototype.getInsertIdField = function() {
return 'id'; return 'id';
}; };
return Query; module.exports = Query;
})();
...@@ -3,8 +3,7 @@ ...@@ -3,8 +3,7 @@
var Utils = require('../../utils'), var Utils = require('../../utils'),
moment = require('moment'); moment = require('moment');
module.exports = { function stringify (data) {
stringify: function (data) {
if (data === null) return null; if (data === null) return null;
if (!Utils._.isArray(data) || data.length !== 2) return ''; if (!Utils._.isArray(data) || data.length !== 2) return '';
...@@ -26,8 +25,9 @@ module.exports = { ...@@ -26,8 +25,9 @@ module.exports = {
}); });
return (data.inclusive[0] ? '[' : '(') + JSON.stringify(data[0]) + ',' + JSON.stringify(data[1]) + (data.inclusive[1] ? ']' : ')'); return (data.inclusive[0] ? '[' : '(') + JSON.stringify(data[0]) + ',' + JSON.stringify(data[1]) + (data.inclusive[1] ? ']' : ')');
}, }
parse: function (value, AttributeType) {
function parse (value, AttributeType) {
if (value === null) return null; if (value === null) return null;
if(typeof AttributeType === 'function') AttributeType = new AttributeType(); if(typeof AttributeType === 'function') AttributeType = new AttributeType();
...@@ -58,5 +58,9 @@ module.exports = { ...@@ -58,5 +58,9 @@ module.exports = {
result.inclusive = [(value[0] === '['), (value[value.length - 1] === ']')]; result.inclusive = [(value[0] === '['), (value[value.length - 1] === ']')];
return result; return result;
} }
module.exports = {
stringify: stringify,
parse: parse
}; };
...@@ -4,6 +4,8 @@ var BaseTypes = require('../../data-types') ...@@ -4,6 +4,8 @@ var BaseTypes = require('../../data-types')
, util = require('util') , util = require('util')
, _ = require('lodash'); , _ = require('lodash');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://www.sqlite.org/datatype3.html';
var STRING = function() { var STRING = function() {
if (!(this instanceof STRING)) return new STRING(); if (!(this instanceof STRING)) return new STRING();
BaseTypes.STRING.apply(this, arguments); BaseTypes.STRING.apply(this, arguments);
...@@ -18,6 +20,14 @@ STRING.prototype.toSql = function() { ...@@ -18,6 +20,14 @@ STRING.prototype.toSql = function() {
} }
}; };
BaseTypes.TEXT.prototype.toSql = function() {
if (this._length) {
this.warn('SQLite does not support TEXT with options. Plain `TEXT` will be used instead.');
this._length = undefined;
}
return 'TEXT';
};
var CHAR = function() { var CHAR = function() {
if (!(this instanceof CHAR)) return new CHAR(); if (!(this instanceof CHAR)) return new CHAR();
BaseTypes.CHAR.apply(this, arguments); BaseTypes.CHAR.apply(this, arguments);
......
...@@ -11,8 +11,7 @@ var MySqlQueryGenerator = Utils._.extend( ...@@ -11,8 +11,7 @@ var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require('../mysql/query-generator')) Utils._.clone(require('../mysql/query-generator'))
); );
module.exports = (function() { var QueryGenerator = {
var QueryGenerator = {
options: {}, options: {},
dialect: 'sqlite', dialect: 'sqlite',
...@@ -251,11 +250,12 @@ module.exports = (function() { ...@@ -251,11 +250,12 @@ module.exports = (function() {
} }
if(dataType.references) { if(dataType.references) {
dataType = Utils.formatReferences(dataType);
template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)'; template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)';
replacements.referencesTable = this.quoteTable(dataType.references); replacements.referencesTable = this.quoteTable(dataType.references.model);
if(dataType.referencesKey) { if(dataType.references.key) {
replacements.referencesKey = this.quoteIdentifier(dataType.referencesKey); replacements.referencesKey = this.quoteIdentifier(dataType.references.key);
} else { } else {
replacements.referencesKey = this.quoteIdentifier('id'); replacements.referencesKey = this.quoteIdentifier('id');
} }
...@@ -436,7 +436,6 @@ module.exports = (function() { ...@@ -436,7 +436,6 @@ module.exports = (function() {
var sql = 'PRAGMA foreign_key_list(<%= tableName %>)'; var sql = 'PRAGMA foreign_key_list(<%= tableName %>)';
return Utils._.template(sql)({ tableName: tableName }); return Utils._.template(sql)({ tableName: tableName });
} }
}; };
return Utils._.extend({}, MySqlQueryGenerator, QueryGenerator); module.exports = Utils._.extend({}, MySqlQueryGenerator, QueryGenerator);
})();
...@@ -9,8 +9,8 @@ var Utils = require('../../utils') ...@@ -9,8 +9,8 @@ var Utils = require('../../utils')
@class QueryInterface @class QueryInterface
@static @static
*/ */
module.exports = {
/** /**
A wrapper that fixes SQLite's inability to remove columns from existing tables. A wrapper that fixes SQLite's inability to remove columns from existing tables.
It will create a backup of the table, drop the table afterwards and create a It will create a backup of the table, drop the table afterwards and create a
new table with the same name but without the obsolete column. new table with the same name but without the obsolete column.
...@@ -27,7 +27,7 @@ module.exports = { ...@@ -27,7 +27,7 @@ module.exports = {
@since 1.6.0 @since 1.6.0
*/ */
removeColumn: function(tableName, attributeName, options) { var removeColumn = function(tableName, attributeName, options) {
var self = this; var self = this;
options = options || {}; options = options || {};
...@@ -41,9 +41,9 @@ module.exports = { ...@@ -41,9 +41,9 @@ module.exports = {
return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging }); return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging });
}); });
}); });
}, };
/** /**
A wrapper that fixes SQLite's inability to change columns from existing tables. A wrapper that fixes SQLite's inability to change columns from existing tables.
It will create a backup of the table, drop the table afterwards and create a It will create a backup of the table, drop the table afterwards and create a
new table with the same name but with a modified version of the respective column. new table with the same name but with a modified version of the respective column.
...@@ -60,7 +60,7 @@ module.exports = { ...@@ -60,7 +60,7 @@ module.exports = {
@since 1.6.0 @since 1.6.0
*/ */
changeColumn: function(tableName, attributes, options) { var changeColumn = function(tableName, attributes, options) {
var attributeName = Utils._.keys(attributes)[0] var attributeName = Utils._.keys(attributes)[0]
, self = this; , self = this;
options = options || {}; options = options || {};
...@@ -75,9 +75,9 @@ module.exports = { ...@@ -75,9 +75,9 @@ module.exports = {
return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging }); return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging });
}); });
}); });
}, };
/** /**
A wrapper that fixes SQLite's inability to rename columns from existing tables. A wrapper that fixes SQLite's inability to rename columns from existing tables.
It will create a backup of the table, drop the table afterwards and create a It will create a backup of the table, drop the table afterwards and create a
new table with the same name but with a renamed version of the respective column. new table with the same name but with a renamed version of the respective column.
...@@ -95,7 +95,7 @@ module.exports = { ...@@ -95,7 +95,7 @@ module.exports = {
@since 1.6.0 @since 1.6.0
*/ */
renameColumn: function(tableName, attrNameBefore, attrNameAfter, options) { var renameColumn = function(tableName, attrNameBefore, attrNameAfter, options) {
var self = this; var self = this;
options = options || {}; options = options || {};
...@@ -110,5 +110,10 @@ module.exports = { ...@@ -110,5 +110,10 @@ module.exports = {
return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging }); return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging });
}); });
}); });
} };
module.exports = {
removeColumn: removeColumn,
changeColumn: changeColumn,
renameColumn: renameColumn
}; };
\ No newline at end of file
...@@ -5,11 +5,11 @@ var Utils = require('../../utils') ...@@ -5,11 +5,11 @@ var Utils = require('../../utils')
, QueryTypes = require('../../query-types') , QueryTypes = require('../../query-types')
, sequelizeErrors = require('../../errors.js'); , sequelizeErrors = require('../../errors.js');
module.exports = (function() { var Query = function(database, sequelize, options) {
var Query = function(database, sequelize, callee, options) {
this.database = database; this.database = database;
this.sequelize = sequelize; this.sequelize = sequelize;
this.callee = callee; this.instance = options.instance;
this.model = options.model;
this.options = Utils._.extend({ this.options = Utils._.extend({
logging: console.log, logging: console.log,
plain: false, plain: false,
...@@ -17,14 +17,14 @@ module.exports = (function() { ...@@ -17,14 +17,14 @@ module.exports = (function() {
}, options || {}); }, options || {});
this.checkLoggingOption(); this.checkLoggingOption();
}; };
Utils.inherit(Query, AbstractQuery); Utils.inherit(Query, AbstractQuery);
Query.prototype.getInsertIdField = function() { Query.prototype.getInsertIdField = function() {
return 'lastID'; return 'lastID';
}; };
Query.prototype.run = function(sql) { Query.prototype.run = function(sql) {
var self = this var self = this
, promise; , promise;
...@@ -49,13 +49,13 @@ module.exports = (function() { ...@@ -49,13 +49,13 @@ module.exports = (function() {
var metaData = this; var metaData = this;
metaData.columnTypes = columnTypes; metaData.columnTypes = columnTypes;
var result = self.callee; var result = self.instance;
// add the inserted row id to the instance // add the inserted row id to the instance
if (self.isInsertQuery(results, metaData)) { if (self.isInsertQuery(results, metaData)) {
self.handleInsertQuery(results, metaData); self.handleInsertQuery(results, metaData);
if (!self.callee) { if (!self.instance) {
result = metaData[self.getInsertIdField()]; result = metaData[self.getInsertIdField()];
} }
} }
...@@ -178,9 +178,9 @@ module.exports = (function() { ...@@ -178,9 +178,9 @@ module.exports = (function() {
}); });
return promise; return promise;
}; };
Query.prototype.formatError = function (err) { Query.prototype.formatError = function (err) {
var match; var match;
switch (err.code) { switch (err.code) {
...@@ -215,15 +215,17 @@ module.exports = (function() { ...@@ -215,15 +215,17 @@ module.exports = (function() {
fields.forEach(function(field) { fields.forEach(function(field) {
errors.push(new sequelizeErrors.ValidationErrorItem( errors.push(new sequelizeErrors.ValidationErrorItem(
field + ' must be unique', 'unique violation', field, self.callee[field])); field + ' must be unique', 'unique violation', field, self.instance && self.instance[field]));
}); });
Utils._.forOwn(this.callee.__options.uniqueKeys, function(constraint) { if (this.model) {
Utils._.forOwn(this.model.uniqueKeys, function(constraint) {
if (Utils._.isEqual(constraint.fields, fields) && !!constraint.msg) { if (Utils._.isEqual(constraint.fields, fields) && !!constraint.msg) {
message = constraint.msg; message = constraint.msg;
return false; return false;
} }
}); });
}
return new sequelizeErrors.UniqueConstraintError({ return new sequelizeErrors.UniqueConstraintError({
message: message, message: message,
...@@ -238,9 +240,9 @@ module.exports = (function() { ...@@ -238,9 +240,9 @@ module.exports = (function() {
default: default:
return new sequelizeErrors.DatabaseError(err); return new sequelizeErrors.DatabaseError(err);
} }
}; };
Query.prototype.handleShowIndexesQuery = function (data) { Query.prototype.handleShowIndexesQuery = function (data) {
var self = this; var self = this;
// Sqlite returns indexes so the one that was defined last is returned first. Lets reverse that! // Sqlite returns indexes so the one that was defined last is returned first. Lets reverse that!
...@@ -261,17 +263,16 @@ module.exports = (function() { ...@@ -261,17 +263,16 @@ module.exports = (function() {
return item; return item;
}); });
}); });
}; };
Query.prototype.getDatabaseMethod = function() { Query.prototype.getDatabaseMethod = function() {
if (this.isUpsertQuery()) { if (this.isUpsertQuery()) {
return 'exec'; // Needed to run multiple queries in one return 'exec'; // Needed to run multiple queries in one
} else if (this.isInsertQuery() || this.isUpdateQuery() || (this.sql.toLowerCase().indexOf('CREATE TEMPORARY TABLE'.toLowerCase()) !== -1) || this.options.type === QueryTypes.BULKDELETE) { } else if (this.isInsertQuery() || this.isUpdateQuery() || this.isBulkUpdateQuery() || (this.sql.toLowerCase().indexOf('CREATE TEMPORARY TABLE'.toLowerCase()) !== -1) || this.options.type === QueryTypes.BULKDELETE) {
return 'run'; return 'run';
} else { } else {
return 'all'; return 'all';
} }
}; };
return Query; module.exports = Query;
})();
'use strict';
var util = require('util')
, EventEmitter = require('events').EventEmitter
, Promise = require('../promise')
, proxyEventKeys = ['success', 'error', 'sql']
, Utils = require('../utils');
var bindToProcess = function(fct) {
if (fct && process.domain) {
return process.domain.bind(fct);
}
return fct;
};
/**
* The EventEmitter is returned from all asynchronous Sequelize calls - So almost all of them.
* The emitter provides a lovely mix of native node.js `EventEmitter` and promise methods.
*
* There are several different syntaxes for attaching listeners to the emitter:
*
* ```js
* Model.find(...).on('success', function (dao) {
* // Using it as a regular node emitter
* })
*
* Model.find(...).success(function (dao) {
* // Using the shortcut methods
* })
*
* Model.find(...).done(function (err, dao) {
* // Using the done method, which is called both if the operation succeeds,
* // and if it fails. On success, the err argument will be null
* })
*
* Model.find(...).then(function (dao) {
* // Using the emitter as a promise. The first function is the success handler,
* // and the second is the error handler.
* }, function (err) {
*
* })
* ```
*
* @deprecated The event emitter is deprecated, and due for removal soon. Please use promises instead!
* @mixes http://nodejs.org/api/events.html#events_class_events_eventemitter
* @class EventEmitter
*/
module.exports = (function() {
/**
* Create a new emitter instance.
*
* @constructor CustomEventEmitter
* @param {function} fct A function that this emitter should run. The function is called with the emitter as first argument and as context
*/
var CustomEventEmitter = function(fct) {
this.fct = bindToProcess(fct);
};
util.inherits(CustomEventEmitter, EventEmitter);
/**
* Run the function that was passed when the emitter was instantiated.
* @return this
*/
CustomEventEmitter.prototype.run = function() {
Utils.tick(function() {
if (this.fct) {
this.fct.call(this, this);
}
}.bind(this));
return this;
};
/**
* Emit an event from the emitter
* @param {string} type The type of event
* @param {any} value(s)* All other arguments will be passed to the event listeners
*/
CustomEventEmitter.prototype.emit = function(type) {
this._events = this._events || {};
// Override default 'error' event logic
if (type === 'error' && !this._events.error) {
// No error listener
var er = arguments[1];
// If error argument is an object but no error,
// boil it down to the value of the first key
// (probably an Array in most cases)
if (Utils._.isObject(er) && !(er instanceof Error)) {
er = er[Object.keys(er)[0]];
}
// If error argument is an array, make sure we
// pass only the first error to the original
// .emit() function of EventEmitter
if (er instanceof Array) {
er = Utils._.flatten(er)[0];
}
// We don't want to throw strings. Make them Errors!
if (typeof er === 'string') {
er = new Error(er);
}
arguments[1] = er;
}
EventEmitter.prototype.emit.apply(this, arguments);
};
/**
* Listen for success events.
*
* ```js
* emitter.success(function (result) {
* //...
* });
* ```
*
* @param {function} onSuccess
* @method success
* @alias ok
* @return this
*/
CustomEventEmitter.prototype.success =
CustomEventEmitter.prototype.ok =
function(fct) {
this.on('success', bindToProcess(fct));
return this;
};
/**
* Listen for error events
*
* ```js
* emitter.error(function (err) {
* //...
* });
* ```
*
* @param {function} onError
* @method error
* @alias fail
* @alias failure
* @return this
*/
CustomEventEmitter.prototype.failure =
CustomEventEmitter.prototype.fail =
CustomEventEmitter.prototype.error =
function(fct) {
this.on('error', bindToProcess(fct));
return this;
};
/**
* Listen for both success and error events.
*
* ```js
* emitter.done(function (err, result) {
* //...
* });
* ```
*
* @param {function} onDone
* @method done
* @alias complete
* @return this
*/
CustomEventEmitter.prototype.done =
CustomEventEmitter.prototype.complete =
function(fct) {
fct = bindToProcess(fct);
this.on('error', function(err) { fct(err, null); })
.on('success', function() {
var args = Array.prototype.slice.call(arguments);
args.unshift(null);
fct.apply(fct, args);
});
return this;
};
/*
* Attach a function that is called every time the function that created this emitter executes a query.
* @param {function} onSQL
* @return this
*/
CustomEventEmitter.prototype.sql = function(fct) {
this.on('sql', bindToProcess(fct));
return this;
};
/**
* Proxy every event of this event emitter to another one.
*
* @param {EventEmitter} emitter The event emitter that should receive the events.
* @param {Object} [options]
* @param {Array} [options.events] An array of the events to proxy. Defaults to sql, error and success
* @return this
*/
CustomEventEmitter.prototype.proxy = function(emitter, options) {
options = Utils._.extend({
events: proxyEventKeys,
skipEvents: []
}, options || {});
options.events = Utils._.difference(options.events, options.skipEvents);
options.events.forEach(function(eventKey) {
this.on(eventKey, function() {
var args = [eventKey].concat([].slice.apply(arguments));
emitter.emit.apply(emitter, args);
});
}.bind(this));
return this;
};
CustomEventEmitter.prototype.proxySql = function(promise) {
return this.proxy(promise, {
events: ['sql']
});
};
/**
* Attach listeners to the emitter, promise style.
*
* @param {Function} onFulfilled The function to call if the promise is fulfilled (if the emitter emits success). Note that this function will always only be called with one argument, as per the promises/A spec. For functions that emit multiple arguments (e.g. findOrCreate) see `spread`
* @param {Function} onRejected
* @return {Bluebird.Promise}
*/
CustomEventEmitter.prototype.then = function(onFulfilled, onRejected) {
var self = this;
onFulfilled = bindToProcess(onFulfilled);
onRejected = bindToProcess(onRejected);
var promise = (new Promise(function(resolve, reject) {
self.on('error', reject)
.on('success', resolve);
})).then(onFulfilled, onRejected);
this.proxySql(promise);
return promise;
};
/**
* Attach listeners to the emitter, promise style. This listener will recieve all arguments emitted by the emitter, as opposed to `then` which will only recieve the first argument.
*
* @param {Function} onFulfilled The function to call if the promise is fulfilled (if the emitter emits success).
* @param {Function} onRejected
* @return {Bluebird.Promise}
*/
CustomEventEmitter.prototype.spread = function(onFulfilled, onRejected) {
var self = this;
onFulfilled = bindToProcess(onFulfilled);
onRejected = bindToProcess(onRejected);
var promise = (new Promise(function(resolve, reject) {
self.on('error', reject)
.on('success', function() {
resolve(Array.prototype.slice.apply(arguments)); // Transform args to an array
});
})).spread(onFulfilled, onRejected);
this.proxySql(promise);
return promise;
};
/**
* Shorthand for `then(null, onRejected)`
*
* @param {Function} onRejected
* @return {Bluebird.Promise}
*/
CustomEventEmitter.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
};
return CustomEventEmitter;
})();
This diff could not be displayed because it is too large.
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!