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

You need to sign in or sign up before continuing.
Commit 049c58f4 by Manuel Darveau

Merge remote-tracking branch 'upstream/master'

2 parents f5218ad6 c7d96ad1
Showing with 1410 additions and 2063 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
...@@ -184,7 +247,7 @@ ...@@ -184,7 +247,7 @@
- [FEATURE] Added `find()` hooks - [FEATURE] Added `find()` hooks
#### Backwards compatibility changes #### Backwards compatibility changes
- The `fieldName` property, used in associations with a foreign key object `(A.hasMany(B, { foreignKey: { ... }})`, has been renamed to `name` to avoid confusion with `field`. - The `fieldName` property, used in associations with a foreign key object `(A.hasMany(B, { foreignKey: { ... }})`, has been renamed to `name` to avoid confusion with `field`.
- The naming of the join table entry for N:M association getters is now singular (like includes) - The naming of the join table entry for N:M association getters is now singular (like includes)
- Signature of hooks has changed to pass options to all hooks. Any hooks previously defined like `Model.beforeCreate(values)` now need to be `Model.beforeCreate(values, options)` etc. - Signature of hooks has changed to pass options to all hooks. Any hooks previously defined like `Model.beforeCreate(values)` now need to be `Model.beforeCreate(values, options)` etc.
- Results returned by hooks are ignored - changes to results by hooks should be made by reference - Results returned by hooks are ignored - changes to results by hooks should be made by reference
...@@ -207,7 +270,7 @@ We are working our way to the first 2.0.0 release candidate. ...@@ -207,7 +270,7 @@ We are working our way to the first 2.0.0 release candidate.
- [BUG] Fix default scope being overwritten [#2087](https://github.com/sequelize/sequelize/issues/2087) - [BUG] Fix default scope being overwritten [#2087](https://github.com/sequelize/sequelize/issues/2087)
- [BUG] Fixed updatedAt timestamp not being set in bulk create when validate = true. [#1962](https://github.com/sequelize/sequelize/issues/1962) - [BUG] Fixed updatedAt timestamp not being set in bulk create when validate = true. [#1962](https://github.com/sequelize/sequelize/issues/1962)
- [INTERNALS] Replaced lingo with inflection - [INTERNALS] Replaced lingo with inflection
- [INTERNALS] Removed underscore.string dependency and moved a couple of helper functions from `Utils._` to `Utils` - [INTERNALS] Removed underscore.string dependency and moved a couple of helper functions from `Utils._` to `Utils`
- [INTERNALS] Update dependencies - [INTERNALS] Update dependencies
+ validator 3.2.0 -> 3.16.1 + validator 3.2.0 -> 3.16.1
+ moment 2.5.0 -> 2.7.0 + moment 2.5.0 -> 2.7.0
......
<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
...@@ -313,4 +313,4 @@ user.getProjects().then(function (projects) { ...@@ -313,4 +313,4 @@ user.getProjects().then(function (projects) {
*** ***
_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)_ _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="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="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:**
...@@ -404,4 +404,4 @@ A hook that is run after Sequelize() call ...@@ -404,4 +404,4 @@ A hook that is run after Sequelize() call
*** ***
_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)_ _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="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,10 +87,10 @@ Commit the transaction ...@@ -70,10 +87,10 @@ 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
*** ***
_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)_ _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
...@@ -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);
......
...@@ -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
``` ```
...@@ -80,5 +79,5 @@ var sequelize = new Sequelize('connectionUri', { ...@@ -80,5 +79,5 @@ var sequelize = new Sequelize('connectionUri', {
var User = sequelize.define('user', {}); // timestamps is false by default var User = sequelize.define('user', {}); // timestamps is false by default
var Post = sequelize.define('user', {}, { var Post = sequelize.define('user', {}, {
timestamps: true // timestamps will now be true timestamps: true // timestamps will now be true
}); });
``` ```
...@@ -2,32 +2,32 @@ Hooks (also known as callbacks or lifecycle events), are functions which are cal ...@@ -2,32 +2,32 @@ Hooks (also known as callbacks or lifecycle events), are functions which are cal
## Order of Operations ## Order of Operations
``` ```
(1) (1)
beforeBulkCreate(instances, options, fn) beforeBulkCreate(instances, options, fn)
beforeBulkDestroy(instances, options, fn) beforeBulkDestroy(instances, options, fn)
beforeBulkUpdate(instances, options, fn) beforeBulkUpdate(instances, options, fn)
(2) (2)
beforeValidate(instance, options, fn) beforeValidate(instance, options, fn)
(-) (-)
validate validate
(3) (3)
afterValidate(instance, options, fn) afterValidate(instance, options, fn)
(4) (4)
beforeCreate(instance, options, fn) beforeCreate(instance, options, fn)
beforeDestroy(instance, options, fn) beforeDestroy(instance, options, fn)
beforeUpdate(instance, options, fn) beforeUpdate(instance, options, fn)
(-) (-)
create create
destroy destroy
update update
(5) (5)
afterCreate(instance, options, fn) afterCreate(instance, options, fn)
afterDestroy(instance, options, fn) afterDestroy(instance, options, fn)
afterUpdate(instance, options, fn) afterUpdate(instance, options, fn)
(6) (6)
afterBulkCreate(instances, options, fn) afterBulkCreate(instances, options, fn)
afterBulkDestory(instances, options, fn) afterBulkDestory(instances, options, fn)
afterBulkUpdate(instances, options, fn) afterBulkUpdate(instances, options, fn)
``` ```
...@@ -35,7 +35,7 @@ Hooks (also known as callbacks or lifecycle events), are functions which are cal ...@@ -35,7 +35,7 @@ Hooks (also known as callbacks or lifecycle events), are functions which are cal
There are currently three ways to programmatically add hooks. A hook function always runs asynchronousĺy, and can be resolved either by calling a callback (passed as the last argument), There are currently three ways to programmatically add hooks. A hook function always runs asynchronousĺy, and can be resolved either by calling a callback (passed as the last argument),
or by returning a promise. or by returning a promise.
```js ```js
// Method 1 via the .define() method // Method 1 via the .define() method
var User = sequelize.define('User', { var User = sequelize.define('User', {
...@@ -56,7 +56,7 @@ var User = sequelize.define('User', { ...@@ -56,7 +56,7 @@ var User = sequelize.define('User', {
} }
} }
}) })
// Method 2 via the .hook() method // Method 2 via the .hook() method
var User = sequelize.define('User', { var User = sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
...@@ -65,16 +65,16 @@ var User = sequelize.define('User', { ...@@ -65,16 +65,16 @@ var User = sequelize.define('User', {
values: ['happy', 'sad', 'neutral'] values: ['happy', 'sad', 'neutral']
} }
}) })
User.hook('beforeValidate', function(user, options, fn) { User.hook('beforeValidate', function(user, options, fn) {
user.mood = 'happy' user.mood = 'happy'
fn(null, user) fn(null, user)
}) })
User.hook('afterValidate', function(user, options) { User.hook('afterValidate', function(user, options) {
return sequelize.Promise.reject("I'm afraid I can't let you do that!") return sequelize.Promise.reject("I'm afraid I can't let you do that!")
}) })
// Method 3 via the direct method // Method 3 via the direct method
var User = sequelize.define('User', { var User = sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
...@@ -83,12 +83,12 @@ var User = sequelize.define('User', { ...@@ -83,12 +83,12 @@ var User = sequelize.define('User', {
values: ['happy', 'sad', 'neutral'] values: ['happy', 'sad', 'neutral']
} }
}) })
User.beforeValidate(function(user, options) { User.beforeValidate(function(user, options) {
user.mood = 'happy' user.mood = 'happy'
return sequelize.Promise.resolve(user) return sequelize.Promise.resolve(user)
}) })
User.afterValidate(function(user, options, fn) { User.afterValidate(function(user, options, fn) {
user.username = 'Toni' user.username = 'Toni'
fn(null, user) fn(null, user)
...@@ -115,15 +115,15 @@ User.beforeCreate(function(user) { ...@@ -115,15 +115,15 @@ 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) {
console.log(err) // You can't grant this user an access level above 10! console.log(err) // You can't grant this user an access level above 10!
}) })
``` ```
The following example would emit a success event: The following example would return successful:
```js ```js
User.create({username: 'Boss', accessLevel: 20}).then(function(user) { User.create({username: 'Boss', accessLevel: 20}).then(function(user) {
...@@ -134,18 +134,18 @@ User.create({username: 'Boss', accessLevel: 20}).then(function(user) { ...@@ -134,18 +134,18 @@ User.create({username: 'Boss', accessLevel: 20}).then(function(user) {
### Model hooks ### Model hooks
Sometimes you'll be editing more than one record at a time by utilizing the `bulkCreate, update, destroy` methods on the model. The following will emit whenever you're using one of those methods. Sometimes you'll be editing more than one record at a time by utilizing the `bulkCreate, update, destroy` methods on the model. The following will emit whenever you're using one of those methods.
``` ```
beforeBulkCreate / beforeBulkUpdate / beforeBulkDestroy beforeBulkCreate / beforeBulkUpdate / beforeBulkDestroy
afterBulkCreate / afterBulkUpdate / afterBulkDestroy afterBulkCreate / afterBulkUpdate / afterBulkDestroy
``` ```
If you want to emit hooks for each individual record, along with the bulk hooks you can pass `individualHooks: true` to the call. If you want to emit hooks for each individual record, along with the bulk hooks you can pass `individualHooks: true` to the call.
```js ```js
Model.destroy({ where: {accessLevel: 0}, individualHooks: true}) Model.destroy({ where: {accessLevel: 0}, individualHooks: true})
// Will select all records that are about to be deleted and emit before- + after- Destroy on each instance // Will select all records that are about to be deleted and emit before- + after- Destroy on each instance
Model.update({username: 'Toni'}, { where: {accessLevel: 0}, individualHooks: true}) Model.update({username: 'Toni'}, { where: {accessLevel: 0}, individualHooks: true})
// Will select all records that are about to be updated and emit before- + after- Update on each instance // Will select all records that are about to be updated and emit before- + after- Update on each instance
``` ```
...@@ -157,23 +157,23 @@ Model.beforeBulkCreate(function(records, fields, fn) { ...@@ -157,23 +157,23 @@ Model.beforeBulkCreate(function(records, fields, fn) {
// records = the first argument sent to .bulkCreate // records = the first argument sent to .bulkCreate
// fields = the second argument sent to .bulkCreate // fields = the second argument sent to .bulkCreate
}) })
Model.bulkCreate([ Model.bulkCreate([
{username: 'Toni'}, // part of records argument {username: 'Toni'}, // part of records argument
{username: 'Tobi'} // part of records argument {username: 'Tobi'} // part of records argument
], ['username'] /* part of fields argument */) ], ['username'] /* part of fields argument */)
Model.beforeBulkUpdate(function(attributes, where, fn) { Model.beforeBulkUpdate(function(attributes, where, fn) {
// attributes = first argument sent to Model.update // attributes = first argument sent to Model.update
// where = second argument sent to Model.update // where = second argument sent to Model.update
}) })
Model.update({gender: 'Male'} /*attributes argument*/, { where: {username: 'Tom'}} /*where argument*/) Model.update({gender: 'Male'} /*attributes argument*/, { where: {username: 'Tom'}} /*where argument*/)
Model.beforeBulkDestroy(function(whereClause, fn) { Model.beforeBulkDestroy(function(whereClause, fn) {
// whereClause = first argument sent to Model.destroy // whereClause = first argument sent to Model.destroy
}) })
Model.destroy({ where: {username: 'Tom'}} /*whereClause argument*/) Model.destroy({ where: {username: 'Tom'}} /*whereClause argument*/)
``` ```
...@@ -183,22 +183,22 @@ For the most part hooks will work the same for instances when being associated e ...@@ -183,22 +183,22 @@ For the most part hooks will work the same for instances when being associated e
1. When using add/set\[s\] functions the beforeUpdate/afterUpdate hooks will run. 1. When using add/set\[s\] functions the beforeUpdate/afterUpdate hooks will run.
2. The only way to call beforeDestroy/afterDestroy hooks are on associations with `onDelete: 'cascade'` and the option `hooks: true`. For instance: 2. The only way to call beforeDestroy/afterDestroy hooks are on associations with `onDelete: 'cascade'` and the option `hooks: true`. For instance:
```js ```js
var Projects = sequelize.define('Projects', { var Projects = sequelize.define('Projects', {
title: DataTypes.STRING title: DataTypes.STRING
}) })
var Tasks = sequelize.define('Tasks', { var Tasks = sequelize.define('Tasks', {
title: DataTypes.STRING title: DataTypes.STRING
}) })
Projects.hasMany(Tasks, {onDelete: 'cascade', hooks: true}) Projects.hasMany(Tasks, {onDelete: 'cascade', hooks: true})
Tasks.belongsTo(Projects) Tasks.belongsTo(Projects)
``` ```
This code will run beforeDestroy/afterDestroy on the Tasks table. Sequelize, by default, will try to optimize your queries as much as possible. This code will run beforeDestroy/afterDestroy on the Tasks table. Sequelize, by default, will try to optimize your queries as much as possible.
When calling cascade on delete, Sequelize will simply execute a When calling cascade on delete, Sequelize will simply execute a
```sql ```sql
DELETE FROM `table` WHERE associatedIdentifiier = associatedIdentifier.primaryKey DELETE FROM `table` WHERE associatedIdentifiier = associatedIdentifier.primaryKey
``` ```
...@@ -231,14 +231,14 @@ User.beforeCreate(function(user, options) { ...@@ -231,14 +231,14 @@ User.beforeCreate(function(user, options) {
## A Note About Transactions ## A Note About Transactions
Note that many model operations in Sequelize allow you to specify a transaction in the options parameter of the method. If a transaction _is_ specified in the original call, it will be present in the options parameter passed to the hook function. For example, consider the following snippet: Note that many model operations in Sequelize allow you to specify a transaction in the options parameter of the method. If a transaction _is_ specified in the original call, it will be present in the options parameter passed to the hook function. For example, consider the following snippet:
```js ```js
// Here we use the promise-style of async hooks rather than // Here we use the promise-style of async hooks rather than
// the callback. // the callback.
User.hook('afterCreate', function(user, options) { User.hook('afterCreate', function(user, options) {
// 'transaction' will be available in options.transaction // 'transaction' will be available in options.transaction
// This operation will be part of the same transaction as the // This operation will be part of the same transaction as the
// original User.create call. // original User.create call.
return User.update({ return User.update({
mood: 'sad' mood: 'sad'
...@@ -249,19 +249,18 @@ User.hook('afterCreate', function(user, options) { ...@@ -249,19 +249,18 @@ User.hook('afterCreate', function(user, options) {
transaction: options.transaction transaction: options.transaction
}); });
}); });
sequelize.transaction(function(t) { sequelize.transaction(function(t) {
User.create({ User.create({
username: 'someguy', username: 'someguy',
mood: 'happy' mood: 'happy',
}, {
transaction: t transaction: t
}); });
}); });
``` ```
If we had not included the transaction option in our call to `User.update` in the preceding code, no change would have occurred, since our newly created user does not exist in the database until the pending transaction has been committed. If we had not included the transaction option in our call to `User.update` in the preceding code, no change would have occurred, since our newly created user does not exist in the database until the pending transaction has been committed.
### Internal Transactions ### Internal Transactions
......
## Building a non-persistent instance ## Building a non-persistent instance
In order to create instances of defined classes just do as follows&period; You might recognize the syntax if you coded Ruby in the past&period; Using the `build`-method will return an unsaved object&comma; which you explicitly have to save&period; In order to create instances of defined classes just do as follows&period; You might recognize the syntax if you coded Ruby in the past&period; Using the `build`-method will return an unsaved object&comma; which you explicitly have to save&period;
```js ```js
var project = Project.build({ var project = Project.build({
title: 'my awesome project', title: 'my awesome project',
...@@ -56,7 +56,7 @@ Task ...@@ -56,7 +56,7 @@ Task
## Creating persistent instances ## Creating persistent instances
Besides constructing objects&comma; that needs an explicit save call to get stored in the database&comma; there is also the possibility to do all those steps with one single command&period; It's called `create`. Besides constructing objects&comma; that needs an explicit save call to get stored in the database&comma; there is also the possibility to do all those steps with one single command&period; It's called `create`.
```js ```js
Task.create({ title: 'foo', description: 'bar', deadline: new Date() }).then(function(task) { Task.create({ title: 'foo', description: 'bar', deadline: new Date() }).then(function(task) {
// you can now access the newly created task via the variable task // you can now access the newly created task via the variable task
...@@ -64,7 +64,7 @@ Task.create({ title: 'foo', description: 'bar', deadline: new Date() }).then(fun ...@@ -64,7 +64,7 @@ Task.create({ title: 'foo', description: 'bar', deadline: new Date() }).then(fun
``` ```
It is also possible to define which attributes can be set via the create method&period; This can be especially very handy if you create database entries based on a form which can be filled by a user&period; Using that would for example allow you to restrict the `User` model to set only a username and an address but not an admin flag&colon; It is also possible to define which attributes can be set via the create method&period; This can be especially very handy if you create database entries based on a form which can be filled by a user&period; Using that would for example allow you to restrict the `User` model to set only a username and an address but not an admin flag&colon;
```js ```js
User.create({ username: 'barfooz', isAdmin: true }, [ 'username' ]).then(function(user) { User.create({ username: 'barfooz', isAdmin: true }, [ 'username' ]).then(function(user) {
// let's assume the default of isAdmin is false: // let's assume the default of isAdmin is false:
...@@ -77,7 +77,7 @@ User.create({ username: 'barfooz', isAdmin: true }, [ 'username' ]).then(functio ...@@ -77,7 +77,7 @@ User.create({ username: 'barfooz', isAdmin: true }, [ 'username' ]).then(functio
## Updating / Saving / Persisting an instance ## Updating / Saving / Persisting an instance
Now lets change some values and save changes to the database&period;&period;&period; There are two ways to do that&colon; Now lets change some values and save changes to the database&period;&period;&period; There are two ways to do that&colon;
```js ```js
// way 1 // way 1
task.title = 'a very different title now' task.title = 'a very different title now'
...@@ -90,7 +90,7 @@ task.updateAttributes({ ...@@ -90,7 +90,7 @@ task.updateAttributes({
``` ```
It's also possible to define which attributes should be saved when calling `save`&comma; by passing an array of column names&period; This is useful when you set attributes based on a previously defined object&period; E&period;g&period; if you get the values of an object via a form of a web app&period; Furthermore this is used internally for `updateAttributes`&period; This is how it looks like&colon; It's also possible to define which attributes should be saved when calling `save`&comma; by passing an array of column names&period; This is useful when you set attributes based on a previously defined object&period; E&period;g&period; if you get the values of an object via a form of a web app&period; Furthermore this is used internally for `updateAttributes`&period; This is how it looks like&colon;
```js ```js
task.title = 'foooo' task.title = 'foooo'
task.description = 'baaaaaar' task.description = 'baaaaaar'
...@@ -119,7 +119,7 @@ Task.create({ title: 'a task' }).then(function(task) { ...@@ -119,7 +119,7 @@ Task.create({ title: 'a task' }).then(function(task) {
``` ```
If the `paranoid` options is true, the object will not be deleted, instead the `deletedAt` column will be set to the current timestamp. To force the deletion, you can pass `force: true` to the destroy call: If the `paranoid` options is true, the object will not be deleted, instead the `deletedAt` column will be set to the current timestamp. To force the deletion, you can pass `force: true` to the destroy call:
```js ```js
task.destroy({ force: true }) task.destroy({ force: true })
``` ```
...@@ -135,7 +135,7 @@ In addition to updating a single instance, you can also create, update, and dele ...@@ -135,7 +135,7 @@ In addition to updating a single instance, you can also create, update, and dele
Since you are working with multiple models, the callbacks will not return DAO instances. BulkCreate will return an array of model instances/DAOs, they will however, unlike `create`, not have the resulting values of autoIncrement attributes.`update` and `destroy` will return the number of affected rows. Since you are working with multiple models, the callbacks will not return DAO instances. BulkCreate will return an array of model instances/DAOs, they will however, unlike `create`, not have the resulting values of autoIncrement attributes.`update` and `destroy` will return the number of affected rows.
First lets look at bulkCreate First lets look at bulkCreate
```js ```js
User.bulkCreate([ User.bulkCreate([
{ username: 'barfooz', isAdmin: true }, { username: 'barfooz', isAdmin: true },
...@@ -149,7 +149,7 @@ User.bulkCreate([ ...@@ -149,7 +149,7 @@ User.bulkCreate([
``` ```
To update several rows at once: To update several rows at once:
```js ```js
Task.bulkCreate([ Task.bulkCreate([
{subject: 'programming', status: 'executing'}, {subject: 'programming', status: 'executing'},
...@@ -157,7 +157,7 @@ Task.bulkCreate([ ...@@ -157,7 +157,7 @@ Task.bulkCreate([
{subject: 'programming', status: 'finished'} {subject: 'programming', status: 'finished'}
]).then(function() { ]).then(function() {
Task.update( Task.update(
{ status: 'inactive' } /* set attributes' value */, { status: 'inactive' } /* set attributes' value */,
{ where: { subject: 'programming' }} /* where criteria */ { where: { subject: 'programming' }} /* where criteria */
).then(function(affectedRows) { ).then(function(affectedRows) {
// affectedRows will be 2 // affectedRows will be 2
...@@ -169,14 +169,14 @@ Task.bulkCreate([ ...@@ -169,14 +169,14 @@ Task.bulkCreate([
``` ```
And delete them: And delete them:
```js ```js
Task.bulkCreate([ Task.bulkCreate([
{subject: 'programming', status: 'executing'}, {subject: 'programming', status: 'executing'},
{subject: 'reading', status: 'executing'}, {subject: 'reading', status: 'executing'},
{subject: 'programming', status: 'finished'} {subject: 'programming', status: 'finished'}
]).then(function() { ]).then(function() {
Task.destroy({ Task.destroy({
where: { where: {
subject: 'programming' subject: 'programming'
}, },
...@@ -191,18 +191,18 @@ Task.bulkCreate([ ...@@ -191,18 +191,18 @@ Task.bulkCreate([
``` ```
If you are accepting values directly from the user, it might be beneficial to limit the columns that you want to actually insert.`bulkCreate()`accepts an options object as the second parameter. The object can have a `fields` parameter, &lpar;an array&rpar; to let it know which fields you want to build explicitly If you are accepting values directly from the user, it might be beneficial to limit the columns that you want to actually insert.`bulkCreate()`accepts an options object as the second parameter. The object can have a `fields` parameter, &lpar;an array&rpar; to let it know which fields you want to build explicitly
```js ```js
User.bulkCreate([ User.bulkCreate([
{ username: 'foo' }, { username: 'foo' },
{ username: 'bar', admin: true} { username: 'bar', admin: true}
], { fields: ['username'] }).then(function() { ], { fields: ['username'] }).then(function() {
// nope bar, you can't be admin! // nope bar, you can't be admin!
}) })
``` ```
`bulkCreate` was originally made to be a mainstream&sol;fast way of inserting records&comma; however&comma; sometimes you want the luxury of being able to insert multiple rows at once without sacrificing model validations even when you explicitly tell Sequelize which columns to sift through&period; You can do by adding a `validate: true` property to the options object. `bulkCreate` was originally made to be a mainstream&sol;fast way of inserting records&comma; however&comma; sometimes you want the luxury of being able to insert multiple rows at once without sacrificing model validations even when you explicitly tell Sequelize which columns to sift through&period; You can do by adding a `validate: true` property to the options object.
```js ```js
var Tasks = sequelize.define('Task', { var Tasks = sequelize.define('Task', {
name: { name: {
...@@ -226,19 +226,19 @@ Tasks.bulkCreate([ ...@@ -226,19 +226,19 @@ Tasks.bulkCreate([
], { validate: true }).catch(function(errors) { ], { validate: true }).catch(function(errors) {
/* console.log(errors) would look like: /* console.log(errors) would look like:
[ [
{ record: { record:
... ...
errors: errors:
{ name: 'SequelizeValidationError', { name: 'SequelizeValidationError',
message: 'Validation error', message: 'Validation error',
errors: [Object] } }, errors: [Object] } },
{ record: { record:
... ...
errors: errors:
{ name: 'SequelizeValidationError', { name: 'SequelizeValidationError',
message: 'Validation error', message: 'Validation error',
errors: [Object] } } errors: [Object] } }
] ]
*/ */
}) })
``` ```
...@@ -246,7 +246,7 @@ Tasks.bulkCreate([ ...@@ -246,7 +246,7 @@ Tasks.bulkCreate([
## Values of an instance ## Values of an instance
If you log an instance you will notice&comma; that there is a lot of additional stuff&period; In order to hide such stuff and reduce it to the very interesting information&comma; you can use the`get`-attribute&period; Calling it with the option `plain` = true will only return the values of an instance&period; If you log an instance you will notice&comma; that there is a lot of additional stuff&period; In order to hide such stuff and reduce it to the very interesting information&comma; you can use the`get`-attribute&period; Calling it with the option `plain` = true will only return the values of an instance&period;
```js ```js
Person.create({ Person.create({
name: 'Rambow', name: 'Rambow',
...@@ -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'
   
...@@ -289,25 +289,25 @@ Person.find({ where: { name: 'john' } }).then(function(person) { ...@@ -289,25 +289,25 @@ Person.find({ where: { name: 'john' } }).then(function(person) {
In order to increment values of an instance without running into concurrency issues&comma; you may use `increment`&period; In order to increment values of an instance without running into concurrency issues&comma; you may use `increment`&period;
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
...@@ -320,25 +320,25 @@ User.find(1).then(function(user) { ...@@ -320,25 +320,25 @@ User.find(1).then(function(user) {
In order to decrement values of an instance without running into concurrency issues&comma; you may use `decrement`&period; In order to decrement values of an instance without running into concurrency issues&comma; you may use `decrement`&period;
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
......
...@@ -30,7 +30,7 @@ $ sequelize help:db:migrate ...@@ -30,7 +30,7 @@ $ sequelize help:db:migrate
$ sequelize help:db:migrate:undo $ sequelize help:db:migrate:undo
# etc # etc
``` ```
The latter one for example will print out the following output: The latter one for example will print out the following output:
```bash ```bash
...@@ -48,21 +48,19 @@ OPTIONS ...@@ -48,21 +48,19 @@ OPTIONS
--coffee Enables coffee script support. Default: false --coffee Enables coffee script support. Default: false
--config The path to the config file. Default: config/config.json --config The path to the config file. Default: config/config.json
``` ```
## Skeleton ## Skeleton
The following skeleton shows a typical migration file&period; All migrations are expected to be located in a folder called `migrations` at the very top of the project&period; The sequelize binary can generate a migration skeleton&period; See the aboves section for more details&period; The following skeleton shows a typical migration file&period; All migrations are expected to be located in a folder called `migrations` at the very top of the project&period; The sequelize binary can generate a migration skeleton&period; See the aboves section for more details&period;
```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,7 +583,29 @@ User.findAll({ ...@@ -535,7 +583,29 @@ User.findAll({
}) })
``` ```
Include all also supports nested loading: 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:
```js ```js
User.findAll({ include: [{ all: true, nested: true }]}); User.findAll({ include: [{ all: true, nested: true }]});
......
...@@ -57,12 +57,12 @@ $like: '%hat', // LIKE '%hat' ...@@ -57,12 +57,12 @@ $like: '%hat', // LIKE '%hat'
$notLike: '%hat' // NOT LIKE '%hat' $notLike: '%hat' // NOT LIKE '%hat'
$iLike: '%hat' // ILIKE '%hat' (case insensitive) $iLike: '%hat' // ILIKE '%hat' (case insensitive)
$notILike: '%hat' // NOT ILIKE '%hat' $notILike: '%hat' // NOT ILIKE '%hat'
$like: { $any: ['cat', 'hat']} $like: { $any: ['cat', 'hat']}
// LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike // LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike
$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'],
...@@ -169,7 +169,7 @@ something.find({ ...@@ -169,7 +169,7 @@ something.find({
// Will order by max(age) DESC // Will order by max(age) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'], [sequelize.fn('max', sequelize.col('age')), 'DESC'],
// Will order by otherfunction(`col1`, 12, 'lalala') DESC // Will order by otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'], [sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// Both the following statements will be treated literally so should be treated with care // Both the following statements will be treated literally so should be treated with care
......
...@@ -8,7 +8,7 @@ sequelize.query("UPDATE users SET y = 42 WHERE x = 12").spread(function(results, ...@@ -8,7 +8,7 @@ sequelize.query("UPDATE users SET y = 42 WHERE x = 12").spread(function(results,
}) })
``` ```
In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do: In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do:
```js ```js
sequelize.query("SELECT * FROM `users`", { type: sequelize.QueryTypes.SELECT}) sequelize.query("SELECT * FROM `users`", { type: sequelize.QueryTypes.SELECT})
...@@ -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
}) })
``` ```
...@@ -35,13 +35,13 @@ Replacements in a query can be done in two different ways, either using named pa ...@@ -35,13 +35,13 @@ Replacements in a query can be done in two different ways, either using named pa
* If an object is passed, `:key` will be replaced with the keys from that object. If the object contains keys not found in the query or vice verca, an exception will be thrown. * If an object is passed, `:key` will be replaced with the keys from that object. If the object contains keys not found in the query or vice verca, an exception will be thrown.
```js ```js
sequelize.query('SELECT * FROM projects WHERE status = ?', sequelize.query('SELECT * FROM projects WHERE status = ?',
{ replacements: ['active'], type: sequelize.QueryTypes.SELECT } { replacements: ['active'], type: sequelize.QueryTypes.SELECT }
).then(function(projects) { ).then(function(projects) {
console.log(projects) console.log(projects)
}) })
sequelize.query('SELECT * FROM projects WHERE status = :status ', sequelize.query('SELECT * FROM projects WHERE status = :status ',
{ replacements: { status: 'active' }, type: sequelize.QueryTypes.SELECT } { replacements: { status: 'active' }, type: sequelize.QueryTypes.SELECT }
).then(function(projects) { ).then(function(projects) {
console.log(projects) console.log(projects)
......
...@@ -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,15 +24,18 @@ return sequelize.transaction(function (t) { ...@@ -22,15 +24,18 @@ 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
}).catch(function (err) { }).catch(function (err) {
// Transaction has been rolled back // Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback // err is whatever rejected the promise chain returned to the transaction callback
}); });
``` ```
### 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,24 +91,51 @@ sequelize.transaction(function (t1) { ...@@ -84,24 +91,51 @@ 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.
```js ```js
return sequelize.transaction().then(function (t) { return sequelize.transaction().then(function (t) {
return User.create({ return User.create({
firstName: 'Homer', firstName: 'Homer',
...@@ -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.
......
## Basic usage ## Basic usage
To get the ball rollin' you first have to create an instance of Sequelize&period; Use it the following way&colon; To get the ball rollin' you first have to create an instance of Sequelize&period; Use it the following way&colon;
```js ```js
var sequelize = new Sequelize('database', 'username'[, 'password']) var sequelize = new Sequelize('database', 'username'[, 'password'])
``` ```
...@@ -15,7 +15,7 @@ var sequelize = new Sequelize('database', 'username', 'password', { ...@@ -15,7 +15,7 @@ var sequelize = new Sequelize('database', 'username', 'password', {
``` ```
If you just don't have a password&colon; If you just don't have a password&colon;
```js ```js
var sequelize = new Sequelize('database', 'username') var sequelize = new Sequelize('database', 'username')
// or // or
...@@ -23,7 +23,7 @@ var sequelize = new Sequelize('database', 'username', null) ...@@ -23,7 +23,7 @@ var sequelize = new Sequelize('database', 'username', null)
``` ```
You can also use a connection string&colon; You can also use a connection string&colon;
```js ```js
var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname', { var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname', {
// Look to the next section for possible options // Look to the next section for possible options
...@@ -33,7 +33,7 @@ var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname', { ...@@ -33,7 +33,7 @@ var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname', {
## Options ## Options
Besides the host and the port&comma; Sequelize comes with a whole bunch of options&period; Here they are&colon; Besides the host and the port&comma; Sequelize comes with a whole bunch of options&period; Here they are&colon;
```js ```js
var sequelize = new Sequelize('database', 'username', 'password', { var sequelize = new Sequelize('database', 'username', 'password', {
// custom host; default: localhost // custom host; default: localhost
...@@ -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
}) })
``` ```
...@@ -117,7 +125,7 @@ var sequelize = new Sequelize('database', 'username', 'password', { ...@@ -117,7 +125,7 @@ var sequelize = new Sequelize('database', 'username', 'password', {
## Read replication ## Read replication
Sequelize supports read replication&comma; i&period;e&period; having multiple servers that you can connect to when you want to do a SELECT query&period; When you do read replication&comma; you specify one or more servers to act as read replicas&comma; and one server to act as the write master&comma; which handles all writes and updates and propagates them to the replicas &lpar;note that the actual replication process is **not** handled by Sequelize&comma; but should be set up in MySql&rpar;&period; Sequelize supports read replication&comma; i&period;e&period; having multiple servers that you can connect to when you want to do a SELECT query&period; When you do read replication&comma; you specify one or more servers to act as read replicas&comma; and one server to act as the write master&comma; which handles all writes and updates and propagates them to the replicas &lpar;note that the actual replication process is **not** handled by Sequelize&comma; but should be set up in MySql&rpar;&period;
```js ```js
var sequelize = new Sequelize('database', null, null, { var sequelize = new Sequelize('database', null, null, {
dialect: 'mysql', dialect: 'mysql',
...@@ -139,7 +147,7 @@ var sequelize = new Sequelize('database', null, null, { ...@@ -139,7 +147,7 @@ var sequelize = new Sequelize('database', null, null, {
If you have any general settings that apply to all replicas you do not need to provide them for each instance&period; In the code above&comma; database name and port is propagated to all replicas&period; The same will happen for user and password&comma; if you leave them out for any of the replicas&period; Each replica has the following options&colon;`host`&comma;`port`&comma;`username`&comma;`password`&comma;`database`. If you have any general settings that apply to all replicas you do not need to provide them for each instance&period; In the code above&comma; database name and port is propagated to all replicas&period; The same will happen for user and password&comma; if you leave them out for any of the replicas&period; Each replica has the following options&colon;`host`&comma;`port`&comma;`username`&comma;`password`&comma;`database`.
Sequelize uses a pool to manage connections to your replicas&period; The default options are&colon; Sequelize uses a pool to manage connections to your replicas&period; The default options are&colon;
```js ```js
{ {
maxConnections: 10, maxConnections: 10,
...@@ -159,7 +167,7 @@ With the release of Sequelize`1.6.0`&comma; the library got independent from spe ...@@ -159,7 +167,7 @@ With the release of Sequelize`1.6.0`&comma; the library got independent from spe
### MySQL ### MySQL
In order to get Sequelize working nicely together with MySQL&comma; you'll need to install`mysql@~2.5.0`or higher. Once that's done you can use it like this&colon; In order to get Sequelize working nicely together with MySQL&comma; you'll need to install`mysql@~2.5.0`or higher. Once that's done you can use it like this&colon;
```js ```js
var sequelize = new Sequelize('database', 'username', 'password', { var sequelize = new Sequelize('database', 'username', 'password', {
// mysql is the default dialect, but you know... // mysql is the default dialect, but you know...
...@@ -177,8 +185,8 @@ for examples (currently only mysql and mariadb are supported). ...@@ -177,8 +185,8 @@ for examples (currently only mysql and mariadb are supported).
For MariaDB compatibility you have to install the package `mariasql@~0.1.20`. For MariaDB compatibility you have to install the package `mariasql@~0.1.20`.
The configuration needs to look like this: The configuration needs to look like this:
```js ```js
var sequelize = new Sequelize('database', 'username', 'password', { var sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mariadb' dialect: 'mariadb'
}) })
...@@ -188,7 +196,7 @@ var sequelize = new Sequelize('database', 'username', 'password', { ...@@ -188,7 +196,7 @@ var sequelize = new Sequelize('database', 'username', 'password', {
For SQLite compatibility you'll need`sqlite3@~3.0.0`. Configure Sequelize like this&colon; For SQLite compatibility you'll need`sqlite3@~3.0.0`. Configure Sequelize like this&colon;
```js ```js
var sequelize = new Sequelize('database', 'username', 'password', { var sequelize = new Sequelize('database', 'username', 'password', {
// sqlite! now! // sqlite! now!
dialect: 'sqlite', dialect: 'sqlite',
...@@ -202,8 +210,8 @@ var sequelize = new Sequelize('database', 'username', 'password', { ...@@ -202,8 +210,8 @@ var sequelize = new Sequelize('database', 'username', 'password', {
### PostgreSQL ### PostgreSQL
The library for PostgreSQL is`pg@~3.6.0` You'll just need to define the dialect&colon; The library for PostgreSQL is`pg@~3.6.0` You'll just need to define the dialect&colon;
```js ```js
var sequelize = new Sequelize('database', 'username', 'password', { var sequelize = new Sequelize('database', 'username', 'password', {
// gimme postgres, please! // gimme postgres, please!
dialect: 'postgres' dialect: 'postgres'
...@@ -213,8 +221,8 @@ var sequelize = new Sequelize('database', 'username', 'password', { ...@@ -213,8 +221,8 @@ var sequelize = new Sequelize('database', 'username', 'password', {
### MSSQL ### MSSQL
The library for MSSQL is`tedious@^1.7.0` You'll just need to define the dialect&colon; The library for MSSQL is`tedious@^1.7.0` You'll just need to define the dialect&colon;
```js ```js
var sequelize = new Sequelize('database', 'username', 'password', { var sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mssql' dialect: 'mssql'
}) })
...@@ -225,29 +233,29 @@ var sequelize = new Sequelize('database', 'username', 'password', { ...@@ -225,29 +233,29 @@ var sequelize = new Sequelize('database', 'username', 'password', {
As there are often use cases in which it is just easier to execute raw &sol; already prepared SQL queries, you can utilize the function `sequelize.query`. As there are often use cases in which it is just easier to execute raw &sol; already prepared SQL queries, you can utilize the function `sequelize.query`.
Here is how it works: 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
...@@ -6,176 +6,174 @@ var Utils = require('./../utils') ...@@ -6,176 +6,174 @@ 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'; this.source = source;
this.source = source; this.target = target;
this.target = target; this.options = options;
this.options = options; this.scope = options.scope;
this.scope = options.scope; this.isSingleAssociation = true;
this.isSingleAssociation = true; this.isSelfAssociation = (this.source === this.target);
this.isSelfAssociation = (this.source === this.target); this.as = this.options.as;
this.as = this.options.as;
if (Utils._.isObject(this.options.foreignKey)) {
if (Utils._.isObject(this.options.foreignKey)) { this.foreignKeyAttribute = this.options.foreignKey;
this.foreignKeyAttribute = this.options.foreignKey; this.foreignKey = this.foreignKeyAttribute.name || this.foreignKeyAttribute.fieldName;
this.foreignKey = this.foreignKeyAttribute.name || this.foreignKeyAttribute.fieldName; } else {
} else { this.foreignKeyAttribute = {};
this.foreignKeyAttribute = {}; this.foreignKey = this.options.foreignKey;
this.foreignKey = this.options.foreignKey; }
}
if (this.as) {
if (this.as) { this.isAliased = true;
this.isAliased = true; this.options.name = {
this.options.name = { singular: this.as
singular: this.as };
}; } else {
} else { this.as = this.target.options.name.singular;
this.as = this.target.options.name.singular; this.options.name = this.target.options.name;
this.options.name = this.target.options.name; }
}
if (!this.options.foreignKey) {
this.options.foreignKey = Utils._.camelizeIf(
[
Utils._.underscoredIf(this.as, this.source.options.underscored),
this.target.primaryKeyAttribute
].join('_'),
!this.source.options.underscored
);
}
this.identifier = this.foreignKey || Utils._.camelizeIf( if (!this.options.foreignKey) {
this.options.foreignKey = Utils._.camelizeIf(
[ [
Utils._.underscoredIf(this.options.name.singular, this.target.options.underscored), Utils._.underscoredIf(this.as, this.source.options.underscored),
this.target.primaryKeyAttribute this.target.primaryKeyAttribute
].join('_'), ].join('_'),
!this.target.options.underscored !this.source.options.underscored
); );
}
this.targetIdentifier = this.target.primaryKeyAttribute;
this.associationAccessor = this.as; this.identifier = this.foreignKey || Utils._.camelizeIf(
this.options.useHooks = options.useHooks; [
Utils._.underscoredIf(this.options.name.singular, this.target.options.underscored),
// Get singular name, trying to uppercase the first letter, unless the model forbids it this.target.primaryKeyAttribute
var singular = Utils.uppercaseFirst(this.options.name.singular); ].join('_'),
!this.target.options.underscored
this.accessors = { );
get: 'get' + singular,
set: 'set' + singular, this.targetIdentifier = this.target.primaryKeyAttribute;
create: 'create' + singular this.associationAccessor = this.as;
}; this.options.useHooks = options.useHooks;
// Get singular name, trying to uppercase the first letter, unless the model forbids it
var singular = Utils.uppercaseFirst(this.options.name.singular);
this.accessors = {
get: 'get' + singular,
set: 'set' + 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 });
if (this.options.constraints !== false) { if (this.options.constraints !== false) {
this.options.onDelete = this.options.onDelete || 'SET NULL'; this.options.onDelete = this.options.onDelete || 'SET NULL';
this.options.onUpdate = this.options.onUpdate || 'CASCADE'; this.options.onUpdate = this.options.onUpdate || 'CASCADE';
} }
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.target, this.source, this.options); Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.target, this.source, this.options);
Utils.mergeDefaults(this.source.rawAttributes, newAttributes); Utils.mergeDefaults(this.source.rawAttributes, newAttributes);
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) {
var where = {}; var where = {};
where[association.targetIdentifier] = this.get(association.identifier); where[association.targetIdentifier] = this.get(association.identifier);
options = association.target.__optClone(options) || {}; options = association.target.__optClone(options) || {};
options.where = { options.where = {
$and: [ $and: [
options.where, options.where,
where where
] ]
}; };
if (options.limit === undefined) options.limit = null; if (options.limit === undefined) options.limit = null;
var model = association.target; var model = association.target;
if (options.hasOwnProperty('scope')) { if (options.hasOwnProperty('scope')) {
if (!options.scope) { if (!options.scope) {
model = model.unscoped(); model = model.unscoped();
} else { } else {
model = model.scope(options.scope); model = model.scope(options.scope);
}
} }
}
return model.find(options); return model.find(options);
};
return this;
}; };
// Add setAssociaton method to the prototype of the model instance return this;
BelongsTo.prototype.injectSetter = function(instancePrototype) { };
var association = this;
instancePrototype[this.accessors.set] = function(associatedInstance, options) { // Add setAssociaton method to the prototype of the model instance
options = options || {}; BelongsTo.prototype.injectSetter = function(instancePrototype) {
var association = this;
var value = associatedInstance; instancePrototype[this.accessors.set] = function(associatedInstance, options) {
if (associatedInstance instanceof association.target.Instance) { options = options || {};
value = associatedInstance[association.targetIdentifier];
}
this.set(association.identifier, value); var value = associatedInstance;
if (associatedInstance instanceof association.target.Instance) {
value = associatedInstance[association.targetIdentifier];
}
if (options.save === false) return; this.set(association.identifier, value);
options = Utils._.extend({ if (options.save === false) return;
fields: [association.identifier],
allowNull: [association.identifier],
association: true
}, options);
options = Utils._.extend({
fields: [association.identifier],
allowNull: [association.identifier],
association: true
}, options);
// passes the changed field to save, so only that field get updated.
return this.save(options);
};
return this; // passes the changed field to save, so only that field get updated.
return this.save(options);
}; };
// Add createAssociation method to the prototype of the model instance return this;
BelongsTo.prototype.injectCreator = function(instancePrototype) { };
var association = this;
instancePrototype[this.accessors.create] = function(values, fieldsOrOptions) { // Add createAssociation method to the prototype of the model instance
var instance = this BelongsTo.prototype.injectCreator = function(instancePrototype) {
, options = {}; var association = this;
if ((fieldsOrOptions || {}).transaction instanceof Transaction) { instancePrototype[this.accessors.create] = function(values, fieldsOrOptions) {
options.transaction = fieldsOrOptions.transaction; var instance = this
} , options = {};
return association.target.create(values, fieldsOrOptions).then(function(newAssociatedObject) { if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
return instance[association.accessors.set](newAssociatedObject, options); options.transaction = fieldsOrOptions.transaction;
}); }
}; options.logging = (fieldsOrOptions || {}).logging;
return this; return association.target.create(values, fieldsOrOptions).then(function(newAssociatedObject) {
return instance[association.accessors.set](newAssociatedObject, options);
});
}; };
return BelongsTo; return this;
})(); };
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) {
var scopeWhere = this.association.scope ? {} : null;
if (this.association.scope) {
Object.keys(this.association.scope).forEach(function (attribute) {
scopeWhere[attribute] = this.association.scope[attribute];
}.bind(this));
}
options.where = {
$and: [
new Utils.where(
this.target.rawAttributes[this.association.identifier],
this.instance[this.source.primaryKeyAttribute]
),
scopeWhere,
options.where
]
}; };
HasManySingleLinked.prototype.injectGetter = function(options, queryOptions) { var model = this.association.target;
var scopeWhere = this.association.scope ? {} : null; if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return model.all(options);
};
HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) {
var self = this
, primaryKeys
, primaryKey
, updateWhere
, associationKeys = Object.keys((oldAssociations[0] || newAssociations[0] || {Model: {primaryKeys: {}}}).Model.primaryKeys || {})
, associationKey = (associationKeys.length === 1) ? associationKeys[0] : 'id'
, options = defaultAttributes
, promises = []
, obsoleteAssociations = oldAssociations.filter(function(old) {
return !Utils._.find(newAssociations, function(obj) {
return obj[associationKey] === old[associationKey];
});
})
, unassociatedObjects = newAssociations.filter(function(obj) {
return !Utils._.find(oldAssociations, function(old) {
return obj[associationKey] === old[associationKey];
});
})
, update;
if (obsoleteAssociations.length > 0) {
// clear the old associations
var obsoleteIds = obsoleteAssociations.map(function(associatedObject) {
associatedObject[self.association.identifier] = (newAssociations.length < 1 ? null : self.instance.id);
return associatedObject[associationKey];
});
update = {};
update[self.association.identifier] = null;
primaryKeys = Object.keys(this.association.target.primaryKeys);
primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id';
updateWhere = {};
updateWhere[primaryKey] = obsoleteIds;
promises.push(this.association.target.unscoped().update(
update,
Utils._.extend(options, {
allowNull: [self.association.identifier],
where: updateWhere
})
));
}
if (unassociatedObjects.length > 0) {
// For the self.instance
var pkeys = Object.keys(self.instance.Model.primaryKeys)
, pkey = pkeys.length === 1 ? pkeys[0] : 'id';
primaryKeys = Object.keys(this.association.target.primaryKeys);
primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id';
updateWhere = {};
// set the new associations
var unassociatedIds = unassociatedObjects.map(function(associatedObject) {
associatedObject[self.association.identifier] = self.instance[pkey] || self.instance.id;
return associatedObject[associationKey];
});
update = {};
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);
scopeWhere[attribute] = this.association.scope[attribute];
}.bind(this));
} }
options.where = { updateWhere[primaryKey] = unassociatedIds;
$and: [
new Utils.where(
this.target.rawAttributes[this.association.identifier],
this.instance[this.source.primaryKeyAttribute]
),
scopeWhere,
options.where
]
};
var model = this.association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return model.all(options, queryOptions); promises.push(this.association.target.unscoped().update(
}; update,
Utils._.extend(options, {
allowNull: [self.association.identifier],
where: updateWhere
})
));
}
HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) { return Utils.Promise.all(promises);
var self = this };
, primaryKeys
, primaryKey
, updateWhere
, associationKeys = Object.keys((oldAssociations[0] || newAssociations[0] || {Model: {primaryKeys: {}}}).Model.primaryKeys || {})
, associationKey = (associationKeys.length === 1) ? associationKeys[0] : 'id'
, options = defaultAttributes
, promises = []
, obsoleteAssociations = oldAssociations.filter(function(old) {
return !Utils._.find(newAssociations, function(obj) {
return obj[associationKey] === old[associationKey];
});
})
, unassociatedObjects = newAssociations.filter(function(obj) {
return !Utils._.find(oldAssociations, function(old) {
return obj[associationKey] === old[associationKey];
});
})
, update;
if (obsoleteAssociations.length > 0) {
// clear the old associations
var obsoleteIds = obsoleteAssociations.map(function(associatedObject) {
associatedObject[self.association.identifier] = (newAssociations.length < 1 ? null : self.instance.id);
return associatedObject[associationKey];
});
update = {};
update[self.association.identifier] = null;
primaryKeys = Object.keys(this.association.target.primaryKeys);
primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id';
updateWhere = {};
updateWhere[primaryKey] = obsoleteIds;
promises.push(this.association.target.unscoped().update(
update,
Utils._.extend(options, {
allowNull: [self.association.identifier],
where: updateWhere
})
));
}
if (unassociatedObjects.length > 0) { HasManySingleLinked.prototype.injectAdder = function(newAssociation, options) {
// For the self.instance newAssociation.set(this.association.identifier, this.instance.get(this.instance.Model.primaryKeyAttribute));
var pkeys = Object.keys(self.instance.Model.primaryKeys) if (this.association.scope) {
, pkey = pkeys.length === 1 ? pkeys[0] : 'id'; Object.keys(this.association.scope).forEach(function (attribute) {
newAssociation.set(attribute, this.association.scope[attribute]);
primaryKeys = Object.keys(this.association.target.primaryKeys); }.bind(this));
primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'; }
updateWhere = {};
// set the new associations
var unassociatedIds = unassociatedObjects.map(function(associatedObject) {
associatedObject[self.association.identifier] = self.instance[pkey] || self.instance.id;
return associatedObject[associationKey];
});
update = {};
update[self.association.identifier] = (newAssociations.length < 1 ? null : self.instance[pkey] || self.instance.id);
if (this.association.scope) {
Object.keys(this.association.scope).forEach(function (attribute) {
update[attribute] = this.association.scope[attribute];
}.bind(this));
}
updateWhere[primaryKey] = unassociatedIds;
promises.push(this.association.target.unscoped().update(
update,
Utils._.extend(options, {
allowNull: [self.association.identifier],
where: updateWhere
})
));
}
return Utils.Promise.all(promises); return newAssociation.save(options);
}; };
HasManySingleLinked.prototype.injectAdder = function(newAssociation, options) {
newAssociation.set(this.association.identifier, this.instance.get(this.instance.Model.primaryKeyAttribute));
if (this.association.scope) {
Object.keys(this.association.scope).forEach(function (attribute) {
newAssociation.set(attribute, this.association.scope[attribute]);
}.bind(this));
}
return newAssociation.save(options);
};
return HasManySingleLinked; module.exports = HasManySingleLinked;
})();
...@@ -2,46 +2,50 @@ ...@@ -2,46 +2,50 @@
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 + '\' and association \'' + association.as + '\' on model ' + association.source.name +
'\' and association \'' + association.as + '\' on model ' + association.source.name + '. 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
if (options.foreignKeyConstraint || options.onDelete || options.onUpdate) { if (options.foreignKeyConstraint || options.onDelete || options.onUpdate) {
// Find primary keys: composite keys not supported with this approach // Find primary keys: composite keys not supported with this approach
var primaryKeys = Utils._.chain(source.rawAttributes).keys() var primaryKeys = Utils._.chain(source.rawAttributes).keys()
.filter(function(key) { return source.rawAttributes[key].primaryKey; }) .filter(function(key) { return source.rawAttributes[key].primaryKey; })
.map(function(key) { return source.rawAttributes[key].field || key; }).value(); .map(function(key) { return source.rawAttributes[key].field || key; }).value();
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 { };
newAttribute.references = source.tableName; } else {
} newAttribute.references = { model: source.tableName };
newAttribute.referencesKey = primaryKeys[0];
newAttribute.onDelete = options.onDelete;
newAttribute.onUpdate = options.onUpdate;
} }
newAttribute.references.key = primaryKeys[0];
newAttribute.onDelete = options.onDelete;
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;
This diff could not be displayed because it is too large.
...@@ -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;
......
...@@ -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
}); });
......
...@@ -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;
......
...@@ -3,60 +3,64 @@ ...@@ -3,60 +3,64 @@
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 ''; if (Utils._.any(data, Utils._.isNull)) return '';
if (Utils._.any(data, Utils._.isNull)) return ''; if (data.hasOwnProperty('inclusive')) {
if (!data.inclusive) data.inclusive = [false, false];
else if (data.inclusive === true) data.inclusive = [true, true];
} else {
data.inclusive = [false, false];
}
if (data.hasOwnProperty('inclusive')) { Utils._.each(data, function (value, index) {
if (!data.inclusive) data.inclusive = [false, false]; if (Utils._.isObject(value)) {
else if (data.inclusive === true) data.inclusive = [true, true]; if (value.hasOwnProperty('inclusive')) data.inclusive[index] = !!value.inclusive;
} else { if (value.hasOwnProperty('value')) data[index] = value.value;
data.inclusive = [false, false];
} }
});
return (data.inclusive[0] ? '[' : '(') + JSON.stringify(data[0]) + ',' + JSON.stringify(data[1]) + (data.inclusive[1] ? ']' : ')');
}
function parse (value, AttributeType) {
if (value === null) return null;
if(typeof AttributeType === 'function') AttributeType = new AttributeType();
AttributeType = AttributeType || ''; // if attribute is not defined, assign empty string in order to prevent
// AttributeType.toString() to fail with uncaught exception later in the code
var result = value
.substring(1, value.length - 1)
.split(',', 2);
Utils._.each(data, function (value, index) { if (result.length !== 2) return value;
if (Utils._.isObject(value)) {
if (value.hasOwnProperty('inclusive')) data.inclusive[index] = !!value.inclusive; result = result
if (value.hasOwnProperty('value')) data[index] = value.value; .map(function (value) {
switch (AttributeType.toString()) {
case 'int4range':
return parseInt(value, 10);
case 'numrange':
return parseFloat(value);
case 'daterange':
case 'tsrange':
case 'tstzrange':
return moment(value).toDate();
} }
return value;
}); });
return (data.inclusive[0] ? '[' : '(') + JSON.stringify(data[0]) + ',' + JSON.stringify(data[1]) + (data.inclusive[1] ? ']' : ')'); result.inclusive = [(value[0] === '['), (value[value.length - 1] === ']')];
},
parse: function (value, AttributeType) { return result;
if (value === null) return null; }
if(typeof AttributeType === 'function') AttributeType = new AttributeType(); module.exports = {
AttributeType = AttributeType || ''; // if attribute is not defined, assign empty string in order to prevent stringify: stringify,
// AttributeType.toString() to fail with uncaught exception later in the code parse: parse
var result = value
.substring(1, value.length - 1)
.split(',', 2);
if (result.length !== 2) return value;
result = result
.map(function (value) {
switch (AttributeType.toString()) {
case 'int4range':
return parseInt(value, 10);
case 'numrange':
return parseFloat(value);
case 'daterange':
case 'tsrange':
case 'tstzrange':
return moment(value).toDate();
}
return value;
});
result.inclusive = [(value[0] === '['), (value[value.length - 1] === ']')];
return result;
}
}; };
...@@ -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);
......
...@@ -9,106 +9,111 @@ var Utils = require('../../utils') ...@@ -9,106 +9,111 @@ 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.
@method removeColumn @method removeColumn
@for QueryInterface @for QueryInterface
@param {String} tableName The name of the table. @param {String} tableName The name of the table.
@param {String} attributeName The name of the attribute that we want to remove. @param {String} attributeName The name of the attribute that we want to remove.
@param {Object} options @param {Object} options
@param {Boolean|Function} [options.logging] A function that logs the sql queries, or false for explicitly not logging these queries @param {Boolean|Function} [options.logging] A function that logs the sql queries, or false for explicitly not logging these queries
@param {CustomEventEmitter} emitter The EventEmitter from outside. @param {CustomEventEmitter} emitter The EventEmitter from outside.
@param {Function} queryAndEmit The function from outside that triggers some events to get triggered. @param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
@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 || {};
return this.describeTable(tableName).then(function(fields) { return this.describeTable(tableName).then(function(fields) {
delete fields[attributeName]; delete fields[attributeName];
var sql = self.QueryGenerator.removeColumnQuery(tableName, fields) var sql = self.QueryGenerator.removeColumnQuery(tableName, fields)
, subQueries = sql.split(';').filter(function(q) { return q !== ''; }); , subQueries = sql.split(';').filter(function(q) { return q !== ''; });
return Promise.each(subQueries, function(subQuery) { return Promise.each(subQueries, function(subQuery) {
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. /**
It will create a backup of the table, drop the table afterwards and create a A wrapper that fixes SQLite's inability to change columns from existing tables.
new table with the same name but with a modified version of the respective column. 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.
@method changeColumn
@for QueryInterface @method changeColumn
@for QueryInterface
@param {String} tableName The name of the table.
@param {Object} attributes An object with the attribute's name as key and it's options as value object. @param {String} tableName The name of the table.
@param {Object} options @param {Object} attributes An object with the attribute's name as key and it's options as value object.
@param {Boolean|Function} [options.logging] A function that logs the sql queries, or false for explicitly not logging these queries @param {Object} options
@param {CustomEventEmitter} emitter The EventEmitter from outside. @param {Boolean|Function} [options.logging] A function that logs the sql queries, or false for explicitly not logging these queries
@param {Function} queryAndEmit The function from outside that triggers some events to get triggered. @param {CustomEventEmitter} emitter The EventEmitter from outside.
@param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
@since 1.6.0
*/ @since 1.6.0
changeColumn: function(tableName, attributes, options) { */
var attributeName = Utils._.keys(attributes)[0] var changeColumn = function(tableName, attributes, options) {
, self = this; var attributeName = Utils._.keys(attributes)[0]
options = options || {}; , self = this;
options = options || {};
return this.describeTable(tableName).then(function(fields) {
fields[attributeName] = attributes[attributeName]; return this.describeTable(tableName).then(function(fields) {
fields[attributeName] = attributes[attributeName];
var sql = self.QueryGenerator.removeColumnQuery(tableName, fields)
, subQueries = sql.split(';').filter(function(q) { return q !== ''; }); var sql = self.QueryGenerator.removeColumnQuery(tableName, fields)
, subQueries = sql.split(';').filter(function(q) { return q !== ''; });
return Promise.each(subQueries, function(subQuery) {
return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging }); return Promise.each(subQueries, function(subQuery) {
}); return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging });
}); });
}, });
};
/**
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 A wrapper that fixes SQLite's inability to rename columns from existing tables.
new table with the same name but with a renamed version of the respective column. 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.
@method renameColumn
@for QueryInterface @method renameColumn
@for QueryInterface
@param {String} tableName The name of the table.
@param {String} attrNameBefore The name of the attribute before it was renamed. @param {String} tableName The name of the table.
@param {String} attrNameAfter The name of the attribute after it was renamed. @param {String} attrNameBefore The name of the attribute before it was renamed.
@param {Object} options @param {String} attrNameAfter The name of the attribute after it was renamed.
@param {Boolean|Function} [options.logging] A function that logs the sql queries, or false for explicitly not logging these queries @param {Object} options
@param {CustomEventEmitter} emitter The EventEmitter from outside. @param {Boolean|Function} [options.logging] A function that logs the sql queries, or false for explicitly not logging these queries
@param {Function} queryAndEmit The function from outside that triggers some events to get triggered. @param {CustomEventEmitter} emitter The EventEmitter from outside.
@param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
@since 1.6.0
*/ @since 1.6.0
renameColumn: function(tableName, attrNameBefore, attrNameAfter, options) { */
var self = this; var renameColumn = function(tableName, attrNameBefore, attrNameAfter, options) {
options = options || {}; var self = this;
options = options || {};
return this.describeTable(tableName).then(function(fields) {
fields[attrNameAfter] = Utils._.clone(fields[attrNameBefore]); return this.describeTable(tableName).then(function(fields) {
delete fields[attrNameBefore]; fields[attrNameAfter] = Utils._.clone(fields[attrNameBefore]);
delete fields[attrNameBefore];
var sql = self.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields)
, subQueries = sql.split(';').filter(function(q) { return q !== ''; }); var sql = self.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields)
, subQueries = sql.split(';').filter(function(q) { return q !== ''; });
return Promise.each(subQueries, function(subQuery) {
return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging }); return Promise.each(subQueries, function(subQuery) {
}); return self.sequelize.query(subQuery + ';', { raw: true, logging: options.logging });
}); });
} });
}; };
module.exports = {
removeColumn: removeColumn,
changeColumn: changeColumn,
renameColumn: renameColumn
};
\ No newline at end of file
'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;
})();
...@@ -138,7 +138,7 @@ util.inherits(error.TimeoutError, error.BaseError); ...@@ -138,7 +138,7 @@ util.inherits(error.TimeoutError, error.BaseError);
error.UniqueConstraintError = function (options) { error.UniqueConstraintError = function (options) {
options = options || {}; options = options || {};
options.parent = options.parent || { sql: '' }; options.parent = options.parent || { sql: '' };
options.message = options.message || 'Validation error'; options.message = options.message || options.parent.message || 'Validation Error';
options.errors = options.errors || {}; options.errors = options.errors || {};
error.ValidationError.call(this, options.message, options.errors); error.ValidationError.call(this, options.message, options.errors);
...@@ -161,7 +161,7 @@ error.ForeignKeyConstraintError = function (options) { ...@@ -161,7 +161,7 @@ error.ForeignKeyConstraintError = function (options) {
error.DatabaseError.call(this, options.parent); error.DatabaseError.call(this, options.parent);
this.name = 'SequelizeForeignKeyConstraintError'; this.name = 'SequelizeForeignKeyConstraintError';
this.message = options.message; this.message = options.message || options.parent.message || 'Database Error';
this.fields = options.fields; this.fields = options.fields;
this.table = options.table; this.table = options.table;
this.value = options.value; this.value = options.value;
...@@ -181,7 +181,7 @@ error.ExclusionConstraintError = function (options) { ...@@ -181,7 +181,7 @@ error.ExclusionConstraintError = function (options) {
error.DatabaseError.call(this, options.parent); error.DatabaseError.call(this, options.parent);
this.name = 'SequelizeExclusionConstraintError'; this.name = 'SequelizeExclusionConstraintError';
this.message = options.message; this.message = options.message || options.parent.message;
this.constraint = options.constraint; this.constraint = options.constraint;
this.fields = options.fields; this.fields = options.fields;
this.table = options.table; this.table = options.table;
......
...@@ -89,7 +89,7 @@ var Hooks = { ...@@ -89,7 +89,7 @@ var Hooks = {
runHooks: function(hooks) { runHooks: function(hooks) {
var self = this var self = this
, fn , fn
, fnArgs = Array.prototype.slice.call(arguments, 1) , fnArgs = Utils.sliceArgs(arguments, 1)
, hookType; , hookType;
if (typeof fnArgs[fnArgs.length - 1] === 'function') { if (typeof fnArgs[fnArgs.length - 1] === 'function') {
......
...@@ -90,9 +90,9 @@ function extendModelValidations(modelInstance) { ...@@ -90,9 +90,9 @@ function extendModelValidations(modelInstance) {
/** /**
* The Main DAO Validator. * The Main Instance Validator.
* *
* @param {DAO} modelInstance The model instance. * @param {Instance} modelInstance The model instance.
* @param {Object} options A dict with options. * @param {Object} options A dict with options.
* @constructor * @constructor
*/ */
...@@ -256,12 +256,17 @@ InstanceValidator.prototype._builtinAttrValidate = function(value, field) { ...@@ -256,12 +256,17 @@ InstanceValidator.prototype._builtinAttrValidate = function(value, field) {
Utils._.forIn(this.modelInstance.validators[field], function(test, Utils._.forIn(this.modelInstance.validators[field], function(test,
validatorType) { validatorType) {
if (['isUrl', 'isURL', 'isEmail'].indexOf(validatorType) !== -1 && test === true) { if (['isUrl', 'isURL', 'isEmail'].indexOf(validatorType) !== -1) {
// Preserve backwards compat. Validator.js now expects the second param to isURL and isEmail to be an object // Preserve backwards compat. Validator.js now expects the second param to isURL and isEmail to be an object
test = {}; if (typeof test === 'object' && test !== null && test.msg) {
test = {
msg: test.msg
};
} else if (test === true) {
test = {};
}
} }
// Check for custom validator. // Check for custom validator.
if (typeof test === 'function') { if (typeof test === 'function') {
return validators.push(self._invokeCustomValidator(test, validatorType, true, value, field)); return validators.push(self._invokeCustomValidator(test, validatorType, true, value, field));
...@@ -273,8 +278,7 @@ InstanceValidator.prototype._builtinAttrValidate = function(value, field) { ...@@ -273,8 +278,7 @@ InstanceValidator.prototype._builtinAttrValidate = function(value, field) {
validators.push(validatorPromise); validators.push(validatorPromise);
}); });
return Promise.settle(validators) return Promise.settle(validators).then(this._handleSettledResult.bind(this, field));
.then(this._handleSettledResult.bind(this, field));
}; };
/** /**
...@@ -311,11 +315,9 @@ InstanceValidator.prototype._invokeCustomValidator = Promise.method(function(val ...@@ -311,11 +315,9 @@ InstanceValidator.prototype._invokeCustomValidator = Promise.method(function(val
} else { } else {
validatorFunction = Promise.promisify(validator.bind(this.modelInstance)); validatorFunction = Promise.promisify(validator.bind(this.modelInstance));
} }
return validatorFunction() return validatorFunction().catch(this._pushError.bind(this, false, errorKey));
.catch(this._pushError.bind(this, false, errorKey));
} else { } else {
return Promise.try(validator.bind(this.modelInstance, invokeArgs)) return Promise.try(validator.bind(this.modelInstance, invokeArgs)).catch(this._pushError.bind(this, false, errorKey));
.catch(this._pushError.bind(this, false, errorKey));
} }
}); });
...@@ -336,21 +338,23 @@ InstanceValidator.prototype._invokeBuiltinValidator = Promise.method(function(va ...@@ -336,21 +338,23 @@ InstanceValidator.prototype._invokeBuiltinValidator = Promise.method(function(va
} }
// extract extra arguments for the validator // extract extra arguments for the validator
var validatorArgs = test.hasOwnProperty('args') || test.hasOwnProperty('msg') ? test.args : test; var validatorArgs = test.args || test;
// extract the error msg
var errorMessage = test.hasOwnProperty('msg') && test.msg ? test.msg :
'Validation ' + validatorType + ' failed';
if (!Array.isArray(validatorArgs)) { if (!Array.isArray(validatorArgs)) {
validatorArgs = [validatorArgs]; if (validatorType === 'isImmutable') {
validatorArgs = [validatorArgs, field];
} else if (validatorType === 'isIP') {
validatorArgs = [];
} else {
validatorArgs = [validatorArgs];
}
} else { } else {
validatorArgs = validatorArgs.slice(0); validatorArgs = validatorArgs.slice(0);
} }
validatorArgs.push(field);
if (!Validator[validatorType].apply(Validator, [value].concat(validatorArgs))) { if (!Validator[validatorType].apply(Validator, [value].concat(validatorArgs))) {
throw errorMessage; // extract the error msg
throw test.msg || 'Validation ' + validatorType + ' failed';
} }
}); });
......
This diff could not be displayed because it is too large.
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!