diff --git a/.gitignore b/.gitignore index 13d90eb69e13..1cc9f561916a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ test*.js .DS_STORE node_modules npm-debug.log +*~ \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 000000000000..e723c4c1c1ff --- /dev/null +++ b/.jshintrc @@ -0,0 +1,22 @@ +{ + "globals": { + "jasmine": false, + "spyOn": false, + "it": false, + "console": false, + "describe": false, + "expect": false, + "beforeEach": false, + "waits": false, + "waitsFor": false, + "runs": false + }, + "camelcase": true, + "curly": true, + "forin": true, + "indent": 2, + "unused": true, + "asi": true, + "evil": false, + "laxcomma": true +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 27c775b3864f..cd439b421dd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,4 @@ env: language: node_js node_js: - # - 0.6 - - 0.8 - # - 0.9 - + - 0.8 \ No newline at end of file diff --git a/.watchr.js b/.watchr.js new file mode 100644 index 000000000000..9e4c5878da9a --- /dev/null +++ b/.watchr.js @@ -0,0 +1,17 @@ +var watchr = require('watchr') + , spawn = require('child_process').spawn + +watchr.watch({ + paths: [__dirname + '/lib', __dirname + '/spec', __dirname + '/index.js'], + listener: function(eventName) { + if (['new', 'change'].indexOf(eventName) > -1) { + // generate the documentation + spawn('npm', ['run', 'docs']) + + // run the tests + var buster = spawn('./node_modules/.bin/buster-test', ['--reporter', 'specification'], { env: process.ENV }) + buster.stderr.on('data', function(data) { console.log(data.toString()) }) + buster.stdout.on('data', function(data) { console.log(data.toString()) }) + } + } +}) diff --git a/README.md b/README.md index 648b52263160..5bfb0cd7770c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,18 @@ The Sequelize library provides easy access to MySQL, SQLite or PostgreSQL databases by mapping database entries to objects and vice versa. To put it in a nutshell... it's an ORM (Object-Relational-Mapper). The library is written entirely in JavaScript and can be used in the Node.JS environment. + +Flattr this + +## Important Notes ## + +### 1.6.0 ### + +- We changed the way timestamps are handled. From v1.6.0 on timestamps are stored and loaded as UTC. +- Support for synchronous migrations has been dropped. `up` and `down` methods in migrations do have a third parameter which is the callback parameter. Pass an error or an error message as first parameter to the callback if something went wrong in the migration. + ## Blogposts/Changes ## +- [v1.6.0](http://blog.sequelizejs.com/post/46949108134/v1-6-0-eager-loading-support-for-enums-decimals-and) Eager loading, support for enums, decimals and bigint, performance improvements … - [v1.4.1](http://blog.sequelizejs.com/post/24403298792/changes-in-sequelize-1-4-1): deprecation of node < 0.6, logging customization, ... - [v1.4.0](http://blog.sequelizejs.com/post/24345409723/changes-in-sequelize-1-4-0): postgresql, connection pooling, ... - [v1.3.0](http://blog.depold.com/post/15283366633/changes-in-sequelize-1-3-0): migrations, cross-database, validations, new listener notation, ... @@ -19,7 +30,7 @@ The Sequelize library provides easy access to MySQL, SQLite or PostgreSQL databa - Associations - Importing definitions from single files -## Documentation, Examples and Updates ## +## Documentation and Updates ## You can find the documentation and announcements of updates on the [project's website](http://www.sequelizejs.com). If you want to know about latest development and releases, follow me on [Twitter](http://twitter.com/sdepold). @@ -27,8 +38,47 @@ Also make sure to take a look at the examples in the repository. The website wil - [Documentation](http://www.sequelizejs.com) - [Twitter](http://twitter.com/sdepold) -- [IRC](irc://irc.freenode.net/sequelizejs) -- [XING](https://www.xing.com/net/priec1b5cx/sequelize) +- [IRC](http://webchat.freenode.net?channels=sequelizejs) +- [Google Groups](https://groups.google.com/forum/#!forum/sequelize) +- [XING](https://www.xing.com/net/priec1b5cx/sequelize) (pretty much inactive, but you might want to name it on your profile) + +## Running Examples +Instructions for running samples are located in the [example directory](https://github.com/sequelize/sequelize/tree/master/examples). Try these samples in a live sandbox environment: + + + +## Roadmap + +A very basic roadmap. Chances aren't too bad, that not mentioned things are implemented as well. Don't panic :) + +### 1.7.0 +- ~~Check if lodash is a proper alternative to current underscore usage.~~ +- Transactions +- Support for update of tables without primary key +- MariaDB support +- ~~Support for update and delete calls for whole tables without previous loading of instances~~ Implemented in [#569](https://github.com/sequelize/sequelize/pull/569) thanks to @optiltude +- Eager loading of nested associations [#388](https://github.com/sdepold/sequelize/issues/388#issuecomment-12019099) +- Model#delete +- ~~Validate a model before it gets saved.~~ Implemented in [#601](https://github.com/sequelize/sequelize/pull/601), thanks to @durango +- Move validation of enum attribute value to validate method +- BLOB [#99](https://github.com/sequelize/sequelize/issues/99) +- ~~Support for foreign keys~~ Implemented in [#595](https://github.com/sequelize/sequelize/pull/595), thanks to @optilude + +### 1.7.x +- Complete support for non-id primary keys + +### 1.8.0 +- API sugar (like Model.select().where().group().include().all()) +- Schema dumping +- ~~enum support~~ +- attributes / values of a dao instance should be scoped + +### 2.0.0 +- ~~save datetimes in UTC~~ +- encapsulate attributes if a dao inside the attributes property +- ~~add getters and setters for dao~~ Implemented in [#538](https://github.com/sequelize/sequelize/pull/538), thanks to iamjochem +- add proper error message everywhere + ## Collaboration 2.0 ## @@ -39,8 +89,8 @@ Still interested? Coolio! Here is how to get started: ### 1. Prepare your environment ### Here comes a little surprise: You need [Node.JS](http://nodejs.org). In order to be -a productive developer, I would recommend the latest v0.8 (or a stable 0.9 if -already out). Also I usually recommend [NVM](https://github.com/creationix/nvm). +a productive developer, I would recommend the latest v0.8. Also I usually recommend +[NVM](https://github.com/creationix/nvm). Once Node.JS is installed on your computer, you will also have access to the lovely Node Package Manager (NPM). @@ -118,10 +168,78 @@ Ah and one last thing: If you think you deserve it, feel free to add yourself to `package.json`. Also I always look for projects which are using sequelize. If you have one of them, drop me a line! +### 6. Some words about coding style ### + +As people are regularly complaining about missing semi-colons and strangely formatted +things, I just want to explain the way I code JavaScript (including Sequelize +... obviously). I won't reject any pull-request because of having a different code +style than me but it would be good to have a consistent way of coding in the whole +project. Here are my rules of thumb: + +- No semi-colons. Where possible I try to avoid semi-colons. Please don't discuss this topic with me. Thanks. +- Curly braces for single line if blocks. I always add curly braces to if blocks. Same for loops and other places. +- Spacing. Indentation = 2 spaces. Also I add a lot of spaces where possible. See below. +- Anonymous functions over names functions. Usually I declare a function and assign it to a variable: `var foo = function() {}` +- Variable declarations. If multiple variables are defined, I use a leading comma for separation. +- Camelcased variable names. No underscores. +- Make sure that key is in objects when iterating over it. See below. + +#### 6.1. Spaces #### + +Use spaces when defining functions. + +```js +function(arg1, arg2, arg3) { + return 1 +} +``` + +Use spaces for if statements. + +```js +if (condition) { + // do something +} else { + // something else +} +``` + +#### 6.2. Variable declarations #### + +```js +var num = 1 + , user = new User() + , date = new Date() +``` + +#### 6.3. For-In-loops #### + +```js +for (var key in obj) { + if (obj.hasOwnProperty(key)) { + console.log(obj[key]) + } +} +``` + +#### 6.4. JSHint options #### + +```js +{ + "camelcase": true, + "curly": true, + "forin": true, + "indent": 2, + "unused": true, + "asi": true, + "evil": false, + "laxcomma": true +} +``` # Build status The automated tests we talk about just so much are running on [Travis public CI](http://travis-ci.org), here is its status: -[![Build Status](https://secure.travis-ci.org/sdepold/sequelize.png)](http://travis-ci.org/sdepold/sequelize) +[![Build Status](https://secure.travis-ci.org/sequelize/sequelize.png)](http://travis-ci.org/sequelize/sequelize) diff --git a/bin/sequelize b/bin/sequelize index d25c8ea60707..b4a60b5a1169 100755 --- a/bin/sequelize +++ b/bin/sequelize @@ -84,7 +84,7 @@ program .version(packageJson.version) .option('-i, --init', 'Initializes the project. Creates a config/config.json') .option('-m, --migrate', 'Runs undone migrations') - .option('-u, --undo', 'Redo the last migration.') + .option('-u, --undo', 'Undo the last migration.') .option('-f, --force', 'Forces the action to be done.') .option('-c, --create-migration [migration-name]', 'Create a new migration skeleton file.') .parse(process.argv) diff --git a/changelog.md b/changelog.md index c29d8389fc74..7a6ad0b21625 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,42 @@ +# v1.7.0 # +- [DEPENDENCIES] Upgraded validator for IPv6 support. [#603](https://github.com/sequelize/sequelize/pull/603). thanks to durango +- [DEPENDENCIES] replaced underscore by lodash. [#954](https://github.com/sequelize/sequelize/pull/594). thanks to durango +- [BUG] Fix string escape with postgresql on raw SQL queries. [#586](https://github.com/sequelize/sequelize/pull/586). thanks to zanamixx +- [BUG] "order by" is now after "group by". [#585](https://github.com/sequelize/sequelize/pull/585). thanks to mekanics +- [BUG] Added decimal support for min/max. [#583](https://github.com/sequelize/sequelize/pull/583). thanks to durango +- [BUG] Null dates don't break SQLite anymore. [#572](https://github.com/sequelize/sequelize/pull/572). thanks to mweibel +- [BUG] Correctly handle booleans in MySQL. [#608](https://github.com/sequelize/sequelize/pull/608). Thanks to terraflubb +- [BUG] Fixed empty where conditions in MySQL. [#619](https://github.com/sequelize/sequelize/pull/619). Thanks to terraflubb +- [BUG] Allow overriding of default columns. [#635](https://github.com/sequelize/sequelize/pull/635). Thanks to sevastos +- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango +- [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango +- [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude +- [FEATURE] Support for bulk insert (`.bulkCreate()`, update (`.update()`) and delete (`.destroy()`) [#569](https://github.com/sequelize/sequelize/pull/569). thanks to optilude +- [FEATURE] Add an extra `queryOptions` parameter to `DAOFactory.find` and `DAOFactory.findAll`. This allows a user to specify `{ raw: true }`, meaning that the raw result should be returned, instead of built DAOs. Usefull for queries returning large datasets, see [#611](https://github.com/sequelize/sequelize/pull/611) janmeier +- [FEATURE] Added convenient data types. [#616](https://github.com/sequelize/sequelize/pull/616). Thanks to Costent +- [FEATURE] Binary is more verbose now. [#612](https://github.com/sequelize/sequelize/pull/612). Thanks to terraflubb +- [FEATURE] Promises/A support. [#626](https://github.com/sequelize/sequelize/pull/626). Thanks to kevinbeaty +- [FEATURE] Added Getters/Setters method for DAO. [#538](https://github.com/sequelize/sequelize/pull/538). Thanks to iamjochem + # v1.6.0 # +- [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work - [DEPENDENCIES] upgraded most dependencies. most important: mysql was upgraded to 2.0.0-alpha-3 +- [DEPENDENCIES] mysql is now an optional dependency. #355 (thanks to clkao) - [REFACTORING] separated tests for dialects +- [REFACTORING] reduced number of sql queries used for adding an element to a N:M association. #449 (thanks to innofluence/janmeier) +- [REFACTORING] dropped support for synchronous migrations. added third parameter which needs to get called once the migration has been finished. also this adds support for asynchronous actions in migrations. +- [OTHERS] code was formatted to fit the latest code style guidelines (thanks to durango) +- [OTHERS] Explicitly target ./docs folder for generate-docs script. #444 (thanks to carsondarling) +- [OTHERS] Overwrite existing daoFactoryDefinition if there already has been one. (thanks to robraux) - [BUG] fixed wrong version in sequelize binary - [BUG] local options have higher priority than global options (thanks to guersam) - [BUG] fixed where clause when passing an empty array (thanks to kbackowski) -- [FEATURE] added association prefetching for find and findAll +- [BUG] fixed updateAttributes for models/tables without primary key (thanks to durango) +- [BUG] fixed the location of the foreign key when using belongsTo (thanks to ricardograca) +- [BUG] don't return timestamps if only specific attributes have been seleceted (thanks to ricardograca) +- [BUG] fixed removeColumn for sqlite +- [BUG] fixed date equality check for instances. (thanks to solotimes) +- [FEATURE] added association prefetching /eager loading for find and findAll. #465 - [FEATURE] it's now possible to use callbacks of async functions inside migrations (thanks to mphilpot) - [FEATURE] improved comfort of sequelize.query. just pass an sql string to it and wait for the result - [FEATURE] Migrations now understand NODE_ENV (thanks to gavri) @@ -17,6 +49,18 @@ - [FEATURE] added possibility to define the attributes of received associations (thanks to joshm) - [FEATURE] added findOrCreate, which returns a the already existing instance or creates one (thanks to eveiga) - [FEATURE] minConnections option for MySQL pooling (thanks to dominiklessel) +- [FEATURE] added BIGINT data type which is treated like a string (thanks to adamsch1) +- [FEATURE] experimental support for read replication for mysql (thanks to Janzeh) +- [FEATURE] allow definition of a models table name (thanks to slamkajs) +- [FEATURE] allow usage of enums. #440 (thanks to KevinMartin) +- [FEATURE] allows updateAttributes to target specific fields only (thanks to Pasvaz) +- [FEATURE] timestamps are now stored as UTC. #461 (thanks to innofluence/janmeier) +- [FEATURE] results of raw queries are parsed with dottie. #468 (thanks to kozze89) +- [FEATURE] support for array serialization. pg only. #443 (thanks to clkao) +- [FEATURE] add increment and decrement methods on dao. #408 (thanks to janmeier/innofluence) +- [FEATURE] unified the result of describeTable +- [FEATURE] add support for decimals (thanks to alexyoung) +- [FEATURE] added DAO.reload(), which updates the attributes of the DAO in-place (as opposed to doing having to do a find() and returning a new model) # v1.5.0 # - [REFACTORING] use underscore functions for Utils.isHash (thanks to Mick-Hansen/innofluence) diff --git a/docs/api.js b/docs/api.js new file mode 100644 index 000000000000..607ecd6f9334 --- /dev/null +++ b/docs/api.js @@ -0,0 +1,18 @@ +YUI.add("yuidoc-meta", function(Y) { + Y.YUIDoc = { meta: { + "classes": [ + "QueryInterface", + "Sequelize" + ], + "modules": [ + "Sequelize" + ], + "allModules": [ + { + "displayName": "Sequelize", + "name": "Sequelize", + "description": "The entry point." + } + ] +} }; +}); \ No newline at end of file diff --git a/docs/assets/css/external-small.png b/docs/assets/css/external-small.png new file mode 100644 index 000000000000..759a1cdcb5b1 Binary files /dev/null and b/docs/assets/css/external-small.png differ diff --git a/docs/assets/css/logo.png b/docs/assets/css/logo.png new file mode 100644 index 000000000000..609b336c7cc5 Binary files /dev/null and b/docs/assets/css/logo.png differ diff --git a/docs/assets/css/main.css b/docs/assets/css/main.css new file mode 100644 index 000000000000..f8f7ee71162d --- /dev/null +++ b/docs/assets/css/main.css @@ -0,0 +1,782 @@ +/* +Font sizes for all selectors other than the body are given in percentages, +with 100% equal to 13px. To calculate a font size percentage, multiply the +desired size in pixels by 7.6923076923. + +Here's a quick lookup table: + +10px - 76.923% +11px - 84.615% +12px - 92.308% +13px - 100% +14px - 107.692% +15px - 115.385% +16px - 123.077% +17px - 130.769% +18px - 138.462% +19px - 146.154% +20px - 153.846% +*/ + +html { + background: #fff; + color: #333; + overflow-y: scroll; +} + +body { + font: 13px/1.4 'Lucida Grande', 'Lucida Sans Unicode', 'DejaVu Sans', 'Bitstream Vera Sans', 'Helvetica', 'Arial', sans-serif; + margin: 0; + padding: 0; +} + +/* -- Links ----------------------------------------------------------------- */ +a { + color: #356de4; + text-decoration: none; +} + +.hidden { + display: none; +} + +a:hover { text-decoration: underline; } + +/* "Jump to Table of Contents" link is shown to assistive tools, but hidden from + sight until it's focused. */ +.jump { + position: absolute; + padding: 3px 6px; + left: -99999px; + top: 0; +} + +.jump:focus { left: 40%; } + +/* -- Paragraphs ------------------------------------------------------------ */ +p { margin: 1.3em 0; } +dd p, td p { margin-bottom: 0; } +dd p:first-child, td p:first-child { margin-top: 0; } + +/* -- Headings -------------------------------------------------------------- */ +h1, h2, h3, h4, h5, h6 { + color: #D98527;/*was #f80*/ + font-family: 'Trebuchet MS', sans-serif; + font-weight: bold; + line-height: 1.1; + margin: 1.1em 0 0.5em; +} + +h1 { + font-size: 184.6%; + color: #30418C; + margin: 0.75em 0 0.5em; +} + +h2 { + font-size: 153.846%; + color: #E48A2B; +} + +h3 { font-size: 138.462%; } + +h4 { + border-bottom: 1px solid #DBDFEA; + color: #E48A2B; + font-size: 115.385%; + font-weight: normal; + padding-bottom: 2px; +} + +h5, h6 { font-size: 107.692%; } + +/* -- Code and examples ----------------------------------------------------- */ +code, kbd, pre, samp { + font-family: Menlo, Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; + font-size: 92.308%; + line-height: 1.35; +} + +p code, p kbd, p samp { + background: #FCFBFA; + border: 1px solid #EFEEED; + padding: 0 3px; +} + +a code, a kbd, a samp, +pre code, pre kbd, pre samp, +table code, table kbd, table samp, +.intro code, .intro kbd, .intro samp, +.toc code, .toc kbd, .toc samp { + background: none; + border: none; + padding: 0; +} + +pre.code, pre.terminal, pre.cmd { + overflow-x: auto; + *overflow-x: scroll; + padding: 0.3em 0.6em; +} + +pre.code { + background: #FCFBFA; + border: 1px solid #EFEEED; + border-left-width: 5px; +} + +pre.terminal, pre.cmd { + background: #F0EFFC; + border: 1px solid #D0CBFB; + border-left: 5px solid #D0CBFB; +} + +/* Don't reduce the font size of // elements inside
+   blocks. */
+pre code, pre kbd, pre samp { font-size: 100%; }
+
+/* Used to denote text that shouldn't be selectable, such as line numbers or
+   shell prompts. Guess which browser this doesn't work in. */
+.noselect {
+    -moz-user-select: -moz-none;
+    -khtml-user-select: none;
+    -webkit-user-select: none;
+    -o-user-select: none;
+    user-select: none;
+}
+
+/* -- Lists ----------------------------------------------------------------- */
+dd { margin: 0.2em 0 0.7em 1em; }
+dl { margin: 1em 0; }
+dt { font-weight: bold; }
+
+/* -- Tables ---------------------------------------------------------------- */
+caption, th { text-align: left; }
+
+table {
+    border-collapse: collapse;
+    width: 100%;
+}
+
+td, th {
+    border: 1px solid #fff;
+    padding: 5px 12px;
+    vertical-align: top;
+}
+
+td { background: #E6E9F5; }
+td dl { margin: 0; }
+td dl dl { margin: 1em 0; }
+td pre:first-child { margin-top: 0; }
+
+th {
+    background: #D2D7E6;/*#97A0BF*/
+    border-bottom: none;
+    border-top: none;
+    color: #000;/*#FFF1D5*/
+    font-family: 'Trebuchet MS', sans-serif;
+    font-weight: bold;
+    line-height: 1.3;
+    white-space: nowrap;
+}
+
+
+/* -- Layout and Content ---------------------------------------------------- */
+#doc {
+    margin: auto;
+    min-width: 1024px;
+}
+
+.content { padding: 0 20px 0 25px; }
+
+.sidebar {
+    padding: 0 15px 0 10px;
+}
+#bd {
+    padding: 7px 0 130px;
+    position: relative;
+    width: 99%;
+}
+
+/* -- Table of Contents ----------------------------------------------------- */
+
+/* The #toc id refers to the single global table of contents, while the .toc
+   class refers to generic TOC lists that could be used throughout the page. */
+
+.toc code, .toc kbd, .toc samp { font-size: 100%; }
+.toc li { font-weight: bold; }
+.toc li li { font-weight: normal; }
+
+/* -- Intro and Example Boxes ----------------------------------------------- */
+/*
+.intro, .example { margin-bottom: 2em; }
+.example {
+    -moz-border-radius: 4px;
+    -webkit-border-radius: 4px;
+    border-radius: 4px;
+    -moz-box-shadow: 0 0 5px #bfbfbf;
+    -webkit-box-shadow: 0 0 5px #bfbfbf;
+    box-shadow: 0 0 5px #bfbfbf;
+    padding: 1em;
+}
+.intro {
+    background: none repeat scroll 0 0 #F0F1F8; border: 1px solid #D4D8EB; padding: 0 1em;
+}
+*/
+
+/* -- Other Styles ---------------------------------------------------------- */
+
+/* These are probably YUI-specific, and should be moved out of Selleck's default
+   theme. */
+
+.button {
+    border: 1px solid #dadada;
+    -moz-border-radius: 3px;
+    -webkit-border-radius: 3px;
+    border-radius: 3px;
+    color: #444;
+    display: inline-block;
+    font-family: Helvetica, Arial, sans-serif;
+    font-size: 92.308%;
+    font-weight: bold;
+    padding: 4px 13px 3px;
+    -moz-text-shadow: 1px 1px 0 #fff;
+    -webkit-text-shadow: 1px 1px 0 #fff;
+    text-shadow: 1px 1px 0 #fff;
+    white-space: nowrap;
+
+    background: #EFEFEF; /* old browsers */
+    background: -moz-linear-gradient(top, #f5f5f5 0%, #efefef 50%, #e5e5e5 51%, #dfdfdf 100%); /* firefox */
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f5f5f5), color-stop(50%,#efefef), color-stop(51%,#e5e5e5), color-stop(100%,#dfdfdf)); /* webkit */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f5f5f5', endColorstr='#dfdfdf',GradientType=0 ); /* ie */
+}
+
+.button:hover {
+    border-color: #466899;
+    color: #fff;
+    text-decoration: none;
+    -moz-text-shadow: 1px 1px 0 #222;
+    -webkit-text-shadow: 1px 1px 0 #222;
+    text-shadow: 1px 1px 0 #222;
+
+    background: #6396D8; /* old browsers */
+    background: -moz-linear-gradient(top, #6396D8 0%, #5A83BC 50%, #547AB7 51%, #466899 100%); /* firefox */
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#6396D8), color-stop(50%,#5A83BC), color-stop(51%,#547AB7), color-stop(100%,#466899)); /* webkit */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#6396D8', endColorstr='#466899',GradientType=0 ); /* ie */
+}
+
+.newwindow { text-align: center; }
+
+.header .version em {
+    display: block;
+    text-align: right;
+}
+
+
+#classdocs .item {
+    border-bottom: 1px solid #466899;
+    margin: 1em 0;
+    padding: 1.5em;
+}
+
+#classdocs .item .params p,
+    #classdocs .item .returns p,{
+    display: inline;
+}
+
+#classdocs .item em code, #classdocs .item em.comment {
+    color: green;
+}
+
+#classdocs .item em.comment a {
+    color: green;
+    text-decoration: underline;
+}
+
+#classdocs .foundat {
+    font-size: 11px;
+    font-style: normal;
+}
+
+.attrs .emits {
+    margin-left: 2em;
+    padding: .5em;
+    border-left: 1px dashed #ccc;
+}
+
+abbr {
+    border-bottom: 1px dashed #ccc;
+    font-size: 80%;
+    cursor: help;
+}
+
+.prettyprint li.L0, 
+.prettyprint li.L1, 
+.prettyprint li.L2, 
+.prettyprint li.L3, 
+.prettyprint li.L5, 
+.prettyprint li.L6, 
+.prettyprint li.L7, 
+.prettyprint li.L8 {
+    list-style: decimal;
+}
+
+ul li p {
+    margin-top: 0;
+}
+
+.method .name {
+    font-size: 110%;
+}
+
+.apidocs .methods .extends .method,
+.apidocs .properties .extends .property,
+.apidocs .attrs .extends .attr,
+.apidocs .events .extends .event {
+    font-weight: bold;
+}
+
+.apidocs .methods .extends .inherited,
+.apidocs .properties .extends .inherited,
+.apidocs .attrs .extends .inherited,
+.apidocs .events .extends .inherited {
+    font-weight: normal;
+}
+
+#hd {
+    background: whiteSmoke;
+    background: -moz-linear-gradient(top,#DCDBD9 0,#F6F5F3 100%);
+    background: -webkit-gradient(linear,left top,left bottom,color-stop(0%,#DCDBD9),color-stop(100%,#F6F5F3));
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#dcdbd9',endColorstr='#F6F5F3',GradientType=0);
+    border-bottom: 1px solid #DFDFDF;
+    padding: 0 15px 1px 20px;
+    margin-bottom: 15px;
+}
+
+#hd img {
+    margin-right: 10px;
+    vertical-align: middle;
+}
+
+
+/* -- API Docs CSS ---------------------------------------------------------- */
+
+/*
+This file is organized so that more generic styles are nearer the top, and more
+specific styles are nearer the bottom of the file. This allows us to take full
+advantage of the cascade to avoid redundant style rules. Please respect this
+convention when making changes.
+*/
+
+/* -- Generic TabView styles ------------------------------------------------ */
+
+/*
+These styles apply to all API doc tabviews. To change styles only for a
+specific tabview, see the other sections below.
+*/
+
+.yui3-js-enabled .apidocs .tabview {
+    visibility: hidden; /* Hide until the TabView finishes rendering. */
+    _visibility: visible;
+}
+
+.apidocs .tabview.yui3-tabview-content { visibility: visible; }
+.apidocs .tabview .yui3-tabview-panel { background: #fff; }
+
+/* -- Generic Content Styles ------------------------------------------------ */
+
+/* Headings */
+h2, h3, h4, h5, h6 {
+    border: none;
+    color: #30418C;
+    font-weight: bold;
+    text-decoration: none;
+}
+
+.link-docs {
+    float: right;
+    font-size: 15px;
+    margin: 4px 4px 6px;
+    padding: 6px 30px 5px;
+}
+
+.apidocs { zoom: 1; }
+
+/* Generic box styles. */
+.apidocs .box {
+    border: 1px solid;
+    border-radius: 3px;
+    margin: 1em 0;
+    padding: 0 1em;
+}
+
+/* A flag is a compact, capsule-like indicator of some kind. It's used to
+   indicate private and protected items, item return types, etc. in an
+   attractive and unobtrusive way. */
+.apidocs .flag {
+    background: #bababa;
+    border-radius: 3px;
+    color: #fff;
+    font-size: 11px;
+    margin: 0 0.5em;
+    padding: 2px 4px 1px;
+}
+
+/* Class/module metadata such as "Uses", "Extends", "Defined in", etc. */
+.apidocs .meta {
+    background: #f9f9f9;
+    border-color: #efefef;
+    color: #555;
+    font-size: 11px;
+    padding: 3px 6px;
+}
+
+.apidocs .meta p { margin: 0; }
+
+/* Deprecation warning. */
+.apidocs .box.deprecated,
+.apidocs .flag.deprecated {
+    background: #fdac9f;
+    border: 1px solid #fd7775;
+}
+
+.apidocs .box.deprecated p { margin: 0.5em 0; }
+.apidocs .flag.deprecated { color: #333; }
+
+/* Module/Class intro description. */
+.apidocs .intro {
+    background: #f0f1f8;
+    border-color: #d4d8eb;
+}
+
+/* Loading spinners. */
+#bd.loading .apidocs,
+#api-list.loading .yui3-tabview-panel {
+    background: #fff url(../img/spinner.gif) no-repeat center 70px;
+    min-height: 150px;
+}
+
+#bd.loading .apidocs .content,
+#api-list.loading .yui3-tabview-panel .apis {
+    display: none;
+}
+
+.apidocs .no-visible-items { color: #666; }
+
+/* Generic inline list. */
+.apidocs ul.inline {
+    display: inline;
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
+.apidocs ul.inline li { display: inline; }
+
+/* Comma-separated list. */
+.apidocs ul.commas li:after { content: ','; }
+.apidocs ul.commas li:last-child:after { content: ''; }
+
+/* Keyboard shortcuts. */
+kbd .cmd { font-family: Monaco, Helvetica; }
+
+/* -- Generic Access Level styles ------------------------------------------- */
+.apidocs .item.protected,
+.apidocs .item.private,
+.apidocs .index-item.protected,
+.apidocs .index-item.deprecated,
+.apidocs .index-item.private {
+    display: none;
+}
+
+.show-deprecated .item.deprecated,
+.show-deprecated .index-item.deprecated,
+.show-protected .item.protected,
+.show-protected .index-item.protected,
+.show-private .item.private,
+.show-private .index-item.private {
+    display: block;
+}
+
+.hide-inherited .item.inherited,
+.hide-inherited .index-item.inherited {
+    display: none;
+}
+
+/* -- Generic Item Index styles --------------------------------------------- */
+.apidocs .index { margin: 1.5em 0 3em; }
+
+.apidocs .index h3 {
+    border-bottom: 1px solid #efefef;
+    color: #333;
+    font-size: 13px;
+    margin: 2em 0 0.6em;
+    padding-bottom: 2px;
+}
+
+.apidocs .index .no-visible-items { margin-top: 2em; }
+
+.apidocs .index-list {
+    border-color: #efefef;
+    font-size: 12px;
+    list-style: none;
+    margin: 0;
+    padding: 0;
+    -moz-column-count: 4;
+    -moz-column-gap: 10px;
+    -moz-column-width: 170px;
+    -ms-column-count: 4;
+    -ms-column-gap: 10px;
+    -ms-column-width: 170px;
+    -o-column-count: 4;
+    -o-column-gap: 10px;
+    -o-column-width: 170px;
+    -webkit-column-count: 4;
+    -webkit-column-gap: 10px;
+    -webkit-column-width: 170px;
+    column-count: 4;
+    column-gap: 10px;
+    column-width: 170px;
+}
+
+.apidocs .no-columns .index-list {
+    -moz-column-count: 1;
+    -ms-column-count: 1;
+    -o-column-count: 1;
+    -webkit-column-count: 1;
+    column-count: 1;
+}
+
+.apidocs .index-item { white-space: nowrap; }
+
+.apidocs .index-item .flag {
+    background: none;
+    border: none;
+    color: #afafaf;
+    display: inline;
+    margin: 0 0 0 0.2em;
+    padding: 0;
+}
+
+/* -- Generic API item styles ----------------------------------------------- */
+.apidocs .args {
+    display: inline;
+    margin: 0 0.5em;
+}
+
+.apidocs .flag.chainable { background: #46ca3b; }
+.apidocs .flag.protected { background: #9b86fc; }
+.apidocs .flag.private { background: #fd6b1b; }
+.apidocs .flag.async { background: #356de4; }
+.apidocs .flag.required { background: #e60923; }
+
+.apidocs .item {
+    border-bottom: 1px solid #efefef;
+    margin: 1.5em 0 2em;
+    padding-bottom: 2em;
+}
+
+.apidocs .item h4,
+.apidocs .item h5,
+.apidocs .item h6 {
+    color: #333;
+    font-family: inherit;
+    font-size: 100%;
+}
+
+.apidocs .item .description p,
+.apidocs .item pre.code {
+    margin: 1em 0 0;
+}
+
+.apidocs .item .meta {
+    background: none;
+    border: none;
+    padding: 0;
+}
+
+.apidocs .item .name {
+    display: inline;
+    font-size: 14px;
+}
+
+.apidocs .item .type,
+.apidocs .item .type a,
+.apidocs .returns-inline {
+    color: #555;
+}
+
+.apidocs .item .type,
+.apidocs .returns-inline {
+    font-size: 11px;
+    margin: 0 0 0 0;
+}
+
+.apidocs .item .type a { border-bottom: 1px dotted #afafaf; }
+.apidocs .item .type a:hover { border: none; }
+
+/* -- Item Parameter List --------------------------------------------------- */
+.apidocs .params-list {
+    list-style: square;
+    margin: 1em 0 0 2em;
+    padding: 0;
+}
+
+.apidocs .param { margin-bottom: 1em; }
+
+.apidocs .param .type,
+.apidocs .param .type a {
+    color: #666;
+}
+
+.apidocs .param .type {
+    margin: 0 0 0 0.5em;
+    *margin-left: 0.5em;
+}
+
+.apidocs .param-name { font-weight: bold; }
+
+/* -- Item "Emits" block ---------------------------------------------------- */
+.apidocs .item .emits {
+    background: #f9f9f9;
+    border-color: #eaeaea;
+}
+
+/* -- Item "Returns" block -------------------------------------------------- */
+.apidocs .item .returns .type,
+.apidocs .item .returns .type a {
+    font-size: 100%;
+    margin: 0;
+}
+
+/* -- Class Constructor block ----------------------------------------------- */
+.apidocs .constructor .item {
+    border: none;
+    padding-bottom: 0;
+}
+
+/* -- File Source View ------------------------------------------------------ */
+.apidocs .file pre.code,
+#doc .apidocs .file pre.prettyprint {
+    background: inherit;
+    border: none;
+    overflow: visible;
+    padding: 0;
+}
+
+.apidocs .L0,
+.apidocs .L1,
+.apidocs .L2,
+.apidocs .L3,
+.apidocs .L4,
+.apidocs .L5,
+.apidocs .L6,
+.apidocs .L7,
+.apidocs .L8,
+.apidocs .L9 {
+    background: inherit;
+}
+
+/* -- Submodule List -------------------------------------------------------- */
+.apidocs .module-submodule-description {
+    font-size: 12px;
+    margin: 0.3em 0 1em;
+}
+
+.apidocs .module-submodule-description p:first-child { margin-top: 0; }
+
+/* -- Sidebar TabView ------------------------------------------------------- */
+#api-tabview { margin-top: 0.6em; }
+
+#api-tabview-filter,
+#api-tabview-panel {
+    border: 1px solid #dfdfdf;
+}
+
+#api-tabview-filter {
+    border-bottom: none;
+    border-top: none;
+    padding: 0.6em 10px 0 10px;
+}
+
+#api-tabview-panel { border-top: none; }
+#api-filter { width: 97%; }
+
+/* -- Content TabView ------------------------------------------------------- */
+#classdocs .yui3-tabview-panel { border: none; }
+
+/* -- Source File Contents -------------------------------------------------- */
+.prettyprint li.L0,
+.prettyprint li.L1,
+.prettyprint li.L2,
+.prettyprint li.L3,
+.prettyprint li.L5,
+.prettyprint li.L6,
+.prettyprint li.L7,
+.prettyprint li.L8 {
+    list-style: decimal;
+}
+
+/* -- API options ----------------------------------------------------------- */
+#api-options {
+    font-size: 11px;
+    margin-top: 2.2em;
+    position: absolute;
+    right: 1.5em;
+}
+
+/*#api-options label { margin-right: 0.6em; }*/
+
+/* -- API list -------------------------------------------------------------- */
+#api-list {
+    margin-top: 1.5em;
+    *zoom: 1;
+}
+
+.apis {
+    font-size: 12px;
+    line-height: 1.4;
+    list-style: none;
+    margin: 0;
+    padding: 0.5em 0 0.5em 0.4em;
+}
+
+.apis a {
+    border: 1px solid transparent;
+    display: block;
+    margin: 0 0 0 -4px;
+    padding: 1px 4px 0;
+    text-decoration: none;
+    _border: none;
+    _display: inline;
+}
+
+.apis a:hover,
+.apis a:focus {
+    background: #E8EDFC;
+    background: -moz-linear-gradient(top, #e8edfc 0%, #becef7 100%);
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#E8EDFC), color-stop(100%,#BECEF7));
+    border-color: #AAC0FA;
+    border-radius: 3px;
+    color: #333;
+    outline: none;
+}
+
+.api-list-item a:hover,
+.api-list-item a:focus {
+    font-weight: bold;
+    text-shadow: 1px 1px 1px #fff;
+}
+
+.apis .message { color: #888; }
+.apis .result a { padding: 3px 5px 2px; }
+
+.apis .result .type {
+    right: 4px;
+    top: 7px;
+}
+
+.api-list-item .yui3-highlight {
+    font-weight: bold;
+}
+
diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png
new file mode 100644
index 000000000000..5a95ddab6ff6
Binary files /dev/null and b/docs/assets/favicon.png differ
diff --git a/docs/assets/img/spinner.gif b/docs/assets/img/spinner.gif
new file mode 100644
index 000000000000..44f96ba68c89
Binary files /dev/null and b/docs/assets/img/spinner.gif differ
diff --git a/docs/assets/index.html b/docs/assets/index.html
new file mode 100644
index 000000000000..487fe15b2ad0
--- /dev/null
+++ b/docs/assets/index.html
@@ -0,0 +1,10 @@
+
+
+    
+        Redirector
+        
+    
+    
+        Click here to redirect
+    
+
diff --git a/docs/assets/js/api-filter.js b/docs/assets/js/api-filter.js
new file mode 100644
index 000000000000..37aefbab980b
--- /dev/null
+++ b/docs/assets/js/api-filter.js
@@ -0,0 +1,52 @@
+YUI.add('api-filter', function (Y) {
+
+Y.APIFilter = Y.Base.create('apiFilter', Y.Base, [Y.AutoCompleteBase], {
+    // -- Initializer ----------------------------------------------------------
+    initializer: function () {
+        this._bindUIACBase();
+        this._syncUIACBase();
+    },
+    getDisplayName: function(name) {
+
+        Y.each(Y.YUIDoc.meta.allModules, function(i) {
+            if (i.name === name && i.displayName) {
+                name = i.displayName;
+            }
+        });
+
+        return name;
+    }
+
+}, {
+    // -- Attributes -----------------------------------------------------------
+    ATTRS: {
+        resultHighlighter: {
+            value: 'phraseMatch'
+        },
+
+        // May be set to "classes" or "modules".
+        queryType: {
+            value: 'classes'
+        },
+
+        source: {
+            valueFn: function() {
+                var self = this;
+                return function(q) {
+                    var data = Y.YUIDoc.meta[self.get('queryType')],
+                        out = [];
+                    Y.each(data, function(v) {
+                        if (v.toLowerCase().indexOf(q.toLowerCase()) > -1) {
+                            out.push(v);
+                        }
+                    });
+                    return out;
+                };
+            }
+        }
+    }
+});
+
+}, '3.4.0', {requires: [
+    'autocomplete-base', 'autocomplete-highlighters', 'autocomplete-sources'
+]});
diff --git a/docs/assets/js/api-list.js b/docs/assets/js/api-list.js
new file mode 100644
index 000000000000..88905b52ea3d
--- /dev/null
+++ b/docs/assets/js/api-list.js
@@ -0,0 +1,251 @@
+YUI.add('api-list', function (Y) {
+
+var Lang   = Y.Lang,
+    YArray = Y.Array,
+
+    APIList = Y.namespace('APIList'),
+
+    classesNode    = Y.one('#api-classes'),
+    inputNode      = Y.one('#api-filter'),
+    modulesNode    = Y.one('#api-modules'),
+    tabviewNode    = Y.one('#api-tabview'),
+
+    tabs = APIList.tabs = {},
+
+    filter = APIList.filter = new Y.APIFilter({
+        inputNode : inputNode,
+        maxResults: 1000,
+
+        on: {
+            results: onFilterResults
+        }
+    }),
+
+    search = APIList.search = new Y.APISearch({
+        inputNode : inputNode,
+        maxResults: 100,
+
+        on: {
+            clear  : onSearchClear,
+            results: onSearchResults
+        }
+    }),
+
+    tabview = APIList.tabview = new Y.TabView({
+        srcNode  : tabviewNode,
+        panelNode: '#api-tabview-panel',
+        render   : true,
+
+        on: {
+            selectionChange: onTabSelectionChange
+        }
+    }),
+
+    focusManager = APIList.focusManager = tabviewNode.plug(Y.Plugin.NodeFocusManager, {
+        circular   : true,
+        descendants: '#api-filter, .yui3-tab-panel-selected .api-list-item a, .yui3-tab-panel-selected .result a',
+        keys       : {next: 'down:40', previous: 'down:38'}
+    }).focusManager,
+
+    LIST_ITEM_TEMPLATE =
+        '
  • ' + + '{displayName}' + + '
  • '; + +// -- Init --------------------------------------------------------------------- + +// Duckpunch FocusManager's key event handling to prevent it from handling key +// events when a modifier is pressed. +Y.before(function (e, activeDescendant) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { + return new Y.Do.Prevent(); + } +}, focusManager, '_focusPrevious', focusManager); + +Y.before(function (e, activeDescendant) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { + return new Y.Do.Prevent(); + } +}, focusManager, '_focusNext', focusManager); + +// Create a mapping of tabs in the tabview so we can refer to them easily later. +tabview.each(function (tab, index) { + var name = tab.get('label').toLowerCase(); + + tabs[name] = { + index: index, + name : name, + tab : tab + }; +}); + +// Switch tabs on Ctrl/Cmd-Left/Right arrows. +tabviewNode.on('key', onTabSwitchKey, 'down:37,39'); + +// Focus the filter input when the `/` key is pressed. +Y.one(Y.config.doc).on('key', onSearchKey, 'down:83'); + +// Keep the Focus Manager up to date. +inputNode.on('focus', function () { + focusManager.set('activeDescendant', inputNode); +}); + +// Update all tabview links to resolved URLs. +tabview.get('panelNode').all('a').each(function (link) { + link.setAttribute('href', link.get('href')); +}); + +// -- Private Functions -------------------------------------------------------- +function getFilterResultNode() { + return filter.get('queryType') === 'classes' ? classesNode : modulesNode; +} + +// -- Event Handlers ----------------------------------------------------------- +function onFilterResults(e) { + var frag = Y.one(Y.config.doc.createDocumentFragment()), + resultNode = getFilterResultNode(), + typePlural = filter.get('queryType'), + typeSingular = typePlural === 'classes' ? 'class' : 'module'; + + if (e.results.length) { + YArray.each(e.results, function (result) { + frag.append(Lang.sub(LIST_ITEM_TEMPLATE, { + rootPath : APIList.rootPath, + displayName : filter.getDisplayName(result.highlighted), + name : result.text, + typePlural : typePlural, + typeSingular: typeSingular + })); + }); + } else { + frag.append( + '
  • ' + + 'No ' + typePlural + ' found.' + + '
  • ' + ); + } + + resultNode.empty(true); + resultNode.append(frag); + + focusManager.refresh(); +} + +function onSearchClear(e) { + + focusManager.refresh(); +} + +function onSearchKey(e) { + var target = e.target; + + if (target.test('input,select,textarea') + || target.get('isContentEditable')) { + return; + } + + e.preventDefault(); + + inputNode.focus(); + focusManager.refresh(); +} + +function onSearchResults(e) { + var frag = Y.one(Y.config.doc.createDocumentFragment()); + + if (e.results.length) { + YArray.each(e.results, function (result) { + frag.append(result.display); + }); + } else { + frag.append( + '
  • ' + + 'No results found. Maybe you\'ll have better luck with a ' + + 'different query?' + + '
  • ' + ); + } + + + focusManager.refresh(); +} + +function onTabSelectionChange(e) { + var tab = e.newVal, + name = tab.get('label').toLowerCase(); + + tabs.selected = { + index: tab.get('index'), + name : name, + tab : tab + }; + + switch (name) { + case 'classes': // fallthru + case 'modules': + filter.setAttrs({ + minQueryLength: 0, + queryType : name + }); + + search.set('minQueryLength', -1); + + // Only send a request if this isn't the initially-selected tab. + if (e.prevVal) { + filter.sendRequest(filter.get('value')); + } + break; + + case 'everything': + filter.set('minQueryLength', -1); + search.set('minQueryLength', 1); + + if (search.get('value')) { + search.sendRequest(search.get('value')); + } else { + inputNode.focus(); + } + break; + + default: + // WTF? We shouldn't be here! + filter.set('minQueryLength', -1); + search.set('minQueryLength', -1); + } + + if (focusManager) { + setTimeout(function () { + focusManager.refresh(); + }, 1); + } +} + +function onTabSwitchKey(e) { + var currentTabIndex = tabs.selected.index; + + if (!(e.ctrlKey || e.metaKey)) { + return; + } + + e.preventDefault(); + + switch (e.keyCode) { + case 37: // left arrow + if (currentTabIndex > 0) { + tabview.selectChild(currentTabIndex - 1); + inputNode.focus(); + } + break; + + case 39: // right arrow + if (currentTabIndex < (Y.Object.size(tabs) - 2)) { + tabview.selectChild(currentTabIndex + 1); + inputNode.focus(); + } + break; + } +} + +}, '3.4.0', {requires: [ + 'api-filter', 'api-search', 'event-key', 'node-focusmanager', 'tabview' +]}); diff --git a/docs/assets/js/api-search.js b/docs/assets/js/api-search.js new file mode 100644 index 000000000000..175f6a61791f --- /dev/null +++ b/docs/assets/js/api-search.js @@ -0,0 +1,98 @@ +YUI.add('api-search', function (Y) { + +var Lang = Y.Lang, + Node = Y.Node, + YArray = Y.Array; + +Y.APISearch = Y.Base.create('apiSearch', Y.Base, [Y.AutoCompleteBase], { + // -- Public Properties ---------------------------------------------------- + RESULT_TEMPLATE: + '
  • ' + + '' + + '

    {name}

    ' + + '{resultType}' + + '
    {description}
    ' + + '{class}' + + '
    ' + + '
  • ', + + // -- Initializer ---------------------------------------------------------- + initializer: function () { + this._bindUIACBase(); + this._syncUIACBase(); + }, + + // -- Protected Methods ---------------------------------------------------- + _apiResultFilter: function (query, results) { + // Filter components out of the results. + return YArray.filter(results, function (result) { + return result.raw.resultType === 'component' ? false : result; + }); + }, + + _apiResultFormatter: function (query, results) { + return YArray.map(results, function (result) { + var raw = Y.merge(result.raw), // create a copy + desc = raw.description || ''; + + // Convert description to text and truncate it if necessary. + desc = Node.create('
    ' + desc + '
    ').get('text'); + + if (desc.length > 65) { + desc = Y.Escape.html(desc.substr(0, 65)) + ' …'; + } else { + desc = Y.Escape.html(desc); + } + + raw['class'] || (raw['class'] = ''); + raw.description = desc; + + // Use the highlighted result name. + raw.name = result.highlighted; + + return Lang.sub(this.RESULT_TEMPLATE, raw); + }, this); + }, + + _apiTextLocator: function (result) { + return result.displayName || result.name; + } +}, { + // -- Attributes ----------------------------------------------------------- + ATTRS: { + resultFormatter: { + valueFn: function () { + return this._apiResultFormatter; + } + }, + + resultFilters: { + valueFn: function () { + return this._apiResultFilter; + } + }, + + resultHighlighter: { + value: 'phraseMatch' + }, + + resultListLocator: { + value: 'data.results' + }, + + resultTextLocator: { + valueFn: function () { + return this._apiTextLocator; + } + }, + + source: { + value: '/api/v1/search?q={query}&count={maxResults}' + } + } +}); + +}, '3.4.0', {requires: [ + 'autocomplete-base', 'autocomplete-highlighters', 'autocomplete-sources', + 'escape' +]}); diff --git a/docs/assets/js/apidocs.js b/docs/assets/js/apidocs.js new file mode 100644 index 000000000000..c64bb46326fb --- /dev/null +++ b/docs/assets/js/apidocs.js @@ -0,0 +1,370 @@ +YUI().use( + 'yuidoc-meta', + 'api-list', 'history-hash', 'node-screen', 'node-style', 'pjax', +function (Y) { + +var win = Y.config.win, + localStorage = win.localStorage, + + bdNode = Y.one('#bd'), + + pjax, + defaultRoute, + + classTabView, + selectedTab; + +// Kill pjax functionality unless serving over HTTP. +if (!Y.getLocation().protocol.match(/^https?\:/)) { + Y.Router.html5 = false; +} + +// Create the default route with middleware which enables syntax highlighting +// on the loaded content. +defaultRoute = Y.Pjax.defaultRoute.concat(function (req, res, next) { + prettyPrint(); + bdNode.removeClass('loading'); + + next(); +}); + +pjax = new Y.Pjax({ + container : '#docs-main', + contentSelector: '#docs-main > .content', + linkSelector : '#bd a', + titleSelector : '#xhr-title', + + navigateOnHash: true, + root : '/', + routes : [ + // -- / ---------------------------------------------------------------- + { + path : '/(index.html)?', + callbacks: defaultRoute + }, + + // -- /classes/* ------------------------------------------------------- + { + path : '/classes/:class.html*', + callbacks: [defaultRoute, 'handleClasses'] + }, + + // -- /files/* --------------------------------------------------------- + { + path : '/files/*file', + callbacks: [defaultRoute, 'handleFiles'] + }, + + // -- /modules/* ------------------------------------------------------- + { + path : '/modules/:module.html*', + callbacks: defaultRoute + } + ] +}); + +// -- Utility Functions -------------------------------------------------------- + +pjax.checkVisibility = function (tab) { + tab || (tab = selectedTab); + + if (!tab) { return; } + + var panelNode = tab.get('panelNode'), + visibleItems; + + // If no items are visible in the tab panel due to the current visibility + // settings, display a message to that effect. + visibleItems = panelNode.all('.item,.index-item').some(function (itemNode) { + if (itemNode.getComputedStyle('display') !== 'none') { + return true; + } + }); + + panelNode.all('.no-visible-items').remove(); + + if (!visibleItems) { + if (Y.one('#index .index-item')) { + panelNode.append( + '
    ' + + '

    ' + + 'Some items are not shown due to the current visibility ' + + 'settings. Use the checkboxes at the upper right of this ' + + 'page to change the visibility settings.' + + '

    ' + + '
    ' + ); + } else { + panelNode.append( + '
    ' + + '

    ' + + 'This class doesn\'t provide any methods, properties, ' + + 'attributes, or events.' + + '

    ' + + '
    ' + ); + } + } + + // Hide index sections without any visible items. + Y.all('.index-section').each(function (section) { + var items = 0, + visibleItems = 0; + + section.all('.index-item').each(function (itemNode) { + items += 1; + + if (itemNode.getComputedStyle('display') !== 'none') { + visibleItems += 1; + } + }); + + section.toggleClass('hidden', !visibleItems); + section.toggleClass('no-columns', visibleItems < 4); + }); +}; + +pjax.initClassTabView = function () { + if (!Y.all('#classdocs .api-class-tab').size()) { + return; + } + + if (classTabView) { + classTabView.destroy(); + selectedTab = null; + } + + classTabView = new Y.TabView({ + srcNode: '#classdocs', + + on: { + selectionChange: pjax.onTabSelectionChange + } + }); + + pjax.updateTabState(); + classTabView.render(); +}; + +pjax.initLineNumbers = function () { + var hash = win.location.hash.substring(1), + container = pjax.get('container'), + hasLines, node; + + // Add ids for each line number in the file source view. + container.all('.linenums>li').each(function (lineNode, index) { + lineNode.set('id', 'l' + (index + 1)); + lineNode.addClass('file-line'); + hasLines = true; + }); + + // Scroll to the desired line. + if (hasLines && /^l\d+$/.test(hash)) { + if ((node = container.getById(hash))) { + win.scroll(0, node.getY()); + } + } +}; + +pjax.initRoot = function () { + var terminators = /^(?:classes|files|modules)$/, + parts = pjax._getPathRoot().split('/'), + root = [], + i, len, part; + + for (i = 0, len = parts.length; i < len; i += 1) { + part = parts[i]; + + if (part.match(terminators)) { + // Makes sure the path will end with a "/". + root.push(''); + break; + } + + root.push(part); + } + + pjax.set('root', root.join('/')); +}; + +pjax.updateTabState = function (src) { + var hash = win.location.hash.substring(1), + defaultTab, node, tab, tabPanel; + + function scrollToNode() { + if (node.hasClass('protected')) { + Y.one('#api-show-protected').set('checked', true); + pjax.updateVisibility(); + } + + if (node.hasClass('private')) { + Y.one('#api-show-private').set('checked', true); + pjax.updateVisibility(); + } + + setTimeout(function () { + // For some reason, unless we re-get the node instance here, + // getY() always returns 0. + var node = Y.one('#classdocs').getById(hash); + win.scrollTo(0, node.getY() - 70); + }, 1); + } + + if (!classTabView) { + return; + } + + if (src === 'hashchange' && !hash) { + defaultTab = 'index'; + } else { + if (localStorage) { + defaultTab = localStorage.getItem('tab_' + pjax.getPath()) || + 'index'; + } else { + defaultTab = 'index'; + } + } + + if (hash && (node = Y.one('#classdocs').getById(hash))) { + if ((tabPanel = node.ancestor('.api-class-tabpanel', true))) { + if ((tab = Y.one('#classdocs .api-class-tab.' + tabPanel.get('id')))) { + if (classTabView.get('rendered')) { + Y.Widget.getByNode(tab).set('selected', 1); + } else { + tab.addClass('yui3-tab-selected'); + } + } + } + + // Scroll to the desired element if this is a hash URL. + if (node) { + if (classTabView.get('rendered')) { + scrollToNode(); + } else { + classTabView.once('renderedChange', scrollToNode); + } + } + } else { + tab = Y.one('#classdocs .api-class-tab.' + defaultTab); + + // When the `defaultTab` node isn't found, `localStorage` is stale. + if (!tab && defaultTab !== 'index') { + tab = Y.one('#classdocs .api-class-tab.index'); + } + + if (classTabView.get('rendered')) { + Y.Widget.getByNode(tab).set('selected', 1); + } else { + tab.addClass('yui3-tab-selected'); + } + } +}; + +pjax.updateVisibility = function () { + var container = pjax.get('container'); + + container.toggleClass('hide-inherited', + !Y.one('#api-show-inherited').get('checked')); + + container.toggleClass('show-deprecated', + Y.one('#api-show-deprecated').get('checked')); + + container.toggleClass('show-protected', + Y.one('#api-show-protected').get('checked')); + + container.toggleClass('show-private', + Y.one('#api-show-private').get('checked')); + + pjax.checkVisibility(); +}; + +// -- Route Handlers ----------------------------------------------------------- + +pjax.handleClasses = function (req, res, next) { + var status = res.ioResponse.status; + + // Handles success and local filesystem XHRs. + if (!status || (status >= 200 && status < 300)) { + pjax.initClassTabView(); + } + + next(); +}; + +pjax.handleFiles = function (req, res, next) { + var status = res.ioResponse.status; + + // Handles success and local filesystem XHRs. + if (!status || (status >= 200 && status < 300)) { + pjax.initLineNumbers(); + } + + next(); +}; + +// -- Event Handlers ----------------------------------------------------------- + +pjax.onNavigate = function (e) { + var hash = e.hash, + originTarget = e.originEvent && e.originEvent.target, + tab; + + if (hash) { + tab = originTarget && originTarget.ancestor('.yui3-tab', true); + + if (hash === win.location.hash) { + pjax.updateTabState('hashchange'); + } else if (!tab) { + win.location.hash = hash; + } + + e.preventDefault(); + return; + } + + // Only scroll to the top of the page when the URL doesn't have a hash. + this.set('scrollToTop', !e.url.match(/#.+$/)); + + bdNode.addClass('loading'); +}; + +pjax.onOptionClick = function (e) { + pjax.updateVisibility(); +}; + +pjax.onTabSelectionChange = function (e) { + var tab = e.newVal, + tabId = tab.get('contentBox').getAttribute('href').substring(1); + + selectedTab = tab; + + // If switching from a previous tab (i.e., this is not the default tab), + // replace the history entry with a hash URL that will cause this tab to + // be selected if the user navigates away and then returns using the back + // or forward buttons. + if (e.prevVal && localStorage) { + localStorage.setItem('tab_' + pjax.getPath(), tabId); + } + + pjax.checkVisibility(tab); +}; + +// -- Init --------------------------------------------------------------------- + +pjax.on('navigate', pjax.onNavigate); + +pjax.initRoot(); +pjax.upgrade(); +pjax.initClassTabView(); +pjax.initLineNumbers(); +pjax.updateVisibility(); + +Y.APIList.rootPath = pjax.get('root'); + +Y.one('#api-options').delegate('click', pjax.onOptionClick, 'input'); + +Y.on('hashchange', function (e) { + pjax.updateTabState('hashchange'); +}, win); + +}); diff --git a/docs/assets/js/yui-prettify.js b/docs/assets/js/yui-prettify.js new file mode 100644 index 000000000000..18de8649532c --- /dev/null +++ b/docs/assets/js/yui-prettify.js @@ -0,0 +1,17 @@ +YUI().use('node', function(Y) { + var code = Y.all('.prettyprint.linenums'); + if (code.size()) { + code.each(function(c) { + var lis = c.all('ol li'), + l = 1; + lis.each(function(n) { + n.prepend(''); + l++; + }); + }); + var h = location.hash; + location.hash = ''; + h = h.replace('LINE_', 'LINENUM_'); + location.hash = h; + } +}); diff --git a/docs/assets/vendor/prettify/CHANGES.html b/docs/assets/vendor/prettify/CHANGES.html new file mode 100644 index 000000000000..b50b841499e7 --- /dev/null +++ b/docs/assets/vendor/prettify/CHANGES.html @@ -0,0 +1,130 @@ + + + + Change Log + + + README + +

    Known Issues

    +
      +
    • Perl formatting is really crappy. Partly because the author is lazy and + partly because Perl is + hard to parse. +
    • On some browsers, <code> elements with newlines in the text + which use CSS to specify white-space:pre will have the newlines + improperly stripped if the element is not attached to the document at the time + the stripping is done. Also, on IE 6, all newlines will be stripped from + <code> elements because of the way IE6 produces + innerHTML. Workaround: use <pre> for code with + newlines. +
    + +

    Change Log

    +

    29 March 2007

    +
      +
    • Added tests for PHP support + to address + issue 3. +
    • Fixed + bug: prettyPrintOne was not halting. This was not + reachable through the normal entry point. +
    • Fixed + bug: recursing into a script block or PHP tag that was not properly + closed would not silently drop the content. + (test) +
    • Fixed + bug: was eating tabs + (test) +
    • Fixed entity handling so that the caveat +
      +

      Caveats: please properly escape less-thans. x&lt;y + instead of x<y, and use " instead of + &quot; for string delimiters.

      +
      + is no longer applicable. +
    • Added noisefree's C# + patch +
    • Added a distribution that has comments and + whitespace removed to reduce download size from 45.5kB to 12.8kB. +
    +

    4 Jul 2008

    +
      +
    • Added language specific formatters that are triggered by the presence + of a lang-<language-file-extension>
    • +
    • Fixed bug: python handling of '''string''' +
    • Fixed bug: / in regex [charsets] should not end regex +
    +

    5 Jul 2008

    +
      +
    • Defined language extensions for Lisp and Lua +
    +

    14 Jul 2008

    +
      +
    • Language handlers for F#, OCAML, SQL +
    • Support for nocode spans to allow embedding of line + numbers and code annotations which should not be styled or otherwise + affect the tokenization of prettified code. + See the issue 22 + testcase. +
    +

    6 Jan 2009

    +
      +
    • Language handlers for Visual Basic, Haskell, CSS, and WikiText
    • +
    • Added .mxml extension to the markup style handler for + Flex MXML files. See + issue 37. +
    • Added .m extension to the C style handler so that Objective + C source files properly highlight. See + issue 58. +
    • Changed HTML lexer to use the same embedded source mechanism as the + wiki language handler, and changed to use the registered + CSS handler for STYLE element content. +
    +

    21 May 2009

    +
      +
    • Rewrote to improve performance on large files. + See benchmarks.
    • +
    • Fixed bugs with highlighting of Haskell line comments, Lisp + number literals, Lua strings, C preprocessor directives, + newlines in Wiki code on Windows, and newlines in IE6.
    • +
    +

    14 August 2009

    +
      +
    • Fixed prettifying of <code> blocks with embedded newlines. +
    +

    3 October 2009

    +
      +
    • Fixed prettifying of XML/HTML tags that contain uppercase letters. +
    +

    19 July 2010

    +
      +
    • Added support for line numbers. Bug + 22
    • +
    • Added YAML support. Bug + 123
    • +
    • Added VHDL support courtesy Le Poussin.
    • +
    • IE performance improvements. Bug + 102 courtesy jacobly.
    • +
    • A variety of markup formatting fixes courtesy smain and thezbyg.
    • +
    • Fixed copy and paste in IE[678]. +
    • Changed output to use &#160; instead of + &nbsp; so that the output works when embedded in XML. + Bug + 108.
    • +
    + + diff --git a/docs/assets/vendor/prettify/COPYING b/docs/assets/vendor/prettify/COPYING new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/docs/assets/vendor/prettify/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/assets/vendor/prettify/README.html b/docs/assets/vendor/prettify/README.html new file mode 100644 index 000000000000..c6fe1a32c38f --- /dev/null +++ b/docs/assets/vendor/prettify/README.html @@ -0,0 +1,203 @@ + + + + + Javascript code prettifier + + + + + + + + + + Languages : CH +

    Javascript code prettifier

    + +

    Setup

    +
      +
    1. Download a distribution +
    2. Include the script and stylesheets in your document + (you will need to make sure the css and js file are on your server, and + adjust the paths in the script and link tag) +
      +<link href="prettify.css" type="text/css" rel="stylesheet" />
      +<script type="text/javascript" src="prettify.js"></script>
      +
    3. Add onload="prettyPrint()" to your + document's body tag. +
    4. Modify the stylesheet to get the coloring you prefer
    5. +
    + +

    Usage

    +

    Put code snippets in + <pre class="prettyprint">...</pre> + or <code class="prettyprint">...</code> + and it will automatically be pretty printed. + + + + +
    The original + Prettier +
    class Voila {
    +public:
    +  // Voila
    +  static const string VOILA = "Voila";
    +
    +  // will not interfere with embedded tags.
    +}
    + +
    class Voila {
    +public:
    +  // Voila
    +  static const string VOILA = "Voila";
    +
    +  // will not interfere with embedded tags.
    +}
    +
    + +

    FAQ

    +

    Which languages does it work for?

    +

    The comments in prettify.js are authoritative but the lexer + should work on a number of languages including C and friends, + Java, Python, Bash, SQL, HTML, XML, CSS, Javascript, and Makefiles. + It works passably on Ruby, PHP, VB, and Awk and a decent subset of Perl + and Ruby, but, because of commenting conventions, doesn't work on + Smalltalk, or CAML-like languages.

    + +

    LISPy languages are supported via an extension: + lang-lisp.js.

    +

    And similarly for + CSS, + Haskell, + Lua, + OCAML, SML, F#, + Visual Basic, + SQL, + Protocol Buffers, and + WikiText.. + +

    If you'd like to add an extension for your favorite language, please + look at src/lang-lisp.js and file an + issue including your language extension, and a testcase.

    + +

    How do I specify which language my code is in?

    +

    You don't need to specify the language since prettyprint() + will guess. You can specify a language by specifying the language extension + along with the prettyprint class like so:

    +
    <pre class="prettyprint lang-html">
    +  The lang-* class specifies the language file extensions.
    +  File extensions supported by default include
    +    "bsh", "c", "cc", "cpp", "cs", "csh", "cyc", "cv", "htm", "html",
    +    "java", "js", "m", "mxml", "perl", "pl", "pm", "py", "rb", "sh",
    +    "xhtml", "xml", "xsl".
    +</pre>
    + +

    It doesn't work on <obfuscated code sample>?

    +

    Yes. Prettifying obfuscated code is like putting lipstick on a pig + — i.e. outside the scope of this tool.

    + +

    Which browsers does it work with?

    +

    It's been tested with IE 6, Firefox 1.5 & 2, and Safari 2.0.4. + Look at the test page to see if it + works in your browser.

    + +

    What's changed?

    +

    See the change log

    + +

    Why doesn't Prettyprinting of strings work on WordPress?

    +

    Apparently wordpress does "smart quoting" which changes close quotes. + This causes end quotes to not match up with open quotes. +

    This breaks prettifying as well as copying and pasting of code samples. + See + WordPress's help center for info on how to stop smart quoting of code + snippets.

    + +

    How do I put line numbers in my code?

    +

    You can use the linenums class to turn on line + numbering. If your code doesn't start at line number 1, you can + add a colon and a line number to the end of that class as in + linenums:52. + +

    For example +

    <pre class="prettyprint linenums:4"
    +>// This is line 4.
    +foo();
    +bar();
    +baz();
    +boo();
    +far();
    +faz();
    +<pre>
    + produces +
    // This is line 4.
    +foo();
    +bar();
    +baz();
    +boo();
    +far();
    +faz();
    +
    + +

    How do I prevent a portion of markup from being marked as code?

    +

    You can use the nocode class to identify a span of markup + that is not code. +

    <pre class=prettyprint>
    +int x = foo();  /* This is a comment  <span class="nocode">This is not code</span>
    +  Continuation of comment */
    +int y = bar();
    +</pre>
    +produces +
    +int x = foo();  /* This is a comment  This is not code
    +  Continuation of comment */
    +int y = bar();
    +
    + +

    For a more complete example see the issue22 + testcase.

    + +

    I get an error message "a is not a function" or "opt_whenDone is not a function"

    +

    If you are calling prettyPrint via an event handler, wrap it in a function. + Instead of doing +

    + addEventListener('load', prettyPrint, false); +
    + wrap it in a closure like +
    + addEventListener('load', function (event) { prettyPrint() }, false); +
    + so that the browser does not pass an event object to prettyPrint which + will confuse it. + +


    + + + + diff --git a/docs/assets/vendor/prettify/prettify-min.css b/docs/assets/vendor/prettify/prettify-min.css new file mode 100644 index 000000000000..d44b3a2282ad --- /dev/null +++ b/docs/assets/vendor/prettify/prettify-min.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} \ No newline at end of file diff --git a/docs/assets/vendor/prettify/prettify-min.js b/docs/assets/vendor/prettify/prettify-min.js new file mode 100644 index 000000000000..4845d05d4b5e --- /dev/null +++ b/docs/assets/vendor/prettify/prettify-min.js @@ -0,0 +1 @@ +window.PR_SHOULD_USE_CONTINUATION=true;var prettyPrintOne;var prettyPrint;(function(){var O=window;var j=["break,continue,do,else,for,if,return,while"];var v=[j,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var q=[v,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var m=[q,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var y=[q,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var T=[y,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"];var s="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes";var x=[q,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var t="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var J=[j,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var g=[j,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var I=[j,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var B=[m,T,x,t+J,g,I];var f=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;var D="str";var A="kwd";var k="com";var Q="typ";var H="lit";var M="pun";var G="pln";var n="tag";var F="dec";var K="src";var R="atn";var o="atv";var P="nocode";var N="(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function l(ab){var af=0;var U=false;var ae=false;for(var X=0,W=ab.length;X122)){if(!(am<65||ai>90)){ah.push([Math.max(65,ai)|32,Math.min(am,90)|32])}if(!(am<97||ai>122)){ah.push([Math.max(97,ai)&~32,Math.min(am,122)&~32])}}}}ah.sort(function(aw,av){return(aw[0]-av[0])||(av[1]-aw[1])});var ak=[];var aq=[];for(var at=0;atau[0]){if(au[1]+1>au[0]){ao.push("-")}ao.push(V(au[1]))}}ao.push("]");return ao.join("")}function Y(an){var al=an.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var aj=al.length;var ap=[];for(var am=0,ao=0;am=2&&ak==="["){al[am]=Z(ai)}else{if(ak!=="\\"){al[am]=ai.replace(/[a-zA-Z]/g,function(aq){var ar=aq.charCodeAt(0);return"["+String.fromCharCode(ar&~32,ar|32)+"]"})}}}}return al.join("")}var ac=[];for(var X=0,W=ab.length;X=0;){U[ae.charAt(ag)]=aa}}var ah=aa[1];var ac=""+ah;if(!ai.hasOwnProperty(ac)){aj.push(ah);ai[ac]=null}}aj.push(/[\0-\uffff]/);X=l(aj)})();var Z=V.length;var Y=function(aj){var ab=aj.sourceCode,aa=aj.basePos;var af=[aa,G];var ah=0;var ap=ab.match(X)||[];var al={};for(var ag=0,at=ap.length;ag=5&&"lang-"===ar.substring(0,5);if(ao&&!(ak&&typeof ak[1]==="string")){ao=false;ar=K}if(!ao){al[ai]=ar}}var ad=ah;ah+=ai.length;if(!ao){af.push(aa+ad,ar)}else{var an=ak[1];var am=ai.indexOf(an);var ae=am+an.length;if(ak[2]){ae=ai.length-ak[2].length;am=ae-an.length}var au=ar.substring(5);C(aa+ad,ai.substring(0,am),Y,af);C(aa+ad+am,an,r(au,an),af);C(aa+ad+ae,ai.substring(ae),Y,af)}}aj.decorations=af};return Y}function i(V){var Y=[],U=[];if(V.tripleQuotedStrings){Y.push([D,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(V.multiLineStrings){Y.push([D,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{Y.push([D,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(V.verbatimStrings){U.push([D,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var ab=V.hashComments;if(ab){if(V.cStyleComments){if(ab>1){Y.push([k,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{Y.push([k,/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}U.push([D,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,null])}else{Y.push([k,/^#[^\r\n]*/,null,"#"])}}if(V.cStyleComments){U.push([k,/^\/\/[^\r\n]*/,null]);U.push([k,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(V.regexLiterals){var aa=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");U.push(["lang-regex",new RegExp("^"+N+"("+aa+")")])}var X=V.types;if(X){U.push([Q,X])}var W=(""+V.keywords).replace(/^ | $/g,"");if(W.length){U.push([A,new RegExp("^(?:"+W.replace(/[\s,]+/g,"|")+")\\b"),null])}Y.push([G,/^\s+/,null," \r\n\t\xA0"]);var Z=/^.[^\s\w\.$@\'\"\`\/\\]*/;U.push([H,/^@[a-z_$][a-z_$@0-9]*/i,null],[Q,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[G,/^[a-z_$][a-z_$@0-9]*/i,null],[H,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[G,/^\\[\s\S]?/,null],[M,Z,null]);return h(Y,U)}var L=i({keywords:B,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function S(W,ah,aa){var V=/(?:^|\s)nocode(?:\s|$)/;var ac=/\r\n?|\n/;var ad=W.ownerDocument;var ag=ad.createElement("li");while(W.firstChild){ag.appendChild(W.firstChild)}var X=[ag];function af(am){switch(am.nodeType){case 1:if(V.test(am.className)){break}if("br"===am.nodeName){ae(am);if(am.parentNode){am.parentNode.removeChild(am)}}else{for(var ao=am.firstChild;ao;ao=ao.nextSibling){af(ao)}}break;case 3:case 4:if(aa){var an=am.nodeValue;var ak=an.match(ac);if(ak){var aj=an.substring(0,ak.index);am.nodeValue=aj;var ai=an.substring(ak.index+ak[0].length);if(ai){var al=am.parentNode;al.insertBefore(ad.createTextNode(ai),am.nextSibling)}ae(am);if(!aj){am.parentNode.removeChild(am)}}}break}}function ae(al){while(!al.nextSibling){al=al.parentNode;if(!al){return}}function aj(am,at){var ar=at?am.cloneNode(false):am;var ap=am.parentNode;if(ap){var aq=aj(ap,1);var ao=am.nextSibling;aq.appendChild(ar);for(var an=ao;an;an=ao){ao=an.nextSibling;aq.appendChild(an)}}return ar}var ai=aj(al.nextSibling,0);for(var ak;(ak=ai.parentNode)&&ak.nodeType===1;){ai=ak}X.push(ai)}for(var Z=0;Z=U){aj+=2}if(Y>=ar){ac+=2}}}finally{if(au){au.style.display=ak}}}var u={};function d(W,X){for(var U=X.length;--U>=0;){var V=X[U];if(!u.hasOwnProperty(V)){u[V]=W}else{if(O.console){console.warn("cannot override language handler %s",V)}}}}function r(V,U){if(!(V&&u.hasOwnProperty(V))){V=/^\s*]*(?:>|$)/],[k,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[M,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);d(h([[G,/^[\s]+/,null," \t\r\n"],[o,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[n,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[R,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[M,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);d(h([],[[o,/^[\s\S]+/]]),["uq.val"]);d(i({keywords:m,hashComments:true,cStyleComments:true,types:f}),["c","cc","cpp","cxx","cyc","m"]);d(i({keywords:"null,true,false"}),["json"]);d(i({keywords:T,hashComments:true,cStyleComments:true,verbatimStrings:true,types:f}),["cs"]);d(i({keywords:y,cStyleComments:true}),["java"]);d(i({keywords:I,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);d(i({keywords:J,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);d(i({keywords:t,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);d(i({keywords:g,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);d(i({keywords:x,cStyleComments:true,regexLiterals:true}),["js"]);d(i({keywords:s,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);d(h([],[[D,/^[\s\S]+/]]),["regex"]);function e(X){var W=X.langExtension;try{var U=b(X.sourceNode,X.pre);var V=U.sourceCode;X.sourceCode=V;X.spans=U.spans;X.basePos=0;r(W,V)(X);E(X)}catch(Y){if(O.console){console.log(Y&&Y.stack?Y.stack:Y)}}}function z(Y,X,W){var U=document.createElement("pre");U.innerHTML=Y;if(W){S(U,W,true)}var V={langExtension:X,numberLines:W,sourceNode:U,pre:1};e(V);return U.innerHTML}function c(aj){function ab(al){return document.getElementsByTagName(al)}var ah=[ab("pre"),ab("code"),ab("xmp")];var V=[];for(var ae=0;ae]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); \ No newline at end of file diff --git a/docs/associations/belongs-to.js.html b/docs/associations/belongs-to.js.html deleted file mode 100644 index c30aa52eefec..000000000000 --- a/docs/associations/belongs-to.js.html +++ /dev/null @@ -1,188 +0,0 @@ -Sequelize

    Sequelize

    declaration

    Utils

    Utils
      var Utils     = require("./../utils")
      -  , DataTypes = require('./../data-types')
      -
      -module.exports = (function() {
      -  var BelongsTo = function(srcDAO, targetDAO, options) {
      -    this.associationType   = 'BelongsTo'
      -    this.source            = srcDAO
      -    this.target            = targetDAO
      -    this.options           = options
      -    this.isSelfAssociation = (this.source.tableName == this.target.tableName)
      -
      -    if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) {
      -      this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.source.options.underscored)
      -    }
      -
      -    this.associationAccessor = this.isSelfAssociation
      -      ? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName)
      -      : this.options.as || this.target.tableName
      -  }
      -
      -  // the id is in the source table
      -  BelongsTo.prototype.injectAttributes = function() {
      -    var newAttributes  = {}
      -
      -    this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.target.tableName) + "Id", this.source.options.underscored)
      -    newAttributes[this.identifier] = { type: DataTypes.INTEGER }
      -    Utils._.extend(this.source.rawAttributes, newAttributes)
      -    return this
      -  }
      -
      -  BelongsTo.prototype.injectGetter = function(obj) {
      -    var self     = this
      -      , accessor = Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName)))
      -
      -    obj[accessor] = function() {
      -      var id = obj[self.identifier]
      -      return self.target.find(id)
      -    }
      -
      -    return this
      -  }
      -
      -  BelongsTo.prototype.injectSetter = function(obj) {
      -    var self     = this
      -      , accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
      -
      -    obj[accessor] = function(associatedObject) {
      -      obj[self.identifier] = associatedObject ? associatedObject.id : null
      -
      -      // passes the changed field to save, so only that field get updated.
      -      return obj.save([ self.identifier ])
      -    }
      -
      -    return this
      -  }
      -
      -  return BelongsTo
      -})()
      \ No newline at end of file diff --git a/docs/associations/has-many-double-linked.js.html b/docs/associations/has-many-double-linked.js.html deleted file mode 100644 index 7e0c03241b5e..000000000000 --- a/docs/associations/has-many-double-linked.js.html +++ /dev/null @@ -1,250 +0,0 @@ -Sequelize

      Sequelize

      declaration

      Utils

      Utils
        var Utils = require('./../utils')
        -
        -module.exports = (function() {
        -  var HasManyDoubleLinked = function(definition, instance) {
        -    this.__factory = definition
        -    this.instance = instance
        -  }
        -
        -  HasManyDoubleLinked.prototype.injectGetter = function(options) {
        -    var self = this, _options = options
        -
        -    var customEventEmitter = new Utils.CustomEventEmitter(function() {
        -      var where = {}, options = _options || {};
        -
        -      //fully qualify
        -      where[self.__factory.connectorDAO.tableName+"."+self.__factory.identifier] = self.instance.id
        -
        -      var primaryKeys = Utils._.keys(self.__factory.connectorDAO.rawAttributes)
        -        , foreignKey  = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0]
        -
        -      where[self.__factory.connectorDAO.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+".id"}
        -
        -      if (options.where) {
        -        Utils._.each(options.where, function(value, index) {
        -          delete options.where[index];
        -          options.where[self.__factory.target.tableName+"."+index] = value;
        -        });
        -
        -        options.where = options.where ? Utils.merge(options.where, where) : where
        -      } else {
        -        options.where = where;
        -      }
        -
        -      self.__factory.target.findAllJoin(self.__factory.connectorDAO.tableName, options)
        -        .on('success', function(objects) { customEventEmitter.emit('success', objects) })
        -        .on('error', function(err){ customEventEmitter.emit('error', err) })
        -        .on('sql', function(sql) { customEventEmitter.emit('sql', sql)})
        -    })
        -
        -    return customEventEmitter.run()
        -  }
        -
        -  HasManyDoubleLinked.prototype.injectSetter = function(emitterProxy, oldAssociations, newAssociations) {
        -    var self = this
        -
        -    destroyObsoleteAssociations
        -      .call(this, oldAssociations, newAssociations)
        -      .on('sql', function(sql) { emitterProxy.emit('sql', sql) })
        -      .error(function(err) { emitterProxy.emit('error', err) })
        -      .success(function() {
        -        var chainer             = new Utils.QueryChainer
        -          , association         = self.__factory.target.associations[self.__factory.associationAccessor]
        -          , foreignIdentifier   = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
        -          , unassociatedObjects = newAssociations.filter(function(obj) { return !obj.equalsOneOf(oldAssociations) })
        -
        -        unassociatedObjects.forEach(function(unassociatedObject) {
        -          var attributes = {}
        -          attributes[self.__factory.identifier] = self.instance.id
        -          attributes[foreignIdentifier] = unassociatedObject.id
        -
        -          chainer.add(self.__factory.connectorDAO.create(attributes))
        -        })
        -
        -        chainer
        -          .run()
        -          .success(function() { emitterProxy.emit('success', newAssociations) })
        -          .error(function(err) { emitterProxy.emit('error', err) })
        -          .on('sql', function(sql) { emitterProxy.emit('sql', sql) })
        -      })
        -  }
        -
        -  // private
        -
        -  var destroyObsoleteAssociations = function(oldAssociations, newAssociations) {
        -    var self = this
        -
        -    return new Utils.CustomEventEmitter(function(emitter) {
        -      var chainer = new Utils.QueryChainer()
        -      var foreignIdentifier = self.__factory.target.associations[self.__factory.associationAccessor].identifier
        -      var obsoleteAssociations = oldAssociations.filter(function(obj) { return !obj.equalsOneOf(newAssociations) })
        -
        -      if(obsoleteAssociations.length === 0) {
        -        return emitter.emit('success', null)
        -      }
        -
        -      obsoleteAssociations.forEach(function(associatedObject) {
        -        var where            = {}
        -          , primaryKeys      = Utils._.keys(self.__factory.connectorDAO.rawAttributes)
        -          , foreignKey       = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0]
        -          , notFoundEmitters = []
        -
        -        where[self.__factory.identifier] = self.instance.id
        -        where[foreignKey] = associatedObject.id
        -
        -        self.__factory.connectorDAO
        -          .find({ where: where })
        -          .success(function(connector) {
        -            if(connector === null) {
        -              notFoundEmitters.push(null)
        -            } else {
        -              chainer.add(connector.destroy())
        -            }
        -
        -            if((chainer.emitters.length + notFoundEmitters.length) === obsoleteAssociations.length) {
        -              // found all obsolete connectors and will delete them now
        -              chainer
        -                .run()
        -                .success(function() { emitter.emit('success', null) })
        -                .error(function(err) { emitter.emit('error', err) })
        -                .on('sql', function(sql) { emitter.emit('sql', sql) })
        -            }
        -          })
        -          .error(function(err) { emitter.emit('error', err) })
        -          .on('sql', function(sql) { emitter.emit('sql', sql) })
        -      })
        -    }).run()
        -  }
        -
        -  return HasManyDoubleLinked
        -})()
        \ No newline at end of file diff --git a/docs/associations/has-many-single-linked.js.html b/docs/associations/has-many-single-linked.js.html deleted file mode 100644 index c8cf86b9f97e..000000000000 --- a/docs/associations/has-many-single-linked.js.html +++ /dev/null @@ -1,172 +0,0 @@ -Sequelize

        Sequelize

        declaration

        Utils

        Utils
          var Utils = require('./../utils')
          -
          -module.exports = (function() {
          -  var HasManySingleLinked = function(definition, instance) {
          -    this.__factory = definition
          -    this.instance = instance
          -  }
          -
          -  HasManySingleLinked.prototype.injectGetter = function(options) {
          -    var where = {}, options = options || {}
          -
          -    where[this.__factory.identifier] = this.instance.id
          -
          -    options.where = options.where ? Utils.merge(options.where, where) : where
          -    return this.__factory.target.findAll(options)
          -  }
          -
          -  HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
          -    var self    = this
          -      , options = this.__factory.options
          -      , chainer = new Utils.QueryChainer()
          -
          -    // clear the old associations
          -    oldAssociations.forEach(function(associatedObject) {
          -      associatedObject[self.__factory.identifier] = null
          -      chainer.add(associatedObject.save())
          -    })
          -
          -    // set the new associations
          -    newAssociations.forEach(function(associatedObject) {
          -      associatedObject[self.__factory.identifier] = self.instance.id
          -      chainer.add(associatedObject.save())
          -    })
          -
          -    chainer
          -      .run()
          -      .success(function() { emitter.emit('success', newAssociations) })
          -      .error(function(err) { emitter.emit('error', err) })
          -  }
          -
          -  return HasManySingleLinked
          -})()
          \ No newline at end of file diff --git a/docs/associations/has-many.js.html b/docs/associations/has-many.js.html deleted file mode 100644 index cfc43bd7b913..000000000000 --- a/docs/associations/has-many.js.html +++ /dev/null @@ -1,316 +0,0 @@ -Sequelize

          Sequelize

          declaration

          Utils

          Utils
            var Utils     = require("./../utils")
            -  , DataTypes = require('./../data-types')
            -
            -var HasManySingleLinked = require("./has-many-single-linked")
            -  , HasManyMultiLinked  = require("./has-many-double-linked")
            -
            -module.exports = (function() {
            -  var HasMany = function(srcDAO, targetDAO, options) {
            -    this.source = srcDAO
            -    this.target = targetDAO
            -    this.options = options
            -    this.useJunctionTable = this.options.useJunctionTable === undefined ? true : this.options.useJunctionTable
            -    this.isSelfAssociation = (this.source.tableName === this.target.tableName)
            -
            -    var combinedTableName = Utils.combineTableNames(
            -      this.source.tableName,
            -      this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName
            -    )
            -    this.associationAccessor = this.combinedName = (this.options.joinTableName || combinedTableName)
            -
            -    var as = (this.options.as || Utils.pluralize(this.target.tableName))
            -
            -    this.accessors = {
            -      get: Utils._.camelize('get_' + as),
            -      set: Utils._.camelize('set_' + as),
            -      add: Utils._.camelize(Utils.singularize('add_' + as)),
            -      remove: Utils._.camelize(Utils.singularize('remove_' + as)),
            -      hasSingle: Utils._.camelize(Utils.singularize('has_' + as)),
            -      hasAll: Utils._.camelize('has_' + as)
            -    }
            -  }
            -
            -  // the id is in the target table
            -  // or in an extra table which connects two tables
            -  HasMany.prototype.injectAttributes = function() {
            -    var multiAssociation = this.target.associations.hasOwnProperty(this.associationAccessor)
            -    this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
            -
            -    // is there already a single sided association between the source and the target?
            -    // or is the association on the model itself?
            -    if ((this.isSelfAssociation && this.useJunctionTable) || multiAssociation) {
            -      // remove the obsolete association identifier from the source
            -      if(this.isSelfAssociation) {
            -        this.foreignIdentifier = Utils._.underscoredIf((this.options.as || this.target.tableName) + 'Id', this.options.underscored)
            -      } else {
            -        this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier
            -        delete this.source.rawAttributes[this.foreignIdentifier]
            -      }
            -
            -      // define a new model, which connects the models
            -      var combinedTableAttributes = {}
            -      combinedTableAttributes[this.identifier] = {type:DataTypes.INTEGER, primaryKey: true}
            -      combinedTableAttributes[this.foreignIdentifier] = {type:DataTypes.INTEGER, primaryKey: true}
            -
            -      this.connectorDAO = this.source.daoFactoryManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options)
            -      if(!this.isSelfAssociation) {
            -        this.target.associations[this.associationAccessor].connectorDAO = this.connectorDAO
            -      }
            -
            -      if(this.options.syncOnAssociation) {
            -        this.connectorDAO.sync()
            -      }
            -    } else {
            -      var newAttributes = {}
            -      newAttributes[this.identifier] = { type: DataTypes.INTEGER }
            -      Utils._.extend(this.target.rawAttributes, newAttributes)
            -    }
            -
            -    return this
            -  }
            -
            -  HasMany.prototype.injectGetter = function(obj) {
            -    var self = this
            -
            -    obj[this.accessors.get] = function(options) {
            -      var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked
            -      return new Class(self, this).injectGetter(options)
            -    }
            -
            -   obj[this.accessors.hasAll] = function(objects) {
            -    var instance = this;
            -      var customEventEmitter = new Utils.CustomEventEmitter(function() {
            -        instance[self.accessors.get]()
            -        .error(function(err){ customEventEmitter.emit('error', err)})
            -        .success(function(associatedObjects) {
            -          customEventEmitter.emit('success',
            -            Utils._.all(objects, function(o) {
            -              return Utils._.any(associatedObjects, function(associatedObject) {
            -                return Utils._.all(associatedObject.identifiers, function(key, identifier) {
            -                  return o[identifier] == associatedObject[identifier];
            -                });
            -              })
            -            })
            -          )
            -        })
            -      })
            -      return customEventEmitter.run()
            -    }
            -
            -    obj[this.accessors.hasSingle] = function(o) {
            -    var instance = this;
            -      var customEventEmitter = new Utils.CustomEventEmitter(function() {
            -        instance[self.accessors.get]()
            -        .error(function(err){ customEventEmitter.emit('error', err)})
            -        .success(function(associatedObjects) {
            -          customEventEmitter.emit('success',
            -            Utils._.any(associatedObjects, function(associatedObject) {
            -              return Utils._.all(associatedObject.identifiers, function(key, identifier) {
            -                return o[identifier] == associatedObject[identifier];
            -              });
            -            })
            -          )
            -        })
            -      })
            -      return customEventEmitter.run()
            -    }
            -    return this
            -  }
            -
            -  HasMany.prototype.injectSetter = function(obj) {
            -    var self = this
            -
            -    obj[this.accessors.set] = function(newAssociatedObjects) {
            -      if(newAssociatedObjects === null) {
            -        newAssociatedObjects = []
            -      }
            -
            -      var instance = this
            -
            -      // define the returned customEventEmitter, which will emit the success event once everything is done
            -      return new Utils.CustomEventEmitter(function(emitter) {
            -        instance[self.accessors.get]()
            -          .success(function(oldAssociatedObjects) {
            -            var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked
            -            new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects)
            -          })
            -          .error(function(err) {
            -            emitter.emit('error', err)
            -          })
            -          .on('sql', function(sql) {
            -            emitter.emit('sql', sql)
            -          })
            -      }).run()
            -    }
            -
            -    obj[this.accessors.add] = function(newAssociatedObject) {
            -      var instance = this
            -      var customEventEmitter = new Utils.CustomEventEmitter(function() {
            -        instance[self.accessors.get]()
            -          .error(function(err){ customEventEmitter.emit('error', err)})
            -          .success(function(currentAssociatedObjects) {
            -            if(!newAssociatedObject.equalsOneOf(currentAssociatedObjects))
            -              currentAssociatedObjects.push(newAssociatedObject)
            -
            -            instance[self.accessors.set](currentAssociatedObjects)
            -              .success(function(instances) { customEventEmitter.emit('success', instances) })
            -              .error(function(err) { customEventEmitter.emit('error', err) })
            -          })
            -      })
            -      return customEventEmitter.run()
            -    }
            -
            -    obj[this.accessors.remove] = function(oldAssociatedObject) {
            -      var instance = this
            -      var customEventEmitter = new Utils.CustomEventEmitter(function() {
            -        instance[self.accessors.get]().success(function(currentAssociatedObjects) {
            -          var newAssociations = []
            -
            -          currentAssociatedObjects.forEach(function(association) {
            -            if(!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers))
            -              newAssociations.push(association)
            -          })
            -
            -          instance[self.accessors.set](newAssociations)
            -            .success(function() { customEventEmitter.emit('success', null) })
            -            .error(function(err) { customEventEmitter.emit('error', err) })
            -        })
            -      })
            -      return customEventEmitter.run()
            -    }
            -
            -    return this
            -  }
            -
            -  return HasMany
            -})()
            \ No newline at end of file diff --git a/docs/associations/has-one.js.html b/docs/associations/has-one.js.html deleted file mode 100644 index ae62c0c64278..000000000000 --- a/docs/associations/has-one.js.html +++ /dev/null @@ -1,211 +0,0 @@ -Sequelize

            Sequelize

            declaration

            Utils

            Utils
              var Utils     = require("./../utils")
              -  , DataTypes = require('./../data-types')
              -
              -module.exports = (function() {
              -  var HasOne = function(srcDAO, targetDAO, options) {
              -    this.associationType   = 'HasOne'
              -    this.source            = srcDAO
              -    this.target            = targetDAO
              -    this.options           = options
              -    this.isSelfAssociation = (this.source.tableName == this.target.tableName)
              -
              -    if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) {
              -      this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.options.underscored)
              -    }
              -
              -    this.associationAccessor = this.isSelfAssociation
              -      ? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName)
              -      : this.options.as || this.target.tableName
              -
              -    this.accessors = {
              -      get: Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName))),
              -      set: Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
              -    }
              -  }
              -
              -  // the id is in the target table
              -  HasOne.prototype.injectAttributes = function() {
              -    var newAttributes = {}
              -
              -    this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
              -    newAttributes[this.identifier] = { type: DataTypes.INTEGER }
              -    Utils._.extend(this.target.rawAttributes, newAttributes)
              -
              -    return this
              -  }
              -
              -  HasOne.prototype.injectGetter = function(obj) {
              -    var self = this
              -
              -    obj[this.accessors.get] = function() {
              -      var id    = obj.id
              -        , where = {}
              -
              -      where[self.identifier] = id
              -      return self.target.find({where: where})
              -    }
              -
              -    return this
              -  }
              -
              -  HasOne.prototype.injectSetter = function(obj) {
              -    var self    = this
              -      , options = self.options || {}
              -
              -    obj[this.accessors.set] = function(associatedObject) {
              -      return new Utils.CustomEventEmitter(function(emitter) {
              -        obj[self.accessors.get]().success(function(oldObj) {
              -          if(oldObj) {
              -            oldObj[self.identifier] = null
              -            oldObj.save()
              -          }
              -
              -          if(associatedObject) {
              -            associatedObject[self.identifier] = obj.id
              -            associatedObject
              -              .save()
              -              .success(function() { emitter.emit('success', associatedObject) })
              -              .error(function(err) { emitter.emit('error', err) })
              -          } else {
              -            emitter.emit('success', null)
              -          }
              -
              -        })
              -      }).run()
              -    }
              -
              -    return this
              -  }
              -
              -  return HasOne
              -})()
              \ No newline at end of file diff --git a/docs/associations/mixin.js.html b/docs/associations/mixin.js.html deleted file mode 100644 index ed4a931aba33..000000000000 --- a/docs/associations/mixin.js.html +++ /dev/null @@ -1,166 +0,0 @@ -Sequelize

              Sequelize

              declaration

              Mixin

              Mixin

              Defines Mixin for all models.

                var Mixin = module.exports = function(){}
                -
                -Mixin.hasOne = function(associatedDAO, options) {
                -  // the id is in the foreign table
                -  var association = new HasOne(this, associatedDAO, Utils._.extend((options||{}), this.options))
                -  this.associations[association.associationAccessor] = association.injectAttributes()
                -  return this
                -}
                -
                -Mixin.belongsTo = function(associatedDAO, options) {
                -  // the id is in this table
                -  var association = new BelongsTo(this, associatedDAO, Utils._.extend((options||{}), this.options))
                -  this.associations[association.associationAccessor] = association.injectAttributes()
                -  return this
                -}
                -
                -Mixin.hasMany = function(associatedDAO, options) {
                -  // the id is in the foreign table or in a connecting table
                -  var association = new HasMany(this, associatedDAO, Utils._.extend((options||{}), this.options))
                -  this.associations[association.associationAccessor] = association.injectAttributes()
                -  return this
                -}
                -
                -Mixin.getAssociation = function(target, options) {
                -  var result = null
                -
                -  for (var associationName in this.associations) {
                -    var association = this.associations[associationName]
                -
                -    if (!result && (association.target === target)) {
                -      result = association
                -    }
                -  }
                -
                -  return result
                -}

                example for instance methods:
                Mixin.prototype.test = function() {
                console.log('asd')
                }

                  \ No newline at end of file diff --git a/docs/classes/QueryInterface.html b/docs/classes/QueryInterface.html new file mode 100644 index 000000000000..393e1770c3ac --- /dev/null +++ b/docs/classes/QueryInterface.html @@ -0,0 +1,686 @@ + + + + + QueryInterface + + + + + + + + +
                  +
                  +
                  + +

                  + +
                  +
                  + API Docs for: +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + Show: + + + + + + + +
                  + + +
                  +
                  +
                  +

                  QueryInterface Class

                  +
                  + + + + + + + + + + + Module: Sequelize + + + + +
                  + + + +
                  +

                  Returns an object that treats SQLite's inabilities to do certain queries.

                  +
                  + + + +
                  + + +
                  +
                  +

                  Item Index

                  + + +
                  +

                  Methods

                  + + +
                  + + + + + + + +
                  + + +
                  +

                  Methods

                  + + +
                  +

                  changeColumn

                  + + +
                  + (
                    + +
                  • + + tableName + +
                  • + +
                  • + + attributes + +
                  • + +
                  • + + emitter + +
                  • + +
                  • + + queryAndEmit + +
                  • + +
                  ) +
                  + + + + + + + + + + + + + + + + +
                  + + + +

                  + + Defined in + + + + + lib/dialects/sqlite/query-interface.js:40 + +

                  + + + + + +

                  Available since 1.6.0

                  + +
                  + +
                  +

                  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 +new table with the same name but with a modified version of the respective column.

                  +
                  + + +
                  +

                  Parameters:

                  + +
                    + +
                  • + + tableName + String + + + + +
                    +

                    The name of the table.

                    +
                    + + +
                  • + +
                  • + + attributes + Object + + + + +
                    +

                    An object with the attribute's name as key and it's options as value object.

                    +
                    + + +
                  • + +
                  • + + emitter + CustomEventEmitter + + + + +
                    +

                    The EventEmitter from outside.

                    +
                    + + +
                  • + +
                  • + + queryAndEmit + Function + + + + +
                    +

                    The function from outside that triggers some events to get triggered.

                    +
                    + + +
                  • + +
                  +
                  + + + + + +
                  + + +
                  +

                  removeColumn

                  + + +
                  + (
                    + +
                  • + + tableName + +
                  • + +
                  • + + attributeName + +
                  • + +
                  • + + emitter + +
                  • + +
                  • + + queryAndEmit + +
                  • + +
                  ) +
                  + + + + + + + + + + + + + + + + +
                  + + + +

                  + + Defined in + + + + + lib/dialects/sqlite/query-interface.js:10 + +

                  + + + + + +

                  Available since 1.6.0

                  + +
                  + +
                  +

                  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 +new table with the same name but without the obsolete column.

                  +
                  + + +
                  +

                  Parameters:

                  + +
                    + +
                  • + + tableName + String + + + + +
                    +

                    The name of the table.

                    +
                    + + +
                  • + +
                  • + + attributeName + String + + + + +
                    +

                    The name of the attribute that we want to remove.

                    +
                    + + +
                  • + +
                  • + + emitter + CustomEventEmitter + + + + +
                    +

                    The EventEmitter from outside.

                    +
                    + + +
                  • + +
                  • + + queryAndEmit + Function + + + + +
                    +

                    The function from outside that triggers some events to get triggered.

                    +
                    + + +
                  • + +
                  +
                  + + + + + +
                  + + +
                  +

                  renameColumn

                  + + +
                  + (
                    + +
                  • + + tableName + +
                  • + +
                  • + + attrNameBefore + +
                  • + +
                  • + + attrNameAfter + +
                  • + +
                  • + + emitter + +
                  • + +
                  • + + queryAndEmit + +
                  • + +
                  ) +
                  + + + + + + + + + + + + + + + + +
                  + + + +

                  + + Defined in + + + + + lib/dialects/sqlite/query-interface.js:72 + +

                  + + + + + +

                  Available since 1.6.0

                  + +
                  + +
                  +

                  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 +new table with the same name but with a renamed version of the respective column.

                  +
                  + + +
                  +

                  Parameters:

                  + +
                    + +
                  • + + tableName + String + + + + +
                    +

                    The name of the table.

                    +
                    + + +
                  • + +
                  • + + attrNameBefore + String + + + + +
                    +

                    The name of the attribute before it was renamed.

                    +
                    + + +
                  • + +
                  • + + attrNameAfter + String + + + + +
                    +

                    The name of the attribute after it was renamed.

                    +
                    + + +
                  • + +
                  • + + emitter + CustomEventEmitter + + + + +
                    +

                    The EventEmitter from outside.

                    +
                    + + +
                  • + +
                  • + + queryAndEmit + Function + + + + +
                    +

                    The function from outside that triggers some events to get triggered.

                    +
                    + + +
                  • + +
                  +
                  + + + + + +
                  + + +
                  + + + + + + + +
                  +
                  + +
                  +
                  +
                  +
                  +
                  +
                  + + + + + + + + + + diff --git a/docs/classes/Sequelize.html b/docs/classes/Sequelize.html new file mode 100644 index 000000000000..cd7955796e67 --- /dev/null +++ b/docs/classes/Sequelize.html @@ -0,0 +1,754 @@ + + + + + Sequelize + + + + + + + + +
                  +
                  +
                  + +

                  + +
                  +
                  + API Docs for: +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + Show: + + + + + + + +
                  + + +
                  +
                  +
                  +

                  Sequelize Class

                  +
                  + + + + + +
                  + Defined in: lib/sequelize.js:12 +
                  + + + + + Module: Sequelize + + + + +
                  + + + +
                  +

                  Main class of the project.

                  +
                  + + +
                  +

                  Constructor

                  +
                  +

                  Sequelize

                  + + +
                  + (
                    + +
                  • + + database + +
                  • + +
                  • + + username + +
                  • + +
                  • + + [password=null] + +
                  • + +
                  • + + [options={}] + +
                  • + +
                  ) +
                  + + + + + + + + + + + + + + + + +
                  + + + +

                  + + Defined in + + + + + lib/sequelize.js:12 + +

                  + + + + + +
                  + +
                  + +
                  + + +
                  +

                  Parameters:

                  + +
                    + +
                  • + + database + String + + + + +
                    +

                    The name of the database.

                    +
                    + + +
                  • + +
                  • + + username + String + + + + +
                    +

                    The username which is used to authenticate against the database.

                    +
                    + + +
                  • + +
                  • + + [password=null] + String + optional + + + + +
                    +

                    The password which is used to authenticate against the database.

                    +
                    + + +
                  • + +
                  • + + [options={}] + Object + optional + + + + +
                    +

                    An object with options.

                    +
                    + + +
                      + +
                    • + + [dialect='mysql'] + String + optional + + +
                      +

                      The dialect of the relational database.

                      +
                      + + +
                    • + +
                    • + + [host='localhost'] + String + optional + + +
                      +

                      The host of the relational database.

                      +
                      + + +
                    • + +
                    • + + [port=3306] + Integer + optional + + +
                      +

                      The port of the relational database.

                      +
                      + + +
                    • + +
                    • + + [protocol='tcp'] + String + optional + + +
                      +

                      The protocol of the relational database.

                      +
                      + + +
                    • + +
                    • + + [define={}] + Object + optional + + +
                      +

                      Options, which shall be default for every model definition.

                      +
                      + + +
                    • + +
                    • + + [query={}] + Object + optional + + +
                      +

                      I have absolutely no idea.

                      +
                      + + +
                    • + +
                    • + + [sync={}] + Object + optional + + +
                      +

                      Options, which shall be default for every sync call.

                      +
                      + + +
                    • + +
                    • + + [logging=console.log] + Function + optional + + +
                      +

                      A function that gets executed everytime Sequelize would log something.

                      +
                      + + +
                    • + +
                    • + + [omitNull=false] + Boolean + optional + + +
                      +

                      A flag that defines if null values should be passed to SQL queries or not.

                      +
                      + + +
                    • + +
                    • + + [queue=true] + Boolean + optional + + +
                      +

                      I have absolutely no idea.

                      +
                      + + +
                    • + +
                    • + + [native=false] + Boolean + optional + + +
                      +

                      A flag that defines if native library shall be used or not.

                      +
                      + + +
                    • + +
                    • + + [replication=false] + Boolean + optional + + +
                      +

                      I have absolutely no idea.

                      +
                      + + +
                    • + +
                    • + + [pool={}] + Object + optional + + +
                      +

                      Something.

                      +
                      + + +
                    • + +
                    + +
                  • + +
                  +
                  + + + + + +
                  +

                  Example:

                  + +
                  +
                  // without password and options
                  +var sequelize = new Sequelize('database', 'username')
                  +
                  +// without options
                  +var sequelize = new Sequelize('database', 'username', 'password')
                  +
                  +// without password / with blank password
                  +var sequelize = new Sequelize('database', 'username', null, {})
                  +
                  +// with password and options
                  +var sequelize = new Sequelize('my_database', 'john', 'doe', {})
                  +
                  +
                  +
                  + +
                  + +
                  + + +
                  + + +
                  +
                  +

                  Item Index

                  + + +
                  +

                  Methods

                  + + +
                  + + + + + + + +
                  + + +
                  +

                  Methods

                  + + +
                  +

                  getMigrator

                  + + +
                  + (
                    + +
                  • + + [options={}] + +
                  • + +
                  • + + [force=false] + +
                  • + +
                  ) +
                  + + + + + Migrator + + + + + + + + + + + + + + + +
                  + + + +

                  + + Defined in + + + + + lib/sequelize.js:113 + +

                  + + + + + +
                  + +
                  +

                  Returns an instance (singleton) of Migrator.

                  +
                  + + +
                  +

                  Parameters:

                  + +
                    + +
                  • + + [options={}] + Object + optional + + + + +
                    +

                    Some options

                    +
                    + + +
                  • + +
                  • + + [force=false] + Boolean + optional + + + + +
                    +

                    A flag that defines if the migrator should get instantiated or not.

                    +
                    + + +
                  • + +
                  +
                  + + + +
                  +

                  Returns:

                  + +
                  + + + Migrator: + + An instance of Migrator. + +
                  +
                  + + + +
                  + + +
                  +

                  getQueryInterface

                  + + + () + + + + + QueryInterface + + + + + + + + + + + + + + + +
                  + + + +

                  + + Defined in + + + + + lib/sequelize.js:102 + +

                  + + + + + +
                  + +
                  +

                  Returns an instance of QueryInterface.

                  +
                  + + + + +
                  +

                  Returns:

                  + +
                  + + + QueryInterface: + + An instance (singleton) of QueryInterface. + +
                  +
                  + + + +
                  + + +
                  + + + + + + + +
                  +
                  + +
                  +
                  +
                  +
                  +
                  +
                  + + + + + + + + + + diff --git a/docs/classes/index.html b/docs/classes/index.html new file mode 100644 index 000000000000..487fe15b2ad0 --- /dev/null +++ b/docs/classes/index.html @@ -0,0 +1,10 @@ + + + + Redirector + + + + Click here to redirect + + diff --git a/docs/dao-factory-manager.js.html b/docs/dao-factory-manager.js.html deleted file mode 100644 index 1c8bb2cedf87..000000000000 --- a/docs/dao-factory-manager.js.html +++ /dev/null @@ -1,165 +0,0 @@ -Sequelize

                  Sequelize

                  property

                  exports

                  module.exports
                    module.exports = (function() {
                    -  var DAOFactoryManager = function(sequelize) {
                    -    this.daos = []
                    -    this.sequelize = sequelize
                    -  }
                    -
                    -  DAOFactoryManager.prototype.addDAO = function(dao) {
                    -    this.daos.push(dao)
                    -
                    -    return dao
                    -  }
                    -
                    -  DAOFactoryManager.prototype.removeDAO = function(dao) {
                    -    this.daos = this.daos.filter(function(_dao) {
                    -      return _dao.name != dao.name
                    -    })
                    -  }
                    -
                    -  DAOFactoryManager.prototype.getDAO = function(daoName, options) {
                    -    options = options || {}
                    -    options.attribute = options.attribute || 'name'
                    -
                    -    var dao = this.daos.filter(function(dao) {
                    -      return dao[options.attribute] === daoName
                    -    })
                    -
                    -    return !!dao ? dao[0] : null
                    -  }
                    -
                    -  DAOFactoryManager.prototype.__defineGetter__('all', function() {
                    -    return this.daos
                    -  })
                    -
                    -  return DAOFactoryManager
                    -})()
                    \ No newline at end of file diff --git a/docs/dao-factory.js.html b/docs/dao-factory.js.html deleted file mode 100644 index d00c3550494f..000000000000 --- a/docs/dao-factory.js.html +++ /dev/null @@ -1,409 +0,0 @@ -Sequelize

                    Sequelize

                    declaration

                    Utils

                    Utils
                      var Utils     = require("./utils")
                      -  , DAO       = require("./dao")
                      -  , DataTypes = require("./data-types")
                      -
                      -module.exports = (function() {
                      -  var DAOFactory = function(name, attributes, options) {
                      -    var self = this
                      -
                      -    this.options = Utils._.extend({
                      -      timestamps: true,
                      -      instanceMethods: {},
                      -      classMethods: {},
                      -      validate: {},
                      -      freezeTableName: false,
                      -      underscored: false,
                      -      syncOnAssociation: true,
                      -      paranoid: false
                      -    }, options || {})
                      -
                      -    this.name = name
                      -    this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name)
                      -    this.rawAttributes = attributes
                      -    this.daoFactoryManager = null // defined in init function
                      -    this.associations = {}
                      -
                      -    // extract validation
                      -    this.validate = this.options.validate || {}
                      -  }
                      -
                      -  Object.defineProperty(DAOFactory.prototype, 'attributes', {
                      -    get: function() {
                      -      return this.QueryGenerator.attributesToSQL(this.rawAttributes)
                      -    }
                      -  })
                      -
                      -  Object.defineProperty(DAOFactory.prototype, 'QueryInterface', {
                      -    get: function() { return this.daoFactoryManager.sequelize.getQueryInterface() }
                      -  })
                      -
                      -  Object.defineProperty(DAOFactory.prototype, 'QueryGenerator', {
                      -    get: function() { return this.QueryInterface.QueryGenerator }
                      -  })
                      -
                      -  Object.defineProperty(DAOFactory.prototype, 'primaryKeyCount', {
                      -    get: function() { return Utils._.keys(this.primaryKeys).length }
                      -  })
                      -
                      -  Object.defineProperty(DAOFactory.prototype, 'hasPrimaryKeys', {
                      -    get: function() { return this.primaryKeyCount > 0 }
                      -  })
                      -
                      -  DAOFactory.prototype.init = function(daoFactoryManager) {
                      -    this.daoFactoryManager = daoFactoryManager
                      -
                      -    addDefaultAttributes.call(this)
                      -    addOptionalClassMethods.call(this)
                      -    findAutoIncrementField.call(this)
                      -
                      -    return this
                      -  }
                      -
                      -  DAOFactory.prototype.sync = function(options) {
                      -    options = Utils.merge(options || {}, this.options)
                      -
                      -    var self = this
                      -    return new Utils.CustomEventEmitter(function(emitter) {
                      -      var doQuery = function() {
                      -        self.QueryInterface
                      -          .createTable(self.tableName, self.attributes, options)
                      -          .success(function() { emitter.emit('success', self) })
                      -          .error(function(err) { emitter.emit('error', err) })
                      -          .on('sql', function(sql) { emitter.emit('sql', sql) })
                      -      }
                      -
                      -      if(options.force)
                      -        self.drop().success(doQuery).error(function(err) { emitter.emit('error', err) })
                      -      else
                      -        doQuery()
                      -
                      -    }).run()
                      -  }
                      -
                      -  DAOFactory.prototype.drop = function() {
                      -    return this.QueryInterface.dropTable(this.tableName)
                      -  }
                      -
                      -  // alias for findAll
                      -  DAOFactory.prototype.all = function(options) {
                      -    return this.findAll(options)
                      -  }
                      -
                      -  DAOFactory.prototype.findAll = function(options) {
                      -    return this.QueryInterface.select(this, this.tableName, options)
                      -  }
                      -
                      -  //right now, the caller (has-many-double-linked) is in charge of the where clause
                      -  DAOFactory.prototype.findAllJoin = function(joinTableName, options) {
                      -    var optcpy = Utils._.clone(options)
                      -    optcpy.attributes = optcpy.attributes || [Utils.addTicks(this.tableName)+".*"]
                      -
                      -    return this.QueryInterface.select(this, [this.tableName, joinTableName], optcpy)
                      -  }
                      -
                      -  DAOFactory.prototype.find = function(options) {
                      -    // no options defined?
                      -    // return an emitter which emits null
                      -    if([null, undefined].indexOf(options) > -1) {
                      -      return new Utils.CustomEventEmitter(function(emitter) {
                      -        setTimeout(function() { emitter.emit('success', null) }, 10)
                      -      }).run()
                      -    }
                      -
                      -    // options is not a hash but an id
                      -    if(typeof options === 'number') {
                      -      options = { where: options }
                      -    } else if (Utils.argsArePrimaryKeys(arguments, this.primaryKeys)) {
                      -        var where = {}
                      -          , self  = this
                      -
                      -        Utils._.each(arguments, function(arg, i) {
                      -          var key = Utils._.keys(self.primaryKeys)[i]
                      -          where[key] = arg
                      -        })
                      -
                      -        options = { where: where }
                      -    } else if((typeof options === 'object') && (options.hasOwnProperty('include'))) {
                      -      var includes = options.include
                      -
                      -      options.include = {}
                      -
                      -      includes.forEach(function(daoName) {
                      -        options.include[daoName] = this.daoFactoryManager.getDAO(daoName)
                      -      }.bind(this))
                      -    }
                      -
                      -    options.limit = 1
                      -
                      -    return this.QueryInterface.select(this, this.tableName, options, { plain: true })
                      -  }
                      -
                      -  DAOFactory.prototype.count = function(options) {
                      -    options = Utils._.extend({ attributes: [] }, options || {})
                      -    options.attributes.push(['count(*)', 'count'])
                      -    options.parseInt = true
                      -
                      -    return this.QueryInterface.rawSelect(this.tableName, options, 'count')
                      -  }
                      -
                      -  DAOFactory.prototype.max = function(field, options) {
                      -    options = Utils._.extend({ attributes: [] }, options || {})
                      -    options.attributes.push(['max(' + field + ')', 'max'])
                      -    options.parseInt = true
                      -
                      -    return this.QueryInterface.rawSelect(this.tableName, options, 'max')
                      -  }
                      -  DAOFactory.prototype.min = function(field, options) {
                      -    options = Utils._.extend({ attributes: [] }, options || {})
                      -    options.attributes.push(['min(' + field + ')', 'min'])
                      -    options.parseInt = true
                      -
                      -    return this.QueryInterface.rawSelect(this.tableName, options, 'min')
                      -  }
                      -
                      -  DAOFactory.prototype.build = function(values, options) {
                      -    var instance = new DAO(values, Utils._.extend(this.options, this.attributes, { hasPrimaryKeys: this.hasPrimaryKeys }))
                      -      , self     = this
                      -
                      -    options = options || {}
                      -    instance.__factory = this
                      -
                      -    Utils._.each(this.attributes, function(definition, name) {
                      -      //transform integer 0,1 into boolean
                      -      if((definition.indexOf(DataTypes.BOOLEAN) !== -1) && (typeof instance[name] === "number")) {
                      -        instance[name] = (instance[name] !== 0)
                      -      }
                      -
                      -      //add default attributes
                      -      if(typeof instance[name] === 'undefined') {
                      -        var value = null
                      -
                      -        if(self.rawAttributes.hasOwnProperty(name) && self.rawAttributes[name].hasOwnProperty('defaultValue')) {
                      -          value = Utils.toDefaultValue(self.rawAttributes[name].defaultValue)
                      -        }
                      -
                      -        instance[name] = value
                      -        instance.addAttribute(name, value)
                      -      }
                      -
                      -      // add validation
                      -      if (self.rawAttributes.hasOwnProperty(name) && self.rawAttributes[name].hasOwnProperty('validate')) {
                      -        instance.setValidators(name, self.rawAttributes[name].validate)
                      -      }
                      -    })
                      -
                      -    Utils._.each(this.options.instanceMethods || {}, function(fct, name) { instance[name] = fct })
                      -    Utils._.each(this.associations, function(association, associationName) {
                      -      association.injectGetter(instance)
                      -      association.injectSetter(instance)
                      -    })
                      -
                      -    instance.isNewRecord = options.hasOwnProperty('isNewRecord') ? options.isNewRecord : true
                      -
                      -    return instance
                      -  }
                      -
                      -  DAOFactory.prototype.create = function(values, fields) {
                      -    return this.build(values).save(fields)
                      -  }
                      -
                      -  DAOFactory.prototype.__defineGetter__('primaryKeys', function() {
                      -    var result = {}
                      -    Utils._.each(this.attributes, function(dataTypeString, attributeName) {
                      -      if((attributeName != 'id') && (dataTypeString.indexOf('PRIMARY KEY') > -1))
                      -        result[attributeName] = dataTypeString
                      -    })
                      -
                      -    return result
                      -  })
                      -
                      -  // private
                      -
                      -  var query = function() {
                      -    var args = Utils._.map(arguments, function(arg, _) { return arg })
                      -      , s    = this.daoFactoryManager.sequelize
                      -
                      -    // add this as the second argument
                      -    if(arguments.length == 1) args.push(this)
                      -    return s.query.apply(s, args)
                      -  }
                      -
                      -  var addOptionalClassMethods = function() {
                      -    var self = this
                      -    Utils._.each(this.options.classMethods || {}, function(fct, name) { self[name] = fct })
                      -  }
                      -
                      -  var addDefaultAttributes = function() {
                      -    var self              = this
                      -      , defaultAttributes = {
                      -        id: {
                      -          type: DataTypes.INTEGER,
                      -          allowNull: false,
                      -          primaryKey: true,
                      -          autoIncrement: true
                      -        }
                      -      }
                      -
                      -    if(this.hasPrimaryKeys) defaultAttributes = {}
                      -
                      -    if(this.options.timestamps) {
                      -      defaultAttributes[Utils._.underscoredIf('createdAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
                      -      defaultAttributes[Utils._.underscoredIf('updatedAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
                      -
                      -      if(this.options.paranoid)
                      -        defaultAttributes[Utils._.underscoredIf('deletedAt', this.options.underscored)] = {type: DataTypes.DATE}
                      -    }
                      -
                      -    Utils._.each(defaultAttributes, function(value, attr) {
                      -      self.rawAttributes[attr] = value
                      -    })
                      -  }
                      -
                      -  var findAutoIncrementField = function() {
                      -    var self   = this
                      -      , fields = this.QueryGenerator.findAutoIncrementField(this)
                      -
                      -    this.autoIncrementField = null
                      -
                      -    fields.forEach(function(field) {
                      -      if(self.autoIncrementField)
                      -        throw new Error('Invalid DAO definition. Only one autoincrement field allowed.')
                      -      else
                      -        self.autoIncrementField = field
                      -    })
                      -  }
                      -
                      -  Utils._.extend(DAOFactory.prototype, require("./associations/mixin"))
                      -
                      -  return DAOFactory
                      -})()
                      \ No newline at end of file diff --git a/docs/dao.js.html b/docs/dao.js.html deleted file mode 100644 index 8f250b63ece6..000000000000 --- a/docs/dao.js.html +++ /dev/null @@ -1,294 +0,0 @@ -Sequelize

                      Sequelize

                      method

                      validate

                      DAO.prototype.validate()

                      Validate this dao's attribute values according to validation rules set in the dao definition.

                      • @return: {null} if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries.
                      DAO.prototype.validate = function() {
                      -    var self = this
                      -    var failures = {}
                      -
                      -    // for each field and value
                      -    Utils._.each(self.values, function(value, field) {
                      -
                      -      // if field has validators
                      -      if (self.validators.hasOwnProperty(field)) {
                      -        // for each validator
                      -        Utils._.each(self.validators[field], function(details, validatorType) {
                      -
                      -          var is_custom_fn = false  // if true then it's a custom validation method
                      -          var fn_method = null      // the validation function to call
                      -          var fn_args = []          // extra arguments to pass to validation function
                      -          var fn_msg = ""           // the error message to return if validation fails
                      -
                      -          // is it a custom validator function?
                      -          if (Utils._.isFunction(details)) {
                      -            is_custom_fn = true
                      -            fn_method = Utils._.bind(details, self, value)
                      -          }
                      -          // is it a validator module function?
                      -          else {
                      -            // extra args
                      -            fn_args = details.hasOwnProperty("args") ? details.args : []
                      -            if (!Utils._.isArray(fn_args))
                      -              fn_args = [fn_args]
                      -            // error msg
                      -            fn_msg = details.hasOwnProperty("msg") ? details.msg : false
                      -            // check method exists
                      -            var v = Validator.check(value, fn_msg)
                      -            if (!Utils._.isFunction(v[validatorType]))
                      -              throw new Error("Invalid validator function: " + validatorType)
                      -            // bind to validator obj
                      -            fn_method = Utils._.bind(v[validatorType], v)
                      -          }
                      -
                      -          try {
                      -            fn_method.apply(null, fn_args)
                      -          } catch (err) {
                      -            err = err.message
                      -            // if we didn't provide a custom error message then augment the default one returned by the validator
                      -            if (!fn_msg && !is_custom_fn)
                      -              err += ": " + field
                      -            // each field can have multiple validation failures stored against it
                      -            if (failures.hasOwnProperty(field)) {
                      -              failures[field].push(err)
                      -            } else {
                      -              failures[field] = [err]
                      -            }
                      -          }
                      -
                      -        }) // for each validator for this field
                      -      } // if field has validator set
                      -    }) // for each field
                      -
                      -    return (Utils._.isEmpty(failures) ? null : failures)
                      -  }
                      -
                      -
                      -  DAO.prototype.updateAttributes = function(updates) {
                      -    this.setAttributes(updates)
                      -    return this.save()
                      -  }
                      -
                      -  DAO.prototype.setAttributes = function(updates) {
                      -    var self = this
                      -
                      -    var readOnlyAttributes = Utils._.keys(this.__factory.primaryKeys)
                      -
                      -    readOnlyAttributes.push('id')
                      -    readOnlyAttributes.push('createdAt')
                      -    readOnlyAttributes.push('updatedAt')
                      -    readOnlyAttributes.push('deletedAt')
                      -
                      -    Utils._.each(updates, function(value, attr) {
                      -      var updateAllowed = (
                      -        (readOnlyAttributes.indexOf(attr) == -1) &&
                      -        (readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
                      -        (self.attributes.indexOf(attr) > -1)
                      -      )
                      -      updateAllowed && (self[attr] = value)
                      -    })
                      -  }
                      -
                      -  DAO.prototype.destroy = function() {
                      -    if(this.__options.timestamps && this.__options.paranoid) {
                      -      var attr = this.__options.underscored ? 'deleted_at' : 'deletedAt'
                      -      this[attr] = new Date()
                      -      return this.save()
                      -    } else {
                      -      var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
                      -      return this.QueryInterface.delete(this, this.__factory.tableName, identifier)
                      -    }
                      -  }
                      -
                      -  DAO.prototype.equals = function(other) {
                      -    var result = true
                      -      , self   = this
                      -
                      -    Utils._.each(this.values, function(value, key) {
                      -      result = result && (value == other[key])
                      -    })
                      -
                      -    return result
                      -  }
                      -
                      -  DAO.prototype.equalsOneOf = function(others) {
                      -    var result = false
                      -      , self   = this
                      -
                      -    others.forEach(function(other) { result = result || self.equals(other) })
                      -
                      -    return result
                      -  }
                      -
                      -  DAO.prototype.addAttribute = function(attribute, value) {
                      -    this[attribute] = value
                      -    this.attributes.push(attribute)
                      -  }
                      -
                      -  DAO.prototype.setValidators = function(attribute, validators) {
                      -    this.validators[attribute] = validators
                      -  }
                      -
                      -  DAO.prototype.toJSON = function() {
                      -    return this.values;
                      -  }
                      -
                      -  // private
                      -
                      -  var initAttributes = function(values) {
                      -    // add all passed values to the dao and store the attribute names in this.attributes
                      -    for (var key in values) {
                      -      if (values.hasOwnProperty(key)) {
                      -        this.addAttribute(key, values[key])
                      -      }
                      -    }
                      -
                      -    // set id to null if not passed as value
                      -    // a newly created dao has no id
                      -    var defaults = this.__options.hasPrimaryKeys ? {} : { id: null }
                      -
                      -    if(this.__options.timestamps) {
                      -      defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = new Date()
                      -      defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = new Date()
                      -
                      -      if(this.__options.paranoid) {
                      -        defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null
                      -      }
                      -    }
                      -
                      -    for (var attr in defaults) {
                      -      var value = defaults[attr]
                      -
                      -      if(!this.hasOwnProperty(attr)) {
                      -        this.addAttribute(attr, Utils.toDefaultValue(value))
                      -      }
                      -    }
                      -  }

                      Add the instance methods to DAO

                        Utils._.extend(DAO.prototype, Mixin.prototype)
                        -
                        -  return DAO
                        -})()
                        \ No newline at end of file diff --git a/docs/data-types.js.html b/docs/data-types.js.html deleted file mode 100644 index 431695e88487..000000000000 --- a/docs/data-types.js.html +++ /dev/null @@ -1,139 +0,0 @@ -Sequelize

                        Sequelize

                        property

                        exports

                        module.exports
                          module.exports = {
                          -  STRING: 'VARCHAR(255)',
                          -  TEXT: 'TEXT',
                          -  INTEGER: 'INTEGER',
                          -  DATE: 'DATETIME',
                          -  BOOLEAN: 'TINYINT(1)',
                          -  FLOAT: 'FLOAT',
                          -  NOW: 'NOW'
                          -}
                          \ No newline at end of file diff --git a/docs/data.json b/docs/data.json new file mode 100644 index 000000000000..f915bd267df6 --- /dev/null +++ b/docs/data.json @@ -0,0 +1,560 @@ +{ + "project": {}, + "files": { + "lib/dialects/abstract/query.js": { + "name": "lib/dialects/abstract/query.js", + "modules": {}, + "classes": {}, + "fors": {}, + "namespaces": {} + }, + "lib/dialects/sqlite/query-interface.js": { + "name": "lib/dialects/sqlite/query-interface.js", + "modules": {}, + "classes": { + "QueryInterface": 1 + }, + "fors": { + "QueryInterface": 1 + }, + "namespaces": {} + }, + "lib/dao-factory.js": { + "name": "lib/dao-factory.js", + "modules": {}, + "classes": {}, + "fors": {}, + "namespaces": {} + }, + "lib/sequelize.js": { + "name": "lib/sequelize.js", + "modules": {}, + "classes": { + "Sequelize": 1 + }, + "fors": {}, + "namespaces": {} + }, + "index.js": { + "name": "index.js", + "modules": { + "Sequelize": 1 + }, + "classes": {}, + "fors": {}, + "namespaces": {} + } + }, + "modules": { + "Sequelize": { + "name": "Sequelize", + "submodules": {}, + "classes": { + "QueryInterface": 1, + "Sequelize": 1 + }, + "fors": {}, + "namespaces": {}, + "tag": "module", + "file": "lib/sequelize.js", + "line": 12, + "description": "The entry point." + } + }, + "classes": { + "QueryInterface": { + "name": "QueryInterface", + "shortname": "QueryInterface", + "classitems": [], + "plugins": [], + "extensions": [], + "plugin_for": [], + "extension_for": [], + "module": "Sequelize", + "file": "lib/dialects/sqlite/query-interface.js", + "line": 3, + "description": "Returns an object that treats SQLite's inabilities to do certain queries.", + "static": 1 + }, + "Sequelize": { + "name": "Sequelize", + "shortname": "Sequelize", + "classitems": [], + "plugins": [], + "extensions": [], + "plugin_for": [], + "extension_for": [], + "module": "Sequelize", + "file": "lib/sequelize.js", + "line": 12, + "description": "Main class of the project.", + "params": [ + { + "name": "database", + "description": "The name of the database.", + "type": "String" + }, + { + "name": "username", + "description": "The username which is used to authenticate against the database.", + "type": "String" + }, + { + "name": "password", + "description": "The password which is used to authenticate against the database.", + "type": "String", + "optional": true, + "optdefault": "null" + }, + { + "name": "options", + "description": "An object with options.", + "type": "Object", + "optional": true, + "optdefault": "{}", + "props": [ + { + "name": "dialect", + "description": "The dialect of the relational database.", + "type": "String", + "optional": true, + "optdefault": "'mysql'" + }, + { + "name": "host", + "description": "The host of the relational database.", + "type": "String", + "optional": true, + "optdefault": "'localhost'" + }, + { + "name": "port", + "description": "The port of the relational database.", + "type": "Integer", + "optional": true, + "optdefault": "3306" + }, + { + "name": "protocol", + "description": "The protocol of the relational database.", + "type": "String", + "optional": true, + "optdefault": "'tcp'" + }, + { + "name": "define", + "description": "Options, which shall be default for every model definition.", + "type": "Object", + "optional": true, + "optdefault": "{}" + }, + { + "name": "query", + "description": "I have absolutely no idea.", + "type": "Object", + "optional": true, + "optdefault": "{}" + }, + { + "name": "sync", + "description": "Options, which shall be default for every `sync` call.", + "type": "Object", + "optional": true, + "optdefault": "{}" + }, + { + "name": "logging", + "description": "A function that gets executed everytime Sequelize would log something.", + "type": "Function", + "optional": true, + "optdefault": "console.log" + }, + { + "name": "omitNull", + "description": "A flag that defines if null values should be passed to SQL queries or not.", + "type": "Boolean", + "optional": true, + "optdefault": "false" + }, + { + "name": "queue", + "description": "I have absolutely no idea.", + "type": "Boolean", + "optional": true, + "optdefault": "true" + }, + { + "name": "native", + "description": "A flag that defines if native library shall be used or not.", + "type": "Boolean", + "optional": true, + "optdefault": "false" + }, + { + "name": "replication", + "description": "I have absolutely no idea.", + "type": "Boolean", + "optional": true, + "optdefault": "false" + }, + { + "name": "pool", + "description": "Something.", + "type": "Object", + "optional": true, + "optdefault": "{}" + } + ] + } + ], + "example": [ + "\n // without password and options\n var sequelize = new Sequelize('database', 'username')\n\n // without options\n var sequelize = new Sequelize('database', 'username', 'password')\n\n // without password / with blank password\n var sequelize = new Sequelize('database', 'username', null, {})\n\n // with password and options\n var sequelize = new Sequelize('my_database', 'john', 'doe', {})" + ], + "is_constructor": 1 + } + }, + "classitems": [ + { + "file": "lib/dialects/abstract/query.js", + "line": 8, + "description": "Inherit from CustomEventEmitter", + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 13, + "description": "Execute the passed sql query.\n\nExamples:\n\n query.run('SELECT 1')", + "params": [ + { + "name": "sql", + "description": "- The SQL query which should be executed.", + "type": "String" + } + ], + "api": "public", + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 27, + "description": "Check the logging option of the instance and print deprecation warnings.", + "return": { + "description": "", + "type": "Void" + }, + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 44, + "description": "High level function that handles the results of a query execution.\n\n\nExample:\n query.formatResults([\n {\n id: 1, // this is from the main table\n attr2: 'snafu', // this is from the main table\n Tasks.id: 1, // this is from the associated table\n Tasks.title: 'task' // this is from the associated table\n }\n ])", + "params": [ + { + "name": "data", + "description": "- The result of the query execution.", + "type": "Array" + } + ], + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 102, + "description": "Shortcut methods (success, ok) for listening for success events.\n\nParams:\n - fct: A function that gets executed once the *success* event was triggered.\n\nResult:\n The function returns the instance of the query.", + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 118, + "description": "Shortcut methods (failure, fail, error) for listening for error events.\n\nParams:\n - fct: A function that gets executed once the *error* event was triggered.\n\nResult:\n The function returns the instance of the query.", + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 135, + "description": "This function is a wrapper for private methods.", + "params": [ + { + "name": "fctName", + "description": "The name of the private method.", + "type": "String" + } + ], + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 146, + "description": "Get the attributes of an insert query, which contains the just inserted id.", + "return": { + "description": "The field name.", + "type": "String" + }, + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 159, + "description": "Iterate over all known tables and search their names inside the sql query.\nThis method will also check association aliases ('as' option).", + "params": [ + { + "name": "attribute", + "description": "An attribute of a SQL query. (?)", + "type": "String" + } + ], + "return": { + "description": "The found tableName / alias.", + "type": "String" + }, + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 350, + "description": "The function takes the result of the query execution and groups\nthe associated data by the callee.\n\nExample:\n groupDataByCalleeFactory([\n {\n callee: { some: 'data', id: 1 },\n association: { foo: 'bar', id: 1 }\n }, {\n callee: { some: 'data', id: 1 },\n association: { foo: 'bar', id: 2 }\n }, {\n callee: { some: 'data', id: 1 },\n association: { foo: 'bar', id: 3 }\n }\n ])\n\nResult:\n Something like this:\n\n [\n {\n callee: { some: 'data', id: 1 },\n association: [\n { foo: 'bar', id: 1 },\n { foo: 'bar', id: 2 },\n { foo: 'bar', id: 3 }\n ]\n }\n ]", + "class": "QueryInterface" + }, + { + "file": "lib/dialects/abstract/query.js", + "line": 410, + "description": "This function will prepare the result of select queries with joins.", + "params": [ + { + "name": "data", + "description": "This array contains objects.", + "type": "Array" + } + ], + "return": { + "description": "The array will have the needed format for groupDataByCalleeFactory.", + "type": "Array" + }, + "class": "QueryInterface" + }, + { + "file": "lib/dialects/sqlite/query-interface.js", + "line": 10, + "description": "A wrapper that fixes SQLite's inability to remove columns from existing tables.\nIt will create a backup of the table, drop the table afterwards and create a\nnew table with the same name but without the obsolete column.", + "itemtype": "method", + "name": "removeColumn", + "params": [ + { + "name": "tableName", + "description": "The name of the table.", + "type": "String" + }, + { + "name": "attributeName", + "description": "The name of the attribute that we want to remove.", + "type": "String" + }, + { + "name": "emitter", + "description": "The EventEmitter from outside.", + "type": "CustomEventEmitter" + }, + { + "name": "queryAndEmit", + "description": "The function from outside that triggers some events to get triggered.", + "type": "Function" + } + ], + "since": "1.6.0", + "class": "QueryInterface" + }, + { + "file": "lib/dialects/sqlite/query-interface.js", + "line": 40, + "description": "A wrapper that fixes SQLite's inability to change columns from existing tables.\nIt will create a backup of the table, drop the table afterwards and create a\nnew table with the same name but with a modified version of the respective column.", + "itemtype": "method", + "name": "changeColumn", + "params": [ + { + "name": "tableName", + "description": "The name of the table.", + "type": "String" + }, + { + "name": "attributes", + "description": "An object with the attribute's name as key and it's options as value object.", + "type": "Object" + }, + { + "name": "emitter", + "description": "The EventEmitter from outside.", + "type": "CustomEventEmitter" + }, + { + "name": "queryAndEmit", + "description": "The function from outside that triggers some events to get triggered.", + "type": "Function" + } + ], + "since": "1.6.0", + "class": "QueryInterface" + }, + { + "file": "lib/dialects/sqlite/query-interface.js", + "line": 72, + "description": "A wrapper that fixes SQLite's inability to rename columns from existing tables.\nIt will create a backup of the table, drop the table afterwards and create a\nnew table with the same name but with a renamed version of the respective column.", + "itemtype": "method", + "name": "renameColumn", + "params": [ + { + "name": "tableName", + "description": "The name of the table.", + "type": "String" + }, + { + "name": "attrNameBefore", + "description": "The name of the attribute before it was renamed.", + "type": "String" + }, + { + "name": "attrNameAfter", + "description": "The name of the attribute after it was renamed.", + "type": "String" + }, + { + "name": "emitter", + "description": "The EventEmitter from outside.", + "type": "CustomEventEmitter" + }, + { + "name": "queryAndEmit", + "description": "The function from outside that triggers some events to get triggered.", + "type": "Function" + } + ], + "since": "1.6.0", + "class": "QueryInterface" + }, + { + "file": "lib/dao-factory.js", + "line": 178, + "description": "Search for an instance.", + "params": [ + { + "name": "options", + "description": "Options to describe the scope of the search.", + "type": "Object" + }, + { + "name": "include", + "description": "A list of associations which shall get eagerly loaded. Supported is either { include: [ DaoFactory1, DaoFactory2, ...] } or { include: [ { daoFactory: DaoFactory1, as: 'Alias' } ] }.", + "type": "Array" + } + ], + "return": { + "description": "A promise which fires `success`, `error`, `complete` and `sql`.", + "type": "Object" + }, + "class": "Sequelize" + }, + { + "file": "lib/sequelize.js", + "line": 93, + "description": "Reference to Utils", + "class": "Sequelize" + }, + { + "file": "lib/sequelize.js", + "line": 102, + "description": "Returns an instance of QueryInterface.", + "itemtype": "method", + "name": "getQueryInterface", + "return": { + "description": "An instance (singleton) of QueryInterface.", + "type": "QueryInterface" + }, + "class": "Sequelize" + }, + { + "file": "lib/sequelize.js", + "line": 113, + "description": "Returns an instance (singleton) of Migrator.", + "itemtype": "method", + "name": "getMigrator", + "params": [ + { + "name": "options", + "description": "Some options", + "type": "Object", + "optional": true, + "optdefault": "{}" + }, + { + "name": "force", + "description": "A flag that defines if the migrator should get instantiated or not.", + "type": "Boolean", + "optional": true, + "optdefault": "false" + } + ], + "return": { + "description": "An instance of Migrator.", + "type": "Migrator" + }, + "class": "Sequelize" + } + ], + "warnings": [ + { + "message": "unknown tag: api", + "line": " lib/dialects/abstract/query.js:13" + }, + { + "message": "Missing item type\nInherit from CustomEventEmitter", + "line": " lib/dialects/abstract/query.js:8" + }, + { + "message": "Missing item type\nExecute the passed sql query.\n\nExamples:\n\n query.run('SELECT 1')", + "line": " lib/dialects/abstract/query.js:13" + }, + { + "message": "Missing item type\nCheck the logging option of the instance and print deprecation warnings.", + "line": " lib/dialects/abstract/query.js:27" + }, + { + "message": "Missing item type\nHigh level function that handles the results of a query execution.\n\n\nExample:\n query.formatResults([\n {\n id: 1, // this is from the main table\n attr2: 'snafu', // this is from the main table\n Tasks.id: 1, // this is from the associated table\n Tasks.title: 'task' // this is from the associated table\n }\n ])", + "line": " lib/dialects/abstract/query.js:44" + }, + { + "message": "Missing item type\nShortcut methods (success, ok) for listening for success events.\n\nParams:\n - fct: A function that gets executed once the *success* event was triggered.\n\nResult:\n The function returns the instance of the query.", + "line": " lib/dialects/abstract/query.js:102" + }, + { + "message": "Missing item type\nShortcut methods (failure, fail, error) for listening for error events.\n\nParams:\n - fct: A function that gets executed once the *error* event was triggered.\n\nResult:\n The function returns the instance of the query.", + "line": " lib/dialects/abstract/query.js:118" + }, + { + "message": "Missing item type\nThis function is a wrapper for private methods.", + "line": " lib/dialects/abstract/query.js:135" + }, + { + "message": "Missing item type\nGet the attributes of an insert query, which contains the just inserted id.", + "line": " lib/dialects/abstract/query.js:146" + }, + { + "message": "Missing item type\nIterate over all known tables and search their names inside the sql query.\nThis method will also check association aliases ('as' option).", + "line": " lib/dialects/abstract/query.js:159" + }, + { + "message": "Missing item type\nThe function takes the result of the query execution and groups\nthe associated data by the callee.\n\nExample:\n groupDataByCalleeFactory([\n {\n callee: { some: 'data', id: 1 },\n association: { foo: 'bar', id: 1 }\n }, {\n callee: { some: 'data', id: 1 },\n association: { foo: 'bar', id: 2 }\n }, {\n callee: { some: 'data', id: 1 },\n association: { foo: 'bar', id: 3 }\n }\n ])\n\nResult:\n Something like this:\n\n [\n {\n callee: { some: 'data', id: 1 },\n association: [\n { foo: 'bar', id: 1 },\n { foo: 'bar', id: 2 },\n { foo: 'bar', id: 3 }\n ]\n }\n ]", + "line": " lib/dialects/abstract/query.js:350" + }, + { + "message": "Missing item type\nThis function will prepare the result of select queries with joins.", + "line": " lib/dialects/abstract/query.js:410" + }, + { + "message": "Missing item type\nSearch for an instance.", + "line": " lib/dao-factory.js:178" + }, + { + "message": "Missing item type\nReference to Utils", + "line": " lib/sequelize.js:93" + } + ] +} \ No newline at end of file diff --git a/docs/dialects/abstract/query.js.html b/docs/dialects/abstract/query.js.html deleted file mode 100644 index 13d6c3a60671..000000000000 --- a/docs/dialects/abstract/query.js.html +++ /dev/null @@ -1,336 +0,0 @@ -Sequelize

                          Sequelize

                          Inherit from CustomEventEmitter

                            Utils.inherit(AbstractQuery, CustomEventEmitter)
                            method

                            run

                            AbstractQuery.prototype.run()

                            Execute the passed sql query.

                            - -

                            Examples

                            - -
                            query.run('SELECT 1')
                            -
                            • @param: {String} sql - The SQL query which should be executed.
                            AbstractQuery.prototype.run = function(sql) {
                            -    throw new Error("The run method wasn't overwritten!")
                            -  }
                            method

                            formatResults

                            AbstractQuery.prototype.formatResults()

                            High level function that handles the results of a query execution.

                            - -

                            Example

                            - -

                            query.formatResults([
                            {
                            UserWithNames: {
                            name: 'barfooz',
                            id: 1,
                            createdAt: Sat Oct 06 2012 14:46:36 GMT+0200 (CEST),
                            updatedAt: Sat Oct 06 2012 14:46:36 GMT+0200 (CEST)
                            },
                            Tasks: {
                            title: 'task',
                            id: 1,
                            createdAt: Sat Oct 06 2012 14:46:36 GMT+0200 (CEST),
                            updatedAt: Sat Oct 06 2012 14:46:36 GMT+0200 (CEST),
                            UserWithNameId: 1
                            }
                            }
                            ])

                            • @param: {Array} data - The result of the query execution.
                            AbstractQuery.prototype.formatResults = function(data) {
                            -    var result  = this.callee
                            -
                            -    if (isInsertQuery.call(this, data)) {
                            -      handleInsertQuery.call(this, data)
                            -    }
                            -
                            -    if (isSelectQuery.call(this)) {
                            -      result = handleSelectQuery.call(this, data)
                            -    } else if (isShowTableQuery.call(this)) {
                            -      result = handleShowTableQuery.call(this, data)
                            -    } else if (isShowOrDescribeQuery.call(this)) {
                            -      result = data
                            -    }
                            -
                            -    return result
                            -  }

                            Shortcut methods (success, ok) for listening for success events.

                            - -
                            Params:
                            -  - fct: A function that gets executed once the *success* event was triggered.
                            -
                            -Result:
                            -  The function returns the instance of the query.
                            -
                              AbstractQuery.prototype.success =
                              -  AbstractQuery.prototype.ok =
                              -  function(fct) {
                              -    this.on('success', fct)
                              -    return this
                              -  }

                              Shortcut methods (failure, fail, error) for listening for error events.

                              - -
                              Params:
                              -  - fct: A function that gets executed once the *error* event was triggered.
                              -
                              -Result:
                              -  The function returns the instance of the query.
                              -
                                AbstractQuery.prototype.failure =
                                -  AbstractQuery.prototype.fail =
                                -  AbstractQuery.prototype.error =
                                -  function(fct) {
                                -    this.on('error', fct)
                                -    return this
                                -  }
                                -
                                -  var queryResultHasJoin = function(results) {
                                -    var hasJoin = !!results[0]
                                -
                                -    hasJoin = hasJoin && (Utils._.keys(results[0]).length > 1)
                                -    hasJoin = hasJoin && (Utils.isHash(results[0][Utils._.keys(results[0])[0]]))
                                -
                                -    return hasJoin
                                -  }
                                -
                                -  var isInsertQuery = function(results) {
                                -    var result = !!this.callee
                                -
                                -    result = result && (this.sql.indexOf('INSERT INTO') === 0)
                                -    result = result && results.hasOwnProperty('insertId')
                                -
                                -    return result
                                -  }
                                -
                                -  var handleInsertQuery = function(results) {
                                -    // add the inserted row id to the instance
                                -    var autoIncrementField = this.callee.__factory.autoIncrementField
                                -    this.callee[autoIncrementField] = results.insertId
                                -  }
                                -
                                -  var isShowTableQuery = function() {
                                -    return (this.sql.indexOf('SHOW TABLES') === 0)
                                -  }
                                -
                                -  var handleShowTableQuery = function(results) {
                                -    return Utils._.flatten(results.map(function(resultSet) {
                                -      return Utils._.values(resultSet)
                                -    }))
                                -  }
                                -
                                -  var isSelectQuery = function() {
                                -    return (this.sql.indexOf('SELECT') === 0)
                                -  }
                                -
                                -  var handleSelectQuery = function(results) {
                                -    var result = null
                                -
                                -    if (this.options.raw) {
                                -      result = results
                                -    } else if (queryResultHasJoin(results)) {
                                -      result = groupDataByCalleeFactory.call(this, results).map(function(result) {
                                -        // let's build the actual dao instance first...
                                -        var dao = this.callee.build(result[this.callee.tableName], { isNewRecord: false })
                                -
                                -        // ... and afterwards the prefetched associations
                                -        for (var tableName in result) {
                                -          if (result.hasOwnProperty(tableName) && (tableName !== this.callee.tableName)) {
                                -            buildAssociatedDaoInstances.call(this, tableName, result[tableName], dao)
                                -          }
                                -        }
                                -
                                -        return dao
                                -      }.bind(this))
                                -    } else {
                                -      result = results.map(function(result) {
                                -        return this.callee.build(result, { isNewRecord: false })
                                -      }.bind(this))
                                -    }
                                -
                                -    // return the first real model instance if options.plain is set (e.g. Model.find)
                                -    if(this.options.plain) {
                                -      result = (result.length === 0) ? null : result[0]
                                -    }
                                -
                                -    return result
                                -  }
                                -
                                -  var buildAssociatedDaoInstances = function(tableName, associationData, dao) {
                                -    var associatedDao = this.sequelize.daoFactoryManager.getDAO(tableName, { attribute: 'tableName' })
                                -      , association   = this.callee.getAssociation(associatedDao)
                                -      , accessor      = Utils._.camelize(associatedDao.tableName)
                                -
                                -    // downcase the first char
                                -    accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
                                -
                                -    associationData.forEach(function(data) {
                                -      var daoInstance = associatedDao.build(data, { isNewRecord: false })
                                -
                                -      if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) {
                                -        accessor = Utils.singularize(accessor)
                                -        dao[accessor] = daoInstance
                                -      } else {
                                -        dao[accessor] = dao[accessor] || []
                                -        dao[accessor].push(daoInstance)
                                -      }
                                -    })
                                -  }
                                -
                                -  var isShowOrDescribeQuery = function() {
                                -    return (this.sql.indexOf('SHOW') === 0) || (this.sql.indexOf('DESCRIBE') === 0)
                                -  }
                                function

                                groupDataByCalleeFactory

                                groupDataByCalleeFactory()

                                The function takes the result of the query execution and groups
                                the associated data by the callee.

                                - -
                                Example:
                                -  groupDataByCalleeFactory([
                                -    {
                                -      callee: { some: 'data', id: 1 },
                                -      association: { foo: 'bar', id: 1 }
                                -    }, {
                                -      callee: { some: 'data', id: 1 },
                                -      association: { foo: 'bar', id: 2 }
                                -    }, {
                                -      callee: { some: 'data', id: 1 },
                                -      association: { foo: 'bar', id: 3 }
                                -    }
                                -  ])
                                -
                                -Result:
                                -  Something like this:
                                -
                                -  [
                                -    {
                                -      callee:  { some: 'data', id: 1 },
                                -      association: [
                                -        { foo: 'bar', id: 1 },
                                -        { foo: 'bar', id: 2 },
                                -        { foo: 'bar', id: 3 }
                                -      ]
                                -    }
                                -  ]
                                -
                                  var groupDataByCalleeFactory = function(data) {
                                  -    var result          = []
                                  -      , calleeTableName = this.callee.tableName
                                  -
                                  -    data.forEach(function(row) {
                                  -      var calleeData    = row[calleeTableName]
                                  -        , existingEntry = result.filter(function(groupedRow) {
                                  -            return Utils._.isEqual(groupedRow[calleeTableName], calleeData)
                                  -          })[0]
                                  -
                                  -      if (!existingEntry) {
                                  -        existingEntry = {}
                                  -        result.push(existingEntry)
                                  -        existingEntry[calleeTableName] = calleeData
                                  -      }
                                  -
                                  -      for (var attrName in row) {
                                  -        if (row.hasOwnProperty(attrName) && (attrName !== calleeTableName)) {
                                  -          existingEntry[attrName] = existingEntry[attrName] || []
                                  -          existingEntry[attrName].push(row[attrName])
                                  -        }
                                  -      }
                                  -    })
                                  -
                                  -    return result
                                  -  }
                                  -
                                  -  return AbstractQuery
                                  -})()
                                  \ No newline at end of file diff --git a/docs/dialects/connector-manager.js.html b/docs/dialects/connector-manager.js.html deleted file mode 100644 index fd7f8f0f017c..000000000000 --- a/docs/dialects/connector-manager.js.html +++ /dev/null @@ -1,154 +0,0 @@ -Sequelize

                                  Sequelize

                                  property

                                  exports

                                  module.exports
                                    module.exports = (function(){
                                    -  var ConnectorManager = function(sequelize, config) {
                                    -    throw new Error('Define the constructor!')
                                    -  }
                                    -
                                    -  ConnectorManager.prototype.query = function(sql, callee, options) {
                                    -    throw new Error('Define the query method!')
                                    -  }
                                    -
                                    -  ConnectorManager.prototype.connect = function() {
                                    -    throw new Error('Define the connect method!')
                                    -  }
                                    -
                                    -  ConnectorManager.prototype.disconnect = function() {
                                    -    throw new Error('Define the disconnect method!')
                                    -  }
                                    -
                                    -  ConnectorManager.prototype.reconnect = function() {
                                    -    this.disconnect()
                                    -    this.connect()
                                    -  }
                                    -
                                    -  return ConnectorManager
                                    -})()
                                    \ No newline at end of file diff --git a/docs/dialects/mysql/connector-manager.js.html b/docs/dialects/mysql/connector-manager.js.html deleted file mode 100644 index 310eb1585663..000000000000 --- a/docs/dialects/mysql/connector-manager.js.html +++ /dev/null @@ -1,343 +0,0 @@ -Sequelize

                                    Sequelize

                                    declaration

                                    mysql

                                    mysql
                                      var mysql   = require("mysql")
                                      -  , Pooling = require('generic-pool')
                                      -  , Query   = require("./query")
                                      -  , Utils   = require("../../utils")
                                      -  , without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) }
                                      -
                                      -module.exports = (function() {
                                      -  var ConnectorManager = function(sequelize, config) {
                                      -    this.sequelize = sequelize
                                      -    this.client = null
                                      -    this.config = config || {}
                                      -    this.disconnectTimeoutId = null
                                      -    this.queue = []
                                      -    this.activeQueue = []
                                      -    this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
                                      -    this.poolCfg = this.config.pool
                                      -
                                      -    var self = this
                                      -
                                      -    if (this.poolCfg) {
                                      -      //the user has requested pooling, so create our connection pool
                                      -      if (!this.poolCfg.maxConnections) {
                                      -        this.poolCfg.maxConnections = 10
                                      -      }
                                      -      this.pool = Pooling.Pool({
                                      -        name: 'sequelize-mysql',
                                      -        create: function (done) {
                                      -            connect.call(self, done)
                                      -        },
                                      -        destroy: function(client) {
                                      -            disconnect.call(self, client)
                                      -        },
                                      -        max: self.poolCfg.maxConnections,
                                      -        idleTimeoutMillis: self.poolCfg.maxIdleTime
                                      -      })
                                      -    }
                                      -
                                      -    process.on('exit', function () {
                                      -      //be nice & close our connections on exit
                                      -      if (self.pool) {
                                      -        self.pool.drain()
                                      -      } else if (self.client) {
                                      -        disconnect(self.client)
                                      -      }
                                      -
                                      -      return
                                      -    })
                                      -  }
                                      -
                                      -  Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
                                      -
                                      -  var isConnecting = false
                                      -
                                      -  ConnectorManager.prototype.query = function(sql, callee, options) {
                                      -    if(!this.isConnected && !this.pool) this.connect()
                                      -
                                      -    var queueItem = {
                                      -      query: new Query(this.client, this.sequelize, callee, options || {}),
                                      -      sql: sql
                                      -    }
                                      -
                                      -    enqueue.call(this, queueItem)
                                      -
                                      -    return queueItem.query
                                      -  }
                                      -
                                      -  ConnectorManager.prototype.connect = function() {
                                      -    var self = this
                                      -    // in case database is slow to connect, prevent orphaning the client
                                      -    if (this.isConnecting || this.pool) {
                                      -      return
                                      -    }
                                      -    connect.call(self, function(err, client) {
                                      -      self.client = client
                                      -      return
                                      -    })
                                      -    return
                                      -  }
                                      -
                                      -  ConnectorManager.prototype.disconnect = function() {
                                      -    if (this.client)
                                      -      disconnect.call(this, this.client)
                                      -    return
                                      -  }
                                      -
                                      -
                                      -  // private
                                      -
                                      -  var disconnect = function(client) {
                                      -    var self = this
                                      -    client.end(function() {
                                      -      var intervalObj = null
                                      -      var cleanup = function () {
                                      -        var retryCt = 0
                                      -        // make sure to let client finish before calling destroy
                                      -        if (self && self.hasQueuedItems) {
                                      -          return
                                      -        }
                                      -        // needed to prevent mysql connection leak
                                      -        client.destroy()
                                      -        if (self && self.client) {
                                      -          self.client = null
                                      -        }
                                      -        clearInterval(intervalObj)
                                      -      }
                                      -      intervalObj = setInterval(cleanup, 10)
                                      -      cleanup()
                                      -      return
                                      -    })
                                      -  }
                                      -
                                      -  var connect = function(done) {
                                      -    var connection = mysql.createConnection({
                                      -      host: this.config.host,
                                      -      port: this.config.port,
                                      -      user: this.config.username,
                                      -      password: this.config.password,
                                      -      database: this.config.database
                                      -    })
                                      -    // client.setMaxListeners(self.maxConcurrentQueries)
                                      -    this.isConnecting = false
                                      -
                                      -    done(null, connection)
                                      -  }
                                      -
                                      -  var enqueue = function(queueItem) {
                                      -    if(this.activeQueue.length < this.maxConcurrentQueries) {
                                      -      this.activeQueue.push(queueItem)
                                      -      if (this.pool) {
                                      -        var self = this
                                      -        this.pool.acquire(function(err, client) {
                                      -          if (err) {
                                      -            queueItem.query.emit('error', err)
                                      -            return
                                      -          }
                                      -          //we set the client here, asynchronously, when getting a pooled connection
                                      -          //allowing the ConnectorManager.query method to remain synchronous
                                      -          queueItem.query.client = client
                                      -          queueItem.client = client
                                      -          execQueueItem.call(self, queueItem)
                                      -          return
                                      -        })
                                      -      }
                                      -      else
                                      -      {
                                      -        execQueueItem.call(this, queueItem)
                                      -      }
                                      -    } else {
                                      -      this.queue.push(queueItem)
                                      -    }
                                      -  }
                                      -
                                      -  var dequeue = function(queueItem) {
                                      -    //return the item's connection to the pool
                                      -    if (this.pool) {
                                      -      this.pool.release(queueItem.client)
                                      -    }
                                      -    this.activeQueue = without(this.activeQueue, queueItem)
                                      -  }
                                      -
                                      -  var transferQueuedItems = function(count) {
                                      -    for(var i = 0; i < count; i++) {
                                      -      var queueItem = this.queue[0]
                                      -      if(queueItem) {
                                      -        enqueue.call(this, queueItem)
                                      -        this.queue = without(this.queue, queueItem)
                                      -      }
                                      -    }
                                      -  }
                                      -
                                      -  var afterQuery = function(queueItem) {
                                      -    var self = this
                                      -
                                      -    dequeue.call(this, queueItem)
                                      -    transferQueuedItems.call(this, this.maxConcurrentQueries - this.activeQueue.length)
                                      -    disconnectIfNoConnections.call(this)
                                      -  }
                                      -
                                      -
                                      -  var execQueueItem = function(queueItem) {
                                      -    var self = this
                                      -
                                      -    queueItem.query
                                      -      .success(function(){ afterQuery.call(self, queueItem) })
                                      -      .error(function(){ afterQuery.call(self, queueItem) })
                                      -
                                      -    queueItem.query.run(queueItem.sql, queueItem.client)
                                      -  }
                                      -
                                      -  ConnectorManager.prototype.__defineGetter__('hasQueuedItems', function() {
                                      -    return (this.queue.length > 0) || (this.activeQueue.length > 0) || (this.client && this.client._queue && (this.client._queue.length > 0))
                                      -  })
                                      -
                                      -  // legacy
                                      -  ConnectorManager.prototype.__defineGetter__('hasNoConnections', function() {
                                      -    return !this.hasQueuedItems
                                      -  })
                                      -
                                      -  ConnectorManager.prototype.__defineGetter__('isConnected', function() {
                                      -    return this.client != null
                                      -  })
                                      -
                                      -  var disconnectIfNoConnections = function() {
                                      -    var self = this
                                      -
                                      -    this.disconnectTimeoutId && clearTimeout(this.disconnectTimeoutId)
                                      -    this.disconnectTimeoutId = setTimeout(function() {
                                      -      self.isConnected && !self.hasQueuedItems && self.disconnect()
                                      -    }, 100)
                                      -  }
                                      -
                                      -  return ConnectorManager
                                      -})()
                                      \ No newline at end of file diff --git a/docs/dialects/mysql/query-generator.js.html b/docs/dialects/mysql/query-generator.js.html deleted file mode 100644 index 22b4185358ce..000000000000 --- a/docs/dialects/mysql/query-generator.js.html +++ /dev/null @@ -1,512 +0,0 @@ -Sequelize

                                      Sequelize

                                      declaration

                                      Utils

                                      Utils
                                        var Utils     = require("../../utils")
                                        -  , DataTypes = require("../../data-types")
                                        -  , util      = require("util")
                                        -
                                        -module.exports = (function() {
                                        -  var QueryGenerator = {
                                        -    createTableQuery: function(tableName, attributes, options) {
                                        -      options = Utils._.extend({
                                        -        engine: 'InnoDB',
                                        -        charset: null
                                        -      }, options || {})
                                        -
                                        -      var query   = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>) ENGINE=<%= engine %> <%= charset %>"
                                        -        , primaryKeys = []
                                        -        , attrStr = []
                                        -
                                        -      for (var attr in attributes) {
                                        -        var dataType = attributes[attr]
                                        -
                                        -        if (Utils._.includes(dataType, 'PRIMARY KEY')) {
                                        -          primaryKeys.push(attr)
                                        -          attrStr.push(Utils.addTicks(attr) + " " + dataType.replace(/PRIMARY KEY/, ''))
                                        -        } else {
                                        -          attrStr.push(Utils.addTicks(attr) + " " + dataType)
                                        -        }
                                        -      }
                                        -
                                        -      var values = {
                                        -        table: Utils.addTicks(tableName),
                                        -        attributes: attrStr.join(", "),
                                        -        engine: options.engine,
                                        -        charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "")
                                        -      }
                                        -      , pkString = primaryKeys.map(function(pk) { return Utils.addTicks(pk) }).join(", ")
                                        -
                                        -      if(pkString.length > 0) {
                                        -        values.attributes += ", PRIMARY KEY (" + pkString + ")"
                                        -      }
                                        -
                                        -      return Utils._.template(query)(values).trim() + ";"
                                        -    },
                                        -
                                        -    dropTableQuery: function(tableName, options) {
                                        -      options = options || {}
                                        -
                                        -      var query = "DROP TABLE IF EXISTS <%= table %>;"
                                        -
                                        -      return Utils._.template(query)({table: Utils.addTicks(tableName)})
                                        -    },
                                        -
                                        -    renameTableQuery: function(before, after) {
                                        -      var query = "RENAME TABLE `<%= before %>` TO `<%= after %>`;"
                                        -      return Utils._.template(query)({ before: before, after: after })
                                        -    },
                                        -
                                        -    showTablesQuery: function() {
                                        -      return 'SHOW TABLES;'
                                        -    },
                                        -
                                        -    addColumnQuery: function(tableName, attributes) {
                                        -      var query      = "ALTER TABLE `<%= tableName %>` ADD <%= attributes %>;"
                                        -        , attrString = []
                                        -
                                        -      for (var attrName in attributes) {
                                        -        var definition = attributes[attrName]
                                        -
                                        -        attrString.push(Utils._.template('`<%= attrName %>` <%= definition %>')({
                                        -          attrName: attrName,
                                        -          definition: definition
                                        -        }))
                                        -      }
                                        -
                                        -      return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
                                        -    },
                                        -
                                        -    removeColumnQuery: function(tableName, attributeName) {
                                        -      var query = "ALTER TABLE `<%= tableName %>` DROP `<%= attributeName %>`;"
                                        -      return Utils._.template(query)({ tableName: tableName, attributeName: attributeName })
                                        -    },
                                        -
                                        -    changeColumnQuery: function(tableName, attributes) {
                                        -      var query      = "ALTER TABLE `<%= tableName %>` CHANGE <%= attributes %>;"
                                        -      var attrString = []
                                        -
                                        -      for (attrName in attributes) {
                                        -        var definition = attributes[attrName]
                                        -
                                        -        attrString.push(Utils._.template('`<%= attrName %>` `<%= attrName %>` <%= definition %>')({
                                        -          attrName: attrName,
                                        -          definition: definition
                                        -        }))
                                        -      }
                                        -
                                        -      return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
                                        -    },
                                        -
                                        -    renameColumnQuery: function(tableName, attrBefore, attributes) {
                                        -      var query      = "ALTER TABLE `<%= tableName %>` CHANGE <%= attributes %>;"
                                        -      var attrString = []
                                        -
                                        -      for (var attrName in attributes) {
                                        -        var definition = attributes[attrName]
                                        -
                                        -        attrString.push(Utils._.template('`<%= before %>` `<%= after %>` <%= definition %>')({
                                        -          before: attrBefore,
                                        -          after: attrName,
                                        -          definition: definition
                                        -        }))
                                        -      }
                                        -
                                        -      return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
                                        -    },
                                        -
                                        -    selectQuery: function(tableName, options) {
                                        -      var query = "SELECT <%= attributes %> FROM <%= table %>"
                                        -
                                        -      options = options || {}
                                        -      options.table = Array.isArray(tableName) ? tableName.map(function(tbl){ return Utils.addTicks(tbl) }).join(", ") : Utils.addTicks(tableName)
                                        -      options.attributes = options.attributes && options.attributes.map(function(attr){
                                        -        if(Array.isArray(attr) && attr.length == 2) {
                                        -          return [attr[0], Utils.addTicks(attr[1])].join(' as ')
                                        -        } else {
                                        -          return attr.indexOf(Utils.TICK_CHAR) < 0 ? Utils.addTicks(attr) : attr
                                        -        }
                                        -      }).join(", ")
                                        -      options.attributes = options.attributes || '*'
                                        -
                                        -      if (options.include) {
                                        -        var tableNames = []
                                        -
                                        -        for (var daoName in options.include) {
                                        -          tableNames.push(Utils.addTicks(options.include[daoName].tableName))
                                        -        }
                                        -
                                        -        options.table = [options.table].concat(tableNames).join(', ')
                                        -      }
                                        -
                                        -      if(options.where) {
                                        -        options.where = QueryGenerator.getWhereConditions(options.where, tableName)
                                        -        query += " WHERE <%= where %>"
                                        -      }
                                        -
                                        -      if(options.order) {
                                        -        query += " ORDER BY <%= order %>"
                                        -      }
                                        -
                                        -      if(options.group) {
                                        -        options.group = Utils.addTicks(options.group)
                                        -        query += " GROUP BY <%= group %>"
                                        -      }
                                        -
                                        -      if(options.limit && !(options.include && (options.limit === 1))) {
                                        -        if(options.offset) {
                                        -          query += " LIMIT <%= offset %>, <%= limit %>"
                                        -        } else {
                                        -          query += " LIMIT <%= limit %>"
                                        -        }
                                        -      }
                                        -
                                        -      query += ";"
                                        -
                                        -      return Utils._.template(query)(options)
                                        -    },
                                        -
                                        -    insertQuery: function(tableName, attrValueHash) {
                                        -      attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
                                        -
                                        -      var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);"
                                        -
                                        -      var replacements  = {
                                        -        table: Utils.addTicks(tableName),
                                        -        attributes: Utils._.keys(attrValueHash).map(function(attr){return Utils.addTicks(attr)}).join(","),
                                        -        values: Utils._.values(attrValueHash).map(function(value){
                                        -          return Utils.escape((value instanceof Date) ? Utils.toSqlDate(value) : value)
                                        -        }).join(",")
                                        -      }
                                        -
                                        -      return Utils._.template(query)(replacements)
                                        -    },
                                        -
                                        -    updateQuery: function(tableName, attrValueHash, where) {
                                        -      attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
                                        -
                                        -      var query  = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
                                        -        , values = []
                                        -
                                        -      for (var key in attrValueHash) {
                                        -        var value  = attrValueHash[key]
                                        -          , _value = (value instanceof Date) ? Utils.toSqlDate(value) : value
                                        -
                                        -        values.push(Utils.addTicks(key) + "=" + Utils.escape(_value))
                                        -      }
                                        -
                                        -      var replacements = {
                                        -        table: Utils.addTicks(tableName),
                                        -        values: values.join(","),
                                        -        where: QueryGenerator.getWhereConditions(where)
                                        -      }
                                        -
                                        -      return Utils._.template(query)(replacements)
                                        -    },
                                        -
                                        -    deleteQuery: function(tableName, where, options) {
                                        -      options = options || {}
                                        -      options.limit = options.limit || 1
                                        -
                                        -      var query = "DELETE FROM <%= table %> WHERE <%= where %> LIMIT <%= limit %>"
                                        -      var replacements = {
                                        -        table: Utils.addTicks(tableName),
                                        -        where: QueryGenerator.getWhereConditions(where),
                                        -        limit: Utils.escape(options.limit)
                                        -      }
                                        -
                                        -      return Utils._.template(query)(replacements)
                                        -    },
                                        -
                                        -    addIndexQuery: function(tableName, attributes, options) {
                                        -      var transformedAttributes = attributes.map(function(attribute) {
                                        -        if(typeof attribute == 'string')
                                        -          return attribute
                                        -        else {
                                        -          var result = ""
                                        -
                                        -          if(!attribute.attribute)
                                        -            throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute))
                                        -
                                        -          result += attribute.attribute
                                        -
                                        -          if(attribute.length)
                                        -            result += '(' + attribute.length + ')'
                                        -
                                        -          if(attribute.order)
                                        -            result += ' ' + attribute.order
                                        -
                                        -          return result
                                        -        }
                                        -      })
                                        -
                                        -      var onlyAttributeNames = attributes.map(function(attribute) {
                                        -        return (typeof attribute == 'string') ? attribute : attribute.attribute
                                        -      })
                                        -
                                        -      options = Utils._.extend({
                                        -        indicesType: null,
                                        -        indexName: Utils._.underscored(tableName + '_' + onlyAttributeNames.join('_')),
                                        -        parser: null
                                        -      }, options || {})
                                        -
                                        -      return Utils._.compact([
                                        -        "CREATE", options.indicesType, "INDEX", options.indexName,
                                        -        (options.indexType ? ('USING ' + options.indexType) : undefined),
                                        -        "ON", tableName, '(' + transformedAttributes.join(', ') + ')',
                                        -        (options.parser ? "WITH PARSER " + options.parser : undefined)
                                        -      ]).join(' ')
                                        -    },
                                        -
                                        -    showIndexQuery: function(tableName, options) {
                                        -      var sql = "SHOW INDEX FROM <%= tableName %><%= options %>"
                                        -      return Utils._.template(sql)({
                                        -        tableName: tableName,
                                        -        options: (options || {}).database ? ' FROM ' + options.database : ''
                                        -      })
                                        -    },
                                        -
                                        -    removeIndexQuery: function(tableName, indexNameOrAttributes) {
                                        -      var sql       = "DROP INDEX <%= indexName %> ON <%= tableName %>"
                                        -        , indexName = indexNameOrAttributes
                                        -
                                        -      if(typeof indexName != 'string')
                                        -        indexName = Utils._.underscored(tableName + '_' + indexNameOrAttributes.join('_'))
                                        -
                                        -      return Utils._.template(sql)({ tableName: tableName, indexName: indexName })
                                        -    },
                                        -
                                        -    getWhereConditions: function(smth, tableName) {
                                        -      var result = null
                                        -
                                        -      if(Utils.isHash(smth)) {
                                        -        smth   = Utils.prependTableNameToHash(tableName, smth)
                                        -        result = QueryGenerator.hashToWhereConditions(smth)
                                        -      } else if (typeof smth === 'number') {
                                        -        smth   = Utils.prependTableNameToHash(tableName, { id: smth })
                                        -        result = QueryGenerator.hashToWhereConditions(smth)
                                        -      } else if (typeof smth === "string") {
                                        -        result = smth
                                        -      } else if (Array.isArray(smth)) {
                                        -        result = Utils.format(smth)
                                        -      }
                                        -
                                        -      return result
                                        -    },
                                        -
                                        -    hashToWhereConditions: function(hash) {
                                        -      var result = []
                                        -
                                        -      for (var key in hash) {
                                        -        var value = hash[key]
                                        -
                                        -         //handle qualified key names
                                        -        var _key   = key.split('.').map(function(col){return Utils.addTicks(col)}).join(".")
                                        -          , _value = null
                                        -
                                        -        if (Array.isArray(value)) {
                                        -          // is value an array?
                                        -
                                        -          _value = "(" + value.map(function(subValue) {
                                        -            return Utils.escape(subValue);
                                        -          }).join(',') + ")"
                                        -
                                        -          result.push([_key, _value].join(" IN "))
                                        -        } else if ((value) && (typeof value == 'object')) {
                                        -          // is value an object?
                                        -
                                        -          //using as sentinel for join column => value
                                        -          _value = value.join.split('.').map(function(col){ return Utils.addTicks(col) }).join(".")
                                        -          result.push([_key, _value].join("="))
                                        -        } else {
                                        -          _value = Utils.escape(value)
                                        -          result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("="))
                                        -        }
                                        -      }
                                        -
                                        -      return result.join(" AND ")
                                        -    },
                                        -
                                        -    attributesToSQL: function(attributes) {
                                        -      var result = {}
                                        -
                                        -      for (var name in attributes) {
                                        -        var dataType = attributes[name]
                                        -
                                        -        if(Utils.isHash(dataType)) {
                                        -          var template     = "<%= type %>"
                                        -            , replacements = { type: dataType.type }
                                        -
                                        -          if(dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) {
                                        -            template += " NOT NULL"
                                        -          }
                                        -
                                        -          if(dataType.autoIncrement) {
                                        -            template +=" auto_increment"
                                        -          }
                                        -
                                        -          if((dataType.defaultValue != undefined) && (dataType.defaultValue != DataTypes.NOW)) {
                                        -            template += " DEFAULT <%= defaultValue %>"
                                        -            replacements.defaultValue = Utils.escape(dataType.defaultValue)
                                        -          }
                                        -
                                        -          if(dataType.unique) {
                                        -            template += " UNIQUE"
                                        -          }
                                        -
                                        -          if(dataType.primaryKey) {
                                        -            template += " PRIMARY KEY"
                                        -          }
                                        -
                                        -          result[name] = Utils._.template(template)(replacements)
                                        -        } else {
                                        -          result[name] = dataType
                                        -        }
                                        -      }
                                        -
                                        -      return result
                                        -    },
                                        -
                                        -    findAutoIncrementField: function(factory) {
                                        -      var fields = []
                                        -
                                        -      for (var name in factory.attributes) {
                                        -        var definition = factory.attributes[name]
                                        -
                                        -        if (definition && (definition.indexOf('auto_increment') > -1)) {
                                        -          fields.push(name)
                                        -        }
                                        -      }
                                        -
                                        -      return fields
                                        -    }
                                        -  }
                                        -
                                        -  return Utils._.extend(Utils._.clone(require("../query-generator")), QueryGenerator)
                                        -})()
                                        \ No newline at end of file diff --git a/docs/dialects/mysql/query.js.html b/docs/dialects/mysql/query.js.html deleted file mode 100644 index c7cc42b26c67..000000000000 --- a/docs/dialects/mysql/query.js.html +++ /dev/null @@ -1,212 +0,0 @@ -Sequelize

                                        Sequelize

                                        declaration

                                        Utils

                                        Utils
                                          var Utils         = require("../../utils")
                                          -  , AbstractQuery = require('../abstract/query')
                                          -
                                          -module.exports = (function() {
                                          -  var Query = function(client, sequelize, callee, options) {
                                          -    var self = this
                                          -
                                          -    this.client    = client
                                          -    this.callee    = callee
                                          -    this.sequelize = sequelize
                                          -    this.options   = Utils._.extend({
                                          -      logging: console.log,
                                          -      plain: false,
                                          -      raw: false
                                          -    }, options || {})
                                          -
                                          -    if(this.options.logging === true) {
                                          -      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
                                          -      this.options.logging = console.log
                                          -    }
                                          -
                                          -    if(this.options.logging == console.log) {
                                          -      // using just console.log will break in node < 0.6
                                          -      this.options.logging = function(s) { console.log(s) }
                                          -    }
                                          -
                                          -    this.bindClientFunction = function(err) { onFailure.call(self, err) }
                                          -  }
                                          -
                                          -  Utils.inherit(Query, require('../abstract/query'))
                                          -
                                          -  Query.prototype.run = function(sql) {
                                          -    var self = this
                                          -
                                          -    this.sql = sql
                                          -
                                          -    bindClient.call(this)
                                          -
                                          -    if(this.options.logging !== false) {
                                          -      this.options.logging('Executing: ' + this.sql)
                                          -    }
                                          -
                                          -    this.client.query({ sql: this.sql, nestTables: true }, function(err, results, fields) {
                                          -      self.emit('sql', self.sql)
                                          -
                                          -      if (err) {
                                          -        onFailure.call(self, err)
                                          -      } else {
                                          -        if (Array.isArray(results) && !!results[0] && Utils._.keys(results[0]).length === 1) {
                                          -          results = results.map(function(result){ return Utils._.values(result)[0] })
                                          -        }
                                          -
                                          -        onSuccess.call(self, results, fields)
                                          -      }
                                          -    }).setMaxListeners(100)
                                          -
                                          -    return this
                                          -  }
                                          -
                                          -  //private
                                          -
                                          -  var bindClient = function() {
                                          -    this.client.on('error', this.bindClientFunction)
                                          -  }
                                          -
                                          -  var unbindClient = function() {
                                          -    this.client.removeListener('error', this.bindClientFunction)
                                          -  }
                                          -
                                          -  var onSuccess = function(results, fields) {
                                          -    unbindClient.call(this)
                                          -    this.emit('success', this.formatResults(results))
                                          -
                                          -  }
                                          -
                                          -  var onFailure = function(err) {
                                          -    unbindClient.call(this)
                                          -    this.emit('error', err, this.callee)
                                          -  }
                                          -
                                          -  return Query
                                          -})()
                                          \ No newline at end of file diff --git a/docs/dialects/postgres/connector-manager.js.html b/docs/dialects/postgres/connector-manager.js.html deleted file mode 100644 index 61a3be2c6c41..000000000000 --- a/docs/dialects/postgres/connector-manager.js.html +++ /dev/null @@ -1,219 +0,0 @@ -Sequelize

                                          Sequelize

                                          declaration

                                          Query

                                          Query
                                            var Query   = require("./query")
                                            -  , Utils   = require("../../utils")
                                            -  , pg  = require("pg")
                                            -  , without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) }
                                            -
                                            -module.exports = (function() {
                                            -  var ConnectorManager = function(sequelize, config) {
                                            -    this.sequelize = sequelize
                                            -    this.client = null
                                            -    this.config = config || {}
                                            -    this.pooling = (this.config.poolCfg != null && this.config.poolCfg.maxConnections > 0)
                                            -    // set pooling parameters if specified
                                            -    if (this.pooling) {
                                            -      pg.defaults.poolSize = this.config.poolCfg.maxConnections
                                            -      pg.defaults.poolIdleTimeout = this.config.poolCfg.maxIdleTime
                                            -    }
                                            -    this.disconnectTimeoutId = null
                                            -    this.pendingQueries = 0
                                            -    this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
                                            -  }
                                            -  Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
                                            -
                                            -  var isConnecting = false
                                            -  var isConnected  = false
                                            -
                                            -  ConnectorManager.prototype.query = function(sql, callee, options) {
                                            -    var self = this
                                            -    if (this.client == null) this.connect()
                                            -    var query = new Query(this.client, this.sequelize, callee, options || {})
                                            -    self.pendingQueries += 1
                                            -    return query.run(sql)
                                            -      .success(function() { self.endQuery.call(self) })
                                            -      .error(function() { self.endQuery.call(self) })
                                            -  }
                                            -
                                            -  ConnectorManager.prototype.endQuery = function() {
                                            -    var self = this
                                            -    self.pendingQueries -= 1
                                            -    if (self.pendingQueries == 0) {
                                            -      setTimeout(function() {
                                            -        self.pendingQueries == 0 && self.disconnect.call(self)
                                            -      }, 100)
                                            -    }
                                            -  }
                                            -
                                            -  ConnectorManager.prototype.connect = function() {
                                            -    var self = this
                                            -
                                            -    // in case database is slow to connect, prevent orphaning the client
                                            -    if (this.isConnecting) return
                                            -    this.isConnecting = true
                                            -    this.isConnected  = false
                                            -
                                            -    var uri = this.sequelize.getQueryInterface().QueryGenerator.databaseConnectionUri(this.config)
                                            -
                                            -    var connectCallback = function(err, client) {
                                            -      self.isConnecting = false
                                            -      if (!err && client) {
                                            -        client.query("SET TIME ZONE 'UTC'")
                                            -          .on('end', function() {
                                            -            self.isConnected = true
                                            -            this.client = client
                                            -          });
                                            -      } else {
                                            -        this.client = null
                                            -      }
                                            -    }
                                            -
                                            -    if (this.pooling) {
                                            -      // acquire client from pool
                                            -      pg.connect(uri, connectCallback)
                                            -
                                            -    } else {
                                            -      //create one-off client
                                            -      this.client = new pg.Client(uri)
                                            -      this.client.connect(connectCallback)
                                            -    }
                                            -  }
                                            -
                                            -  ConnectorManager.prototype.disconnect = function() {
                                            -    var self = this
                                            -    if (this.client) this.client.end()
                                            -    this.client = null
                                            -    this.isConnecting = false
                                            -    this.isConnected  = false
                                            -  }
                                            -
                                            -  return ConnectorManager
                                            -})()
                                            \ No newline at end of file diff --git a/docs/dialects/postgres/query-generator.js.html b/docs/dialects/postgres/query-generator.js.html deleted file mode 100644 index 95a3629eb026..000000000000 --- a/docs/dialects/postgres/query-generator.js.html +++ /dev/null @@ -1,576 +0,0 @@ -Sequelize

                                            Sequelize

                                            declaration

                                            Utils

                                            Utils
                                              var Utils = require("../../utils")
                                              -  , util  = require("util")
                                              -  , tables = {}
                                              -  , primaryKeys = {};
                                              -
                                              -function removeQuotes(s, quoteChar) {
                                              -  quoteChar = quoteChar || '"'
                                              -  return s.replace(new RegExp(quoteChar, 'g'), '')
                                              -}
                                              -
                                              -function addQuotes(s, quoteChar) {
                                              -  quoteChar = quoteChar || '"'
                                              -  return quoteChar + removeQuotes(s) + quoteChar
                                              -}
                                              -
                                              -function pgEscape(s) {
                                              -  s = Utils.escape(s)
                                              -
                                              -  if (typeof s == 'string') {
                                              -    // http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
                                              -    s = s.replace(/\\'/g, "''")
                                              -  }
                                              -
                                              -  return s
                                              -}
                                              -
                                              -function padInt(i) {
                                              -  return (i < 10) ? '0' + i.toString() : i.toString()
                                              -}
                                              -function pgSqlDate(dt) {
                                              -  var date = [ dt.getUTCFullYear(), padInt(dt.getUTCMonth()+1), padInt(dt.getUTCDate()) ].join('-')
                                              -  var time = [ dt.getUTCHours(), padInt(dt.getUTCMinutes()), padInt(dt.getUTCSeconds())].join(':')
                                              -  return date + ' ' + time + '.' + ((dt.getTime() % 1000) * 1000)
                                              -}
                                              -
                                              -function pgDataTypeMapping(tableName, attr, dataType) {
                                              -  if (Utils._.includes(dataType, 'PRIMARY KEY')) {
                                              -    primaryKeys[tableName].push(attr)
                                              -    dataType = dataType.replace(/PRIMARY KEY/, '')
                                              -  }
                                              -
                                              -  if (Utils._.includes(dataType, 'TINYINT(1)')) {
                                              -    dataType = dataType.replace(/TINYINT\(1\)/, 'BOOLEAN')
                                              -  }
                                              -
                                              -  if (Utils._.includes(dataType, 'DATETIME')) {
                                              -    dataType = dataType.replace(/DATETIME/, 'TIMESTAMP')
                                              -  }
                                              -
                                              -  if (Utils._.includes(dataType, 'SERIAL')) {
                                              -    dataType = dataType.replace(/INTEGER/, '')
                                              -    dataType = dataType.replace(/NOT NULL/, '')
                                              -    tables[tableName][attr] = 'serial'
                                              -  }
                                              -
                                              -  return dataType
                                              -}
                                              -
                                              -module.exports = (function() {
                                              -  var QueryGenerator = {
                                              -    options: {},
                                              -
                                              -    createTableQuery: function(tableName, attributes, options) {
                                              -      options = Utils._.extend({
                                              -      }, options || {})
                                              -
                                              -      primaryKeys[tableName] = []
                                              -      tables[tableName] = {}
                                              -
                                              -      var query   = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)"
                                              -        , attrStr = []
                                              -
                                              -      for (var attr in attributes) {
                                              -        var dataType = pgDataTypeMapping(tableName, attr, attributes[attr])
                                              -        attrStr.push(addQuotes(attr) + " " + dataType)
                                              -      }
                                              -
                                              -      var values  = {
                                              -        table: addQuotes(tableName),
                                              -        attributes: attrStr.join(", "),
                                              -      }
                                              -
                                              -      var pks = primaryKeys[tableName].map(function(pk){ return addQuotes(pk) }).join(",")
                                              -      if (pks.length > 0) {
                                              -        values.attributes += ", PRIMARY KEY (" + pks + ")"
                                              -      }
                                              -
                                              -      return Utils._.template(query)(values).trim() + ";"
                                              -    },
                                              -
                                              -    dropTableQuery: function(tableName, options) {
                                              -      options = options || {}
                                              -      var query = "DROP TABLE IF EXISTS <%= table %>;"
                                              -      return Utils._.template(query)({table: addQuotes(tableName)})
                                              -    },
                                              -
                                              -    renameTableQuery: function(before, after) {
                                              -      var query = "ALTER TABLE <%= before %> RENAME TO <%= after %>;"
                                              -      return Utils._.template(query)({ before: addQuotes(before), after: addQuotes(after) })
                                              -    },
                                              -
                                              -    showTablesQuery: function() {
                                              -      return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';"
                                              -    },
                                              -
                                              -    describeTableQuery: function(tableName) {
                                              -      var query = 'SELECT column_name as "Field", column_default as "Default", is_nullable as "Null", data_type as "Type" FROM information_schema.columns WHERE table_name = <%= table %>;'
                                              -      return Utils._.template(query)({ table: addQuotes(tableName, "'") })
                                              -    },
                                              -
                                              -    addColumnQuery: function(tableName, attributes) {
                                              -      var query      = "ALTER TABLE <%= tableName %> ADD COLUMN <%= attributes %>;"
                                              -        , attrString = []
                                              -
                                              -      for (var attrName in attributes) {
                                              -        var definition = attributes[attrName]
                                              -
                                              -        attrString.push(Utils._.template('<%= attrName %> <%= definition %>')({
                                              -          attrName: addQuotes(attrName),
                                              -          definition: pgDataTypeMapping(tableName, attrName, definition)
                                              -        }))
                                              -      }
                                              -
                                              -      return Utils._.template(query)({ tableName: addQuotes(tableName), attributes: attrString.join(', ') })
                                              -    },
                                              -
                                              -    removeColumnQuery: function(tableName, attributeName) {
                                              -      var query = "ALTER TABLE <%= tableName %> DROP COLUMN <%= attributeName %>;"
                                              -      return Utils._.template(query)({ tableName: addQuotes(tableName), attributeName: addQuotes(attributeName) })
                                              -    },
                                              -
                                              -    changeColumnQuery: function(tableName, attributes) {
                                              -      var query = "ALTER TABLE <%= tableName %> ALTER COLUMN <%= query %>;"
                                              -        , sql   = []
                                              -
                                              -      for (var attributeName in attributes) {
                                              -        var attrSql = ''
                                              -
                                              -        if (definition.indexOf('NOT NULL') > 0) {
                                              -          attrSql += Utils._.template(query)({
                                              -            tableName: addQuotes(tableName),
                                              -            query: addQuotes(attributeName) + ' SET NOT NULL'
                                              -          })
                                              -          definition = definition.replace('NOT NULL', '').trim()
                                              -        } else {
                                              -          attrSql += Utils._.template(query)({
                                              -            tableName: addQuotes(tableName),
                                              -            query: addQuotes(attributeName) + ' DROP NOT NULL'
                                              -          })
                                              -        }
                                              -
                                              -        attrSql += Utils._.template(query)({
                                              -          tableName: addQuotes(tableName),
                                              -          query: addQuotes(attributeName) + ' TYPE ' + definition
                                              -        })
                                              -
                                              -        sql.push(attrSql)
                                              -      }
                                              -
                                              -      return sql.join('')
                                              -    },
                                              -
                                              -    renameColumnQuery: function(tableName, attrBefore, attributes) {
                                              -      var query      = "ALTER TABLE <%= tableName %> RENAME COLUMN <%= attributes %>;"
                                              -      var attrString = []
                                              -
                                              -      for (var attributeName in attributes) {
                                              -        attrString.push(Utils._.template('<%= before %> TO <%= after %>')({
                                              -          before: addQuotes(attrBefore),
                                              -          after: addQuotes(attributeName),
                                              -        }))
                                              -      }
                                              -
                                              -      return Utils._.template(query)({ tableName: addQuotes(tableName), attributes: attrString.join(', ') })
                                              -    },
                                              -
                                              -    selectQuery: function(tableName, options) {
                                              -      options = options || {}
                                              -      options.table = Array.isArray(tableName) ? tableName.map(function(t){return addQuotes(t);}).join(", ") : addQuotes(tableName)
                                              -      options.attributes = options.attributes && options.attributes.map(function(attr){
                                              -        if(Array.isArray(attr) && attr.length == 2) {
                                              -          return [attr[0], addQuotes(removeQuotes(attr[1], '`'))].join(' as ')
                                              -        } else if (attr.indexOf('`') >= 0) {
                                              -          return attr.replace(/`/g, '"')
                                              -        } else {
                                              -          return addQuotes(attr)
                                              -        }
                                              -      }).join(", ")
                                              -
                                              -      options.attributes = options.attributes || '*'
                                              -
                                              -      var query = "SELECT <%= attributes %> FROM <%= table %>"
                                              -
                                              -      if(options.where) {
                                              -        options.where = QueryGenerator.getWhereConditions(options.where)
                                              -        query += " WHERE <%= where %>"
                                              -      }
                                              -      if(options.order) {
                                              -        options.order = options.order.replace(/([^ ]+)(.*)/, function(m, g1, g2) { return addQuotes(g1)+g2 })
                                              -        query += " ORDER BY <%= order %>"
                                              -      }
                                              -      if(options.group) {
                                              -        options.group = addQuotes(options.group)
                                              -        query += " GROUP BY <%= group %>"
                                              -      }
                                              -      if(options.limit) query += " LIMIT <%= limit %>"
                                              -      if(options.offset) query += " OFFSET <%= offset %>"
                                              -
                                              -      query += ";"
                                              -      return Utils._.template(query)(options)
                                              -    },
                                              -
                                              -    insertQuery: function(tableName, attrValueHash) {
                                              -      attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
                                              -
                                              -      var query     = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;"
                                              -        , returning = []
                                              -
                                              -      Utils._.forEach(attrValueHash, function(value, key, hash) {
                                              -        if (tables[tableName] && tables[tableName][key]) {
                                              -          switch (tables[tableName][key]) {
                                              -            case 'serial':
                                              -              delete hash[key]
                                              -              returning.push(key)
                                              -              break
                                              -          }
                                              -        }
                                              -      });
                                              -
                                              -      var replacements  = {
                                              -        table: addQuotes(tableName),
                                              -        attributes: Utils._.keys(attrValueHash).map(function(attr){return addQuotes(attr)}).join(","),
                                              -        values: Utils._.values(attrValueHash).map(function(value){
                                              -          return pgEscape((value instanceof Date) ? pgSqlDate(value) : value)
                                              -        }).join(",")
                                              -      }
                                              -
                                              -      return Utils._.template(query)(replacements)
                                              -    },
                                              -
                                              -    updateQuery: function(tableName, attrValueHash, where) {
                                              -      attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
                                              -
                                              -      var query  = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
                                              -        , values = []
                                              -
                                              -      for (var key in attrValueHash) {
                                              -        var value = attrValueHash[key]
                                              -        values.push(addQuotes(key) + "=" + pgEscape((value instanceof Date) ? pgSqlDate(value) : value))
                                              -      }
                                              -
                                              -      var replacements = {
                                              -        table: addQuotes(tableName),
                                              -        values: values.join(","),
                                              -        where: QueryGenerator.getWhereConditions(where)
                                              -      }
                                              -
                                              -      return Utils._.template(query)(replacements)
                                              -    },
                                              -
                                              -    deleteQuery: function(tableName, where, options) {
                                              -      options = options || {}
                                              -      options.limit = options.limit || 1
                                              -
                                              -      primaryKeys[tableName] = primaryKeys[tableName] || [];
                                              -
                                              -      var query = "DELETE FROM <%= table %> WHERE <%= primaryKeys %> IN (SELECT <%= primaryKeysSelection %> FROM <%= table %> WHERE <%= where %> LIMIT <%= limit %>)"
                                              -
                                              -      var pks;
                                              -      if (primaryKeys[tableName] && primaryKeys[tableName].length > 0) {
                                              -        pks = primaryKeys[tableName].map(function(pk) { return addQuotes(pk) }).join(',')
                                              -      } else {
                                              -        pks = addQuotes('id')
                                              -      }
                                              -
                                              -      var replacements = {
                                              -        table: addQuotes(tableName),
                                              -        where: QueryGenerator.getWhereConditions(where),
                                              -        limit: pgEscape(options.limit),
                                              -        primaryKeys: primaryKeys[tableName].length > 1 ? '(' + pks + ')' : pks,
                                              -        primaryKeysSelection: pks
                                              -      }
                                              -
                                              -      return Utils._.template(query)(replacements)
                                              -    },
                                              -
                                              -    addIndexQuery: function(tableName, attributes, options) {
                                              -      var transformedAttributes = attributes.map(function(attribute) {
                                              -        if(typeof attribute == 'string')
                                              -          return addQuotes(attribute)
                                              -        else {
                                              -          var result = ""
                                              -
                                              -          if(!attribute.attribute)
                                              -            throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute))
                                              -
                                              -          result += addQuotes(attribute.attribute)
                                              -
                                              -          if(attribute.length)
                                              -            result += '(' + attribute.length + ')'
                                              -
                                              -          if(attribute.order)
                                              -            result += ' ' + attribute.order
                                              -
                                              -          return result
                                              -        }
                                              -      })
                                              -
                                              -      var onlyAttributeNames = attributes.map(function(attribute) {
                                              -        return (typeof attribute == 'string') ? attribute : attribute.attribute
                                              -      })
                                              -
                                              -      options = Utils._.extend({
                                              -        indicesType: null,
                                              -        indexName: Utils._.underscored(tableName + '_' + onlyAttributeNames.join('_')),
                                              -        parser: null
                                              -      }, options || {})
                                              -
                                              -      return Utils._.compact([
                                              -        "CREATE", options.indicesType, "INDEX", addQuotes(options.indexName),
                                              -        (options.indexType ? ('USING ' + options.indexType) : undefined),
                                              -        "ON", addQuotes(tableName), '(' + transformedAttributes.join(', ') + ')'
                                              -      ]).join(' ')
                                              -    },
                                              -
                                              -    showIndexQuery: function(tableName, options) {
                                              -      var query = "SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname='<%= tableName %>' AND pg_class.oid=pg_index.indrelid);"
                                              -      return Utils._.template(query)({ tableName: tableName });
                                              -    },
                                              -
                                              -    removeIndexQuery: function(tableName, indexNameOrAttributes) {
                                              -      var sql       = "DROP INDEX IF EXISTS <%= indexName %>"
                                              -        , indexName = indexNameOrAttributes
                                              -
                                              -      if(typeof indexName != 'string')
                                              -        indexName = Utils._.underscored(tableName + '_' + indexNameOrAttributes.join('_'))
                                              -
                                              -      return Utils._.template(sql)({ tableName: addQuotes(tableName), indexName: addQuotes(indexName) })
                                              -    },
                                              -
                                              -    getWhereConditions: function(smth) {
                                              -      var result = null
                                              -
                                              -      if(Utils.isHash(smth))
                                              -        result = QueryGenerator.hashToWhereConditions(smth)
                                              -      else if(typeof smth == 'number')
                                              -        result = '\"id\"' + "=" + pgEscape(smth)
                                              -      else if(typeof smth == "string")
                                              -        result = smth
                                              -      else if(Array.isArray(smth))
                                              -        result = Utils.format(smth)
                                              -
                                              -      return result
                                              -    },
                                              -
                                              -    hashToWhereConditions: function(hash) {
                                              -      var result = []
                                              -
                                              -      for (var key in hash) {
                                              -        var value = hash[key]
                                              -
                                              -        //handle qualified key names
                                              -        var _key   = key.split('.').map(function(col){return addQuotes(col)}).join(".")
                                              -          , _value = null
                                              -
                                              -        if(Array.isArray(value)) {
                                              -          _value = "(" + value.map(function(subValue) {
                                              -            return pgEscape(subValue);
                                              -          }).join(',') + ")"
                                              -
                                              -          result.push([_key, _value].join(" IN "))
                                              -        }
                                              -        else if ((value) && (typeof value == 'object')) {
                                              -          //using as sentinel for join column => value
                                              -          _value = value.join.split('.').map(function(col){return addQuotes(col)}).join(".")
                                              -          result.push([_key, _value].join("="))
                                              -        } else {
                                              -          _value = pgEscape(value)
                                              -          result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("="))
                                              -        }
                                              -      }
                                              -
                                              -      return result.join(' AND ')
                                              -    },
                                              -
                                              -    attributesToSQL: function(attributes) {
                                              -      var result = {}
                                              -
                                              -      for (var name in attributes) {
                                              -        var dataType = attributes[name]
                                              -
                                              -        if(Utils.isHash(dataType)) {
                                              -          var template     = "<%= type %>"
                                              -            , replacements = { type: dataType.type }
                                              -
                                              -          if(dataType.type == 'TINYINT(1)') dataType.type = 'BOOLEAN'
                                              -          if(dataType.type == 'DATETIME') dataType.type = 'TIMESTAMP'
                                              -
                                              -          if(dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) template += " NOT NULL"
                                              -          if(dataType.autoIncrement) template +=" SERIAL"
                                              -          if(dataType.defaultValue != undefined) {
                                              -            template += " DEFAULT <%= defaultValue %>"
                                              -            replacements.defaultValue = pgEscape(dataType.defaultValue)
                                              -          }
                                              -          if(dataType.unique) template += " UNIQUE"
                                              -          if(dataType.primaryKey) template += " PRIMARY KEY"
                                              -
                                              -          result[name] = Utils._.template(template)(replacements)
                                              -        } else {
                                              -          result[name] = dataType
                                              -        }
                                              -      }
                                              -
                                              -      return result
                                              -    },
                                              -
                                              -    findAutoIncrementField: function(factory) {
                                              -      var fields = []
                                              -
                                              -      for (var name in factory.attributes) {
                                              -        var definition = factory.attributes[name]
                                              -
                                              -        if (definition && (definition.indexOf('SERIAL') > -1)) {
                                              -          fields.push(name)
                                              -        }
                                              -      }
                                              -
                                              -      return fields
                                              -    },
                                              -
                                              -    databaseConnectionUri: function(config) {
                                              -      var template = '<%= protocol %>://<%= user %>:<%= password %>@<%= host %><% if(port) { %>:<%= port %><% } %>/<%= database %>';
                                              -
                                              -      return Utils._.template(template)({
                                              -        user: config.username,
                                              -        password: config.password,
                                              -        database: config.database,
                                              -        host: config.host,
                                              -        port: config.port,
                                              -        protocol: config.protocol
                                              -      })
                                              -    }
                                              -  }
                                              -
                                              -  return Utils._.extend(Utils._.clone(require("../query-generator")), QueryGenerator)
                                              -})()
                                              \ No newline at end of file diff --git a/docs/dialects/postgres/query.js.html b/docs/dialects/postgres/query.js.html deleted file mode 100644 index 91f09e1b2464..000000000000 --- a/docs/dialects/postgres/query.js.html +++ /dev/null @@ -1,227 +0,0 @@ -Sequelize

                                              Sequelize

                                              declaration

                                              Utils

                                              Utils
                                                var Utils         = require("../../utils")
                                                -  , AbstractQuery = require('../abstract/query')
                                                -
                                                -module.exports = (function() {
                                                -  var Query = function(client, sequelize, callee, options) {
                                                -    var self = this
                                                -
                                                -    this.client = client
                                                -    this.sequelize = sequelize
                                                -    this.callee = callee
                                                -    this.options = Utils._.extend({
                                                -      logging: console.log,
                                                -      plain: false,
                                                -      raw: false
                                                -    }, options || {})
                                                -
                                                -    if(this.options.logging === true) {
                                                -      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
                                                -      this.options.logging = console.log
                                                -    }
                                                -
                                                -    if(this.options.logging == console.log) {
                                                -      // using just console.log will break in node < 0.6
                                                -      this.options.logging = function(s) { console.log(s) }
                                                -    }
                                                -  }
                                                -  Utils.inherit(Query, AbstractQuery)
                                                -
                                                -  Query.prototype.run = function(sql) {
                                                -    var self = this
                                                -
                                                -    this.sql = sql
                                                -
                                                -    if(this.options.logging !== false) {
                                                -      this.options.logging('Executing: ' + this.sql)
                                                -    }
                                                -
                                                -    var results = [];
                                                -    var receivedError = false;
                                                -
                                                -    var query = this.client.query(sql)
                                                -    query.on('row', function(row) {
                                                -      if (self.callee && (self.sql.indexOf('INSERT INTO') == 0 || self.sql.indexOf('UPDATE') == 0)) {
                                                -        Utils._.forEach(row, function(value, key) {
                                                -          self.callee[key] = value
                                                -        })
                                                -        results.push(self.callee)
                                                -      }
                                                -
                                                -      if (self.sql.indexOf('SELECT table_name FROM information_schema.tables') == 0) {
                                                -        results.push(Utils._.values(row))
                                                -      } else if (self.sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') == 0) {
                                                -        results.push(Utils._.values(row))
                                                -      } else if (self.sql.indexOf('SELECT') == 0) {
                                                -        // transform results into real model instances
                                                -        // return the first real model instance if options.plain is set (e.g. Model.find)
                                                -        if (self.options.raw) {
                                                -          results.push(row);
                                                -        } else {
                                                -          results.push(self.callee.build(row, { isNewRecord: false }))
                                                -        }
                                                -      } else if((self.sql.indexOf('SHOW') == 0) || (self.sql.indexOf('DESCRIBE') == 0)) {
                                                -        results.push(row)
                                                -      }
                                                -    });
                                                -
                                                -    query.on('end', function() {
                                                -      self.emit('sql', self.sql)
                                                -      if (receivedError) return;
                                                -
                                                -      if (self.sql.indexOf('SELECT') == 0) {
                                                -        if (self.options.plain) {
                                                -          self.emit('success', (results.length == 0) ? null : results[0])
                                                -        } else {
                                                -          self.emit('success', results)
                                                -        }
                                                -      } else if((self.sql.indexOf('SHOW') == 0) || (self.sql.indexOf('DESCRIBE') == 0)) {
                                                -        self.emit('success', results)
                                                -      } else if (self.sql.indexOf('INSERT INTO') == 0) {
                                                -        self.emit('success', results[0])
                                                -      } else if (self.sql.indexOf('UPDATE') == 0) {
                                                -        self.emit('success', self.callee)
                                                -      } else {
                                                -        self.emit('success', results)
                                                -      }
                                                -    });
                                                -
                                                -    query.on('error', function(err) {
                                                -      receivedError = true
                                                -      self.emit('error', err, self.callee)
                                                -    });
                                                -
                                                -    return this
                                                -  }
                                                -
                                                -  return Query
                                                -})()
                                                \ No newline at end of file diff --git a/docs/dialects/query-generator.js.html b/docs/dialects/query-generator.js.html deleted file mode 100644 index 3ebd02f14d9b..000000000000 --- a/docs/dialects/query-generator.js.html +++ /dev/null @@ -1,177 +0,0 @@ -Sequelize

                                                Sequelize

                                                Returns a query for creating a table.
                                                Parameters:
                                                - tableName: Name of the new table.
                                                - attributes: An object with containing attribute-attributeType-pairs.
                                                Attributes should have the format:
                                                {attributeName: type, attr2: type2}
                                                --> e.g. {title: 'VARCHAR(255)'}
                                                - options: An object with options.
                                                Defaults: { engine: 'InnoDB', charset: null }

                                                  createTableQuery: function(tableName, attributes, options) {
                                                  -      throwMethodUndefined('createTableQuery')
                                                  -    },

                                                  Returns a query for dropping a table.

                                                    dropTableQuery: function(tableName, options) {
                                                    -      throwMethodUndefined('dropTableQuery')
                                                    -    },

                                                    Returns a rename table query.
                                                    Parameters:
                                                    - originalTableName: Name of the table before execution.
                                                    - futureTableName: Name of the table after execution.

                                                      renameTableQuery: function(originalTableName, futureTableName) {
                                                      -      throwMethodUndefined('renameTableQuery')
                                                      -    },

                                                      Returns a query, which gets all available table names in the database.

                                                        showTablesQuery: function() {
                                                        -      throwMethodUndefined('showTablesQuery')
                                                        -    },

                                                        Returns a query, which adds an attribute to an existing table.
                                                        Parameters:
                                                        - tableName: Name of the existing table.
                                                        - attributes: A hash with attribute-attributeOptions-pairs.
                                                        - key: attributeName
                                                        - value: A hash with attribute specific options:
                                                        - type: DataType
                                                        - defaultValue: A String with the default value
                                                        - allowNull: Boolean

                                                          addColumnQuery: function(tableName, attributes) {
                                                          -      throwMethodUndefined('addColumnQuery')
                                                          -    },

                                                          Returns a query, which removes an attribute from an existing table.
                                                          Parameters:
                                                          - tableName: Name of the existing table
                                                          - attributeName: Name of the obsolete attribute.

                                                            removeColumnQuery: function(tableName, attributeName) {
                                                            -      throwMethodUndefined('removeColumnQuery')
                                                            -    },

                                                            Returns a query, which modifies an existing attribute from a table.
                                                            Parameters:
                                                            - tableName: Name of the existing table.
                                                            - attributes: A hash with attribute-attributeOptions-pairs.
                                                            - key: attributeName
                                                            - value: A hash with attribute specific options:
                                                            - type: DataType
                                                            - defaultValue: A String with the default value
                                                            - allowNull: Boolean

                                                              changeColumnQuery: function(tableName, attributes) {
                                                              -      throwMethodUndefined('changeColumnQuery')
                                                              -    },

                                                              Returns a query, which renames an existing attribute.
                                                              Parameters:
                                                              - tableName: Name of an existing table.
                                                              - attrNameBefore: The name of the attribute, which shall be renamed.
                                                              - attrNameAfter: The name of the attribute, after renaming.

                                                                renameColumnQuery: function(tableName, attrNameBefore, attrNameAfter) {
                                                                -      throwMethodUndefined('renameColumnQuery')
                                                                -    },

                                                                Returns a query for selecting elements in the table .
                                                                Options:
                                                                - attributes -> An array of attributes (e.g. ['name', 'birthday']). Default: *
                                                                - where -> A hash with conditions (e.g. {name: 'foo'})
                                                                OR an ID as integer
                                                                OR a string with conditions (e.g. 'name="foo"').
                                                                If you use a string, you have to escape it on your own.
                                                                - order -> e.g. 'id DESC'
                                                                - group
                                                                - limit -> The maximum count you want to get.
                                                                - offset -> An offset value to start from. Only useable with limit!

                                                                  selectQuery: function(tableName, options) {
                                                                  -      throwMethodUndefined('selectQuery')
                                                                  -    },

                                                                  Returns an insert into command. Parameters: table name + hash of attribute-value-pairs.

                                                                    insertQuery: function(tableName, attrValueHash) {
                                                                    -      throwMethodUndefined('insertQuery')
                                                                    -    },

                                                                    Returns an update query.
                                                                    Parameters:
                                                                    - tableName -> Name of the table
                                                                    - values -> A hash with attribute-value-pairs
                                                                    - where -> A hash with conditions (e.g. {name: 'foo'})
                                                                    OR an ID as integer
                                                                    OR a string with conditions (e.g. 'name="foo"').
                                                                    If you use a string, you have to escape it on your own.

                                                                      updateQuery: function(tableName, values, where) {
                                                                      -      throwMethodUndefined('updateQuery')
                                                                      -    },

                                                                      Returns a deletion query.
                                                                      Parameters:
                                                                      - tableName -> Name of the table
                                                                      - where -> A hash with conditions (e.g. {name: 'foo'})
                                                                      OR an ID as integer
                                                                      OR a string with conditions (e.g. 'name="foo"').
                                                                      If you use a string, you have to escape it on your own.
                                                                      Options:
                                                                      - limit -> Maximaum count of lines to delete

                                                                        deleteQuery: function(tableName, where, options) {
                                                                        -      throwMethodUndefined('deleteQuery')
                                                                        -    },

                                                                        Returns an add index query.
                                                                        Parameters:
                                                                        - tableName -> Name of an existing table.
                                                                        - attributes:
                                                                        An array of attributes as string or as hash.
                                                                        If the attribute is a hash, it must have the following content:
                                                                        - attribute: The name of the attribute/column
                                                                        - length: An integer. Optional
                                                                        - order: 'ASC' or 'DESC'. Optional
                                                                        - options:
                                                                        - indicesType: UNIQUE|FULLTEXT|SPATIAL
                                                                        - indexName: The name of the index. Default is
                                                                        - parser

                                                                          addIndexQuery: function(tableName, attributes, options) {
                                                                          -      throwMethodUndefined('addIndexQuery')
                                                                          -    },

                                                                          Returns an show index query.
                                                                          Parameters:
                                                                          - tableName: Name of an existing table.
                                                                          - options:
                                                                          - database: Name of the database.

                                                                            showIndexQuery: function(tableName, options) {
                                                                            -      throwMethodUndefined('showIndexQuery')
                                                                            -    },

                                                                            Returns a remove index query.
                                                                            Parameters:
                                                                            - tableName: Name of an existing table.
                                                                            - indexNameOrAttributes: The name of the index as string or an array of attribute names.

                                                                              removeIndexQuery: function(tableName, indexNameOrAttributes) {
                                                                              -      throwMethodUndefined('removeIndexQuery')
                                                                              -    },

                                                                              Takes something and transforms it into values of a where condition.

                                                                                getWhereConditions: function(smth) {
                                                                                -      throwMethodUndefined('getWhereConditions')
                                                                                -    },

                                                                                Takes a hash and transforms it into a mysql where condition: {key: value, key2: value2} ==> key=value AND key2=value2
                                                                                The values are transformed by the relevant datatype.

                                                                                  hashToWhereConditions: function(hash) {
                                                                                  -      throwMethodUndefined('hashToWhereConditions')
                                                                                  -    },

                                                                                  This method transforms an array of attribute hashes into equivalent
                                                                                  sql attribute definition.

                                                                                    attributesToSQL: function(attributes) {
                                                                                    -      throwMethodUndefined('attributesToSQL')
                                                                                    -    },

                                                                                    Returns all auto increment fields of a factory.

                                                                                      findAutoIncrementField: function(factory) {
                                                                                      -      throwMethodUndefined('findAutoIncrementField')
                                                                                      -    }
                                                                                      -  }
                                                                                      -
                                                                                      -  var throwMethodUndefined = function(methodName) {
                                                                                      -    throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.')
                                                                                      -  }
                                                                                      -
                                                                                      -  return QueryGenerator
                                                                                      -})()
                                                                                      \ No newline at end of file diff --git a/docs/dialects/query.js.html b/docs/dialects/query.js.html deleted file mode 100644 index ce09e58f7558..000000000000 --- a/docs/dialects/query.js.html +++ /dev/null @@ -1,216 +0,0 @@ -Documentation

                                                                                      Documentation

                                                                                      ./lib/dialects/query.js

                                                                                      declaration

                                                                                      Utils

                                                                                      Utils
                                                                                        var Utils = require("../utils")
                                                                                        -
                                                                                        -module.exports = (function() {
                                                                                        -  var Query = function(database, sequelize, callee, options) {
                                                                                        -    throw new Error('Constructor was not overwritten!')
                                                                                        -  }
                                                                                        -  Utils._.extend(Query.prototype, require("../emitters/custom-event-emitter").prototype)
                                                                                        -
                                                                                        -  Query.prototype.run = function(sql) {
                                                                                        -    throw new Error("The run method wasn't overwritten!")
                                                                                        -  }
                                                                                        -
                                                                                        -  Query.prototype.success = Query.prototype.ok = function(fct) {
                                                                                        -    this.on('success', fct)
                                                                                        -    return this
                                                                                        -  }
                                                                                        -
                                                                                        -  Query.prototype.failure = Query.prototype.fail = Query.prototype.error = function(fct) {
                                                                                        -    this.on('error', fct)
                                                                                        -    return this
                                                                                        -  }
                                                                                        -
                                                                                        -  return Query
                                                                                        -})()
                                                                                        \ No newline at end of file diff --git a/docs/dialects/sqlite/connector-manager.js.html b/docs/dialects/sqlite/connector-manager.js.html deleted file mode 100644 index 31a9cf651a14..000000000000 --- a/docs/dialects/sqlite/connector-manager.js.html +++ /dev/null @@ -1,147 +0,0 @@ -Sequelize

                                                                                        Sequelize

                                                                                        declaration

                                                                                        Utils

                                                                                        Utils
                                                                                          var Utils   = require("../../utils")
                                                                                          -  , sqlite3 = require('sqlite3').verbose()
                                                                                          -  , Query   = require("./query")
                                                                                          -
                                                                                          -module.exports = (function() {
                                                                                          -  var ConnectorManager = function(sequelize) {
                                                                                          -    this.sequelize = sequelize
                                                                                          -    this.database  = new sqlite3.Database(sequelize.options.storage || ':memory:')
                                                                                          -  }
                                                                                          -  Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
                                                                                          -
                                                                                          -  ConnectorManager.prototype.query = function(sql, callee, options) {
                                                                                          -    return new Query(this.database, this.sequelize, callee, options).run(sql)
                                                                                          -  }
                                                                                          -
                                                                                          -  return ConnectorManager
                                                                                          -})()
                                                                                          \ No newline at end of file diff --git a/docs/dialects/sqlite/query-generator.js.html b/docs/dialects/sqlite/query-generator.js.html deleted file mode 100644 index 553644a2df58..000000000000 --- a/docs/dialects/sqlite/query-generator.js.html +++ /dev/null @@ -1,279 +0,0 @@ -Sequelize

                                                                                          Sequelize

                                                                                          declaration

                                                                                          Utils

                                                                                          Utils
                                                                                            var Utils = require("../../utils")
                                                                                            -  , util  = require("util")
                                                                                            -
                                                                                            -var escape = function(str) {
                                                                                            -  if (typeof str === 'string') {
                                                                                            -    return "'" + str.replace(/'/g, "''") + "'";
                                                                                            -  } else if (typeof str === 'boolean') {
                                                                                            -    return str ? 1 : 0; // SQLite has no type boolean
                                                                                            -  } else if (str === null || str === undefined) {
                                                                                            -    return 'NULL';
                                                                                            -  } else {
                                                                                            -    return str;
                                                                                            -  }
                                                                                            -};
                                                                                            -
                                                                                            -module.exports = (function() {
                                                                                            -  var QueryGenerator = {
                                                                                            -    options: {},
                                                                                            -
                                                                                            -    createTableQuery: function(tableName, attributes, options) {
                                                                                            -      options = options || {}
                                                                                            -
                                                                                            -      var query   = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)"
                                                                                            -        , attrStr = []
                                                                                            -
                                                                                            -      for (var attr in attributes) {
                                                                                            -        var dataType = attributes[attr]
                                                                                            -
                                                                                            -        if (Utils._.includes(dataType, 'PRIMARY KEY')) {
                                                                                            -          attrStr.push(Utils.addTicks(attr) + " " + dataType)
                                                                                            -        } else {
                                                                                            -          attrStr.push(Utils.addTicks(attr) + " " + dataType)
                                                                                            -        }
                                                                                            -      }
                                                                                            -
                                                                                            -      var values = {
                                                                                            -        table: Utils.addTicks(tableName),
                                                                                            -        attributes: attrStr.join(", "),
                                                                                            -        charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "")
                                                                                            -      }
                                                                                            -
                                                                                            -      return Utils._.template(query)(values).trim() + ";"
                                                                                            -    },
                                                                                            -
                                                                                            -    showTablesQuery: function() {
                                                                                            -      return "SELECT name FROM sqlite_master WHERE type='table';"
                                                                                            -    },
                                                                                            -
                                                                                            -    insertQuery: function(tableName, attrValueHash) {
                                                                                            -      attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
                                                                                            -
                                                                                            -      var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);";
                                                                                            -
                                                                                            -      var replacements  = {
                                                                                            -        table: Utils.addTicks(tableName),
                                                                                            -        attributes: Utils._.keys(attrValueHash).map(function(attr){return Utils.addTicks(attr)}).join(","),
                                                                                            -        values: Utils._.values(attrValueHash).map(function(value){
                                                                                            -          return escape((value instanceof Date) ? Utils.toSqlDate(value) : value)
                                                                                            -        }).join(",")
                                                                                            -      }
                                                                                            -
                                                                                            -      return Utils._.template(query)(replacements)
                                                                                            -    },
                                                                                            -
                                                                                            -    updateQuery: function(tableName, attrValueHash, where) {
                                                                                            -      attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
                                                                                            -
                                                                                            -      var query  = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
                                                                                            -        , values = []
                                                                                            -
                                                                                            -      for (var key in attrValueHash) {
                                                                                            -        var value = attrValueHash[key]
                                                                                            -        values.push(Utils.addTicks(key) + "=" + escape((value instanceof Date) ? Utils.toSqlDate(value) : value))
                                                                                            -      }
                                                                                            -
                                                                                            -      var replacements = {
                                                                                            -        table: Utils.addTicks(tableName),
                                                                                            -        values: values.join(","),
                                                                                            -        where: MySqlQueryGenerator.getWhereConditions(where)
                                                                                            -      }
                                                                                            -
                                                                                            -      return Utils._.template(query)(replacements)
                                                                                            -    },
                                                                                            -
                                                                                            -    deleteQuery: function(tableName, where, options) {
                                                                                            -      options = options || {}
                                                                                            -
                                                                                            -      var query = "DELETE FROM <%= table %> WHERE <%= where %>"
                                                                                            -      var replacements = {
                                                                                            -        table: Utils.addTicks(tableName),
                                                                                            -        where: this.getWhereConditions(where),
                                                                                            -        limit: Utils.escape(options.limit)
                                                                                            -      }
                                                                                            -
                                                                                            -      return Utils._.template(query)(replacements)
                                                                                            -    },
                                                                                            -
                                                                                            -    attributesToSQL: function(attributes) {
                                                                                            -      var result = {}
                                                                                            -
                                                                                            -      for (var name in attributes) {
                                                                                            -        var dataType = attributes[name]
                                                                                            -
                                                                                            -        if(Utils.isHash(dataType)) {
                                                                                            -          var template     = "<%= type %>"
                                                                                            -            , replacements = { type: dataType.type }
                                                                                            -
                                                                                            -          if(dataType.hasOwnProperty('allowNull') && !dataType.allowNull && !dataType.primaryKey)
                                                                                            -            template += " NOT NULL"
                                                                                            -
                                                                                            -          if(dataType.defaultValue != undefined) {
                                                                                            -            template += " DEFAULT <%= defaultValue %>"
                                                                                            -            replacements.defaultValue = Utils.escape(dataType.defaultValue)
                                                                                            -          }
                                                                                            -
                                                                                            -          if(dataType.unique) template += " UNIQUE"
                                                                                            -          if(dataType.primaryKey) template += " PRIMARY KEY"
                                                                                            -
                                                                                            -          result[name] = Utils._.template(template)(replacements)
                                                                                            -        } else {
                                                                                            -          result[name] = dataType
                                                                                            -        }
                                                                                            -      }
                                                                                            -
                                                                                            -      return result
                                                                                            -    },
                                                                                            -
                                                                                            -    findAutoIncrementField: function(factory) {
                                                                                            -      var fields = []
                                                                                            -
                                                                                            -      for (var name in factory.attributes) {
                                                                                            -        var definition = factory.attributes[name]
                                                                                            -
                                                                                            -        if (definition && (definition.indexOf('INTEGER PRIMARY KEY') == 0)) {
                                                                                            -          fields.push(name)
                                                                                            -        }
                                                                                            -      }
                                                                                            -
                                                                                            -      return fields
                                                                                            -    }
                                                                                            -  }
                                                                                            -
                                                                                            -  var MySqlQueryGenerator = Utils._.extend(
                                                                                            -    Utils._.clone(require("../query-generator")),
                                                                                            -    Utils._.clone(require("../mysql/query-generator"))
                                                                                            -  )
                                                                                            -
                                                                                            -  return Utils._.extend(MySqlQueryGenerator, QueryGenerator)
                                                                                            -})()
                                                                                            \ No newline at end of file diff --git a/docs/dialects/sqlite/query.js.html b/docs/dialects/sqlite/query.js.html deleted file mode 100644 index 02ce096ab2cf..000000000000 --- a/docs/dialects/sqlite/query.js.html +++ /dev/null @@ -1,248 +0,0 @@ -Sequelize

                                                                                            Sequelize

                                                                                            declaration

                                                                                            Utils

                                                                                            Utils
                                                                                              var Utils         = require("../../utils")
                                                                                              -  , AbstractQuery = require('../abstract/query')
                                                                                              -
                                                                                              -
                                                                                              -module.exports = (function() {
                                                                                              -  var Query = function(database, sequelize, callee, options) {
                                                                                              -    this.database = database
                                                                                              -    this.sequelize = sequelize
                                                                                              -    this.callee = callee
                                                                                              -    this.options = Utils._.extend({
                                                                                              -      logging: console.log,
                                                                                              -      plain: false,
                                                                                              -      raw: false
                                                                                              -    }, options || {})
                                                                                              -
                                                                                              -    if(this.options.logging === true) {
                                                                                              -      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
                                                                                              -      this.options.logging = console.log
                                                                                              -    }
                                                                                              -
                                                                                              -    if(this.options.logging == console.log) {
                                                                                              -      // using just console.log will break in node < 0.6
                                                                                              -      this.options.logging = function(s) { console.log(s) }
                                                                                              -    }
                                                                                              -  }
                                                                                              -  Utils.inherit(Query, AbstractQuery)
                                                                                              -
                                                                                              -  Query.prototype.run = function(sql) {
                                                                                              -    var self = this
                                                                                              -
                                                                                              -    this.sql = sql
                                                                                              -
                                                                                              -    if(this.options.logging !== false) {
                                                                                              -      this.options.logging('Executing: ' + this.sql)
                                                                                              -    }
                                                                                              -
                                                                                              -    var columnTypes = {};
                                                                                              -    this.database.serialize(function() {
                                                                                              -      var executeSql = function() {
                                                                                              -        self.database[databaseMethod](self.sql, function(err, results) {
                                                                                              -           //allow clients to listen to sql to do their own logging or whatnot
                                                                                              -          self.emit('sql', self.sql)
                                                                                              -          this.columnTypes = columnTypes;
                                                                                              -          err ? onFailure.call(self, err) : onSuccess.call(self, results, this)
                                                                                              -        })
                                                                                              -      };
                                                                                              -
                                                                                              -      var isInsertCommand = (self.sql.toLowerCase().indexOf('insert') == 0)
                                                                                              -        , isUpdateCommand = (self.sql.toLowerCase().indexOf('update') == 0)
                                                                                              -        , databaseMethod  = (isInsertCommand || isUpdateCommand) ? 'run' : 'all'
                                                                                              -      if (databaseMethod === 'all' && /select\s.*?\sfrom\s+([^ ;]+)/i.test(self.sql)) {
                                                                                              -        var tableName = RegExp.$1;
                                                                                              -        if (tableName !== 'sqlite_master') {
                                                                                              -          // get the column types
                                                                                              -          self.database.all("PRAGMA table_info(" + tableName + ")", function(err, results) {
                                                                                              -            if (!err) {
                                                                                              -              for (var i=0, l=results.length; i
                                                                                              \ No newline at end of file diff --git a/docs/emitters/custom-event-emitter.js.html b/docs/emitters/custom-event-emitter.js.html deleted file mode 100644 index e3f25876927f..000000000000 --- a/docs/emitters/custom-event-emitter.js.html +++ /dev/null @@ -1,176 +0,0 @@ -Sequelize

                                                                                              Sequelize

                                                                                              declaration

                                                                                              util

                                                                                              util
                                                                                                var util         = require("util")
                                                                                                -  , EventEmitter = require("events").EventEmitter
                                                                                                -
                                                                                                -module.exports = (function() {
                                                                                                -  var CustomEventEmitter = function(fct) {
                                                                                                -    this.fct = fct
                                                                                                -  }
                                                                                                -  util.inherits(CustomEventEmitter, EventEmitter)
                                                                                                -
                                                                                                -  CustomEventEmitter.prototype.run = function() {
                                                                                                -    var self = this
                                                                                                -
                                                                                                -    // delay the function call and return the emitter
                                                                                                -    setTimeout(function(){
                                                                                                -      self.fct.call(self, self)
                                                                                                -    }, 1)
                                                                                                -
                                                                                                -    return this
                                                                                                -  }
                                                                                                -
                                                                                                -  CustomEventEmitter.prototype.success =
                                                                                                -  CustomEventEmitter.prototype.ok =
                                                                                                -  function(fct) {
                                                                                                -    this.on('success', fct)
                                                                                                -    return this
                                                                                                -  }
                                                                                                -
                                                                                                -  CustomEventEmitter.prototype.failure =
                                                                                                -  CustomEventEmitter.prototype.fail =
                                                                                                -  CustomEventEmitter.prototype.error =
                                                                                                -  function(fct) {
                                                                                                -    this.on('error', fct)
                                                                                                -    return this
                                                                                                -  }
                                                                                                -
                                                                                                -  CustomEventEmitter.prototype.done =
                                                                                                -  CustomEventEmitter.prototype.complete =
                                                                                                -  function(fct) {
                                                                                                -    this.on('error', function(err) { fct(err, null) })
                                                                                                -        .on('success', function(result) { fct(null, result) })
                                                                                                -    return this
                                                                                                -  }
                                                                                                -
                                                                                                -
                                                                                                -  return CustomEventEmitter
                                                                                                -})()
                                                                                                \ No newline at end of file diff --git a/docs/files/index.html b/docs/files/index.html new file mode 100644 index 000000000000..487fe15b2ad0 --- /dev/null +++ b/docs/files/index.html @@ -0,0 +1,10 @@ + + + + Redirector + + + + Click here to redirect + + diff --git a/docs/files/index.js.html b/docs/files/index.js.html new file mode 100644 index 000000000000..fc50a25afad2 --- /dev/null +++ b/docs/files/index.js.html @@ -0,0 +1,117 @@ + + + + + index.js + + + + + + + + +
                                                                                                +
                                                                                                +
                                                                                                + +

                                                                                                + +
                                                                                                +
                                                                                                + API Docs for: +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + Show: + + + + + + + +
                                                                                                + + +
                                                                                                +
                                                                                                +
                                                                                                +

                                                                                                File: index.js

                                                                                                + +
                                                                                                +
                                                                                                +/**
                                                                                                +  The entry point.
                                                                                                +  @module Sequelize
                                                                                                +**/
                                                                                                +module.exports = require("./lib/sequelize")
                                                                                                +
                                                                                                +    
                                                                                                +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                + + + + + + + + + + diff --git a/docs/files/lib_dao-factory.js.html b/docs/files/lib_dao-factory.js.html new file mode 100644 index 000000000000..81e048070128 --- /dev/null +++ b/docs/files/lib_dao-factory.js.html @@ -0,0 +1,532 @@ + + + + + lib/dao-factory.js + + + + + + + + +
                                                                                                +
                                                                                                +
                                                                                                + +

                                                                                                + +
                                                                                                +
                                                                                                + API Docs for: +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + Show: + + + + + + + +
                                                                                                + + +
                                                                                                +
                                                                                                +
                                                                                                +

                                                                                                File: lib/dao-factory.js

                                                                                                + +
                                                                                                +
                                                                                                +var Utils     = require("./utils")
                                                                                                +  , DAO       = require("./dao")
                                                                                                +  , DataTypes = require("./data-types")
                                                                                                +  , Util      = require('util')
                                                                                                +
                                                                                                +module.exports = (function() {
                                                                                                +  var DAOFactory = function(name, attributes, options) {
                                                                                                +    var self = this
                                                                                                +
                                                                                                +    this.options = Utils._.extend({
                                                                                                +      timestamps: true,
                                                                                                +      instanceMethods: {},
                                                                                                +      classMethods: {},
                                                                                                +      validate: {},
                                                                                                +      freezeTableName: false,
                                                                                                +      underscored: false,
                                                                                                +      syncOnAssociation: true,
                                                                                                +      paranoid: false,
                                                                                                +      whereCollection: null
                                                                                                +    }, options || {})
                                                                                                +
                                                                                                +    this.name = name
                                                                                                +    if (!this.options.tableName) {
                                                                                                +      this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name)
                                                                                                +    } else {
                                                                                                +      this.tableName = this.options.tableName
                                                                                                +    }
                                                                                                +    this.rawAttributes = attributes
                                                                                                +    this.daoFactoryManager = null // defined in init function
                                                                                                +    this.associations = {}
                                                                                                +
                                                                                                +    // extract validation
                                                                                                +    this.validate = this.options.validate || {}
                                                                                                +  }
                                                                                                +
                                                                                                +  Object.defineProperty(DAOFactory.prototype, 'attributes', {
                                                                                                +    get: function() {
                                                                                                +      return this.QueryGenerator.attributesToSQL(this.rawAttributes)
                                                                                                +    }
                                                                                                +  })
                                                                                                +
                                                                                                +  Object.defineProperty(DAOFactory.prototype, 'QueryInterface', {
                                                                                                +    get: function() { return this.daoFactoryManager.sequelize.getQueryInterface() }
                                                                                                +  })
                                                                                                +
                                                                                                +  Object.defineProperty(DAOFactory.prototype, 'QueryGenerator', {
                                                                                                +    get: function() { return this.QueryInterface.QueryGenerator }
                                                                                                +  })
                                                                                                +
                                                                                                +  DAOFactory.prototype.init = function(daoFactoryManager) {
                                                                                                +    var self = this;
                                                                                                +
                                                                                                +    this.daoFactoryManager = daoFactoryManager
                                                                                                +
                                                                                                +    this.primaryKeys = {};
                                                                                                +    Utils._.each(this.attributes, function(dataTypeString, attributeName) {
                                                                                                +      // If you don't specify a valid data type lets help you debug it
                                                                                                +      if (dataTypeString === undefined) {
                                                                                                +        throw new Error("Unrecognized data type for field " + attributeName );
                                                                                                +      }
                                                                                                +
                                                                                                +      if ((attributeName !== 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) {
                                                                                                +        self.primaryKeys[attributeName] = dataTypeString
                                                                                                +      }
                                                                                                +    })
                                                                                                +
                                                                                                +    this.primaryKeyCount = Object.keys(this.primaryKeys).length;
                                                                                                +    this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0;
                                                                                                +
                                                                                                +    addDefaultAttributes.call(this)
                                                                                                +    addOptionalClassMethods.call(this)
                                                                                                +    findAutoIncrementField.call(this)
                                                                                                +
                                                                                                +    // DAO prototype
                                                                                                +    this.DAO = function() {
                                                                                                +      DAO.apply(this, arguments);
                                                                                                +    };
                                                                                                +    Util.inherits(this.DAO, DAO);
                                                                                                +
                                                                                                +    this.DAO.prototype.rawAttributes = this.rawAttributes;
                                                                                                +    if (this.options.instanceMethods) {
                                                                                                +      Utils._.each(this.options.instanceMethods, function(fct, name) {
                                                                                                +        self.DAO.prototype[name] = fct
                                                                                                +      })
                                                                                                +    }
                                                                                                +    this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes);
                                                                                                +
                                                                                                +    this.DAO.prototype.booleanValues = [];
                                                                                                +    this.DAO.prototype.defaultValues = {};
                                                                                                +    this.DAO.prototype.validators = {};
                                                                                                +    Utils._.each(this.rawAttributes, function (definition, name) {
                                                                                                +      if (((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) {
                                                                                                +        self.DAO.prototype.booleanValues.push(name);
                                                                                                +      }
                                                                                                +      if (definition.hasOwnProperty('defaultValue')) {
                                                                                                +        self.DAO.prototype.defaultValues[name] = function() {
                                                                                                +          return Utils.toDefaultValue(definition.defaultValue);
                                                                                                +        }
                                                                                                +      }
                                                                                                +
                                                                                                +      if (definition.hasOwnProperty('validate')) {
                                                                                                +        self.DAO.prototype.validators[name] = definition.validate;
                                                                                                +      }
                                                                                                +    });
                                                                                                +
                                                                                                +    this.DAO.prototype.__factory = this;
                                                                                                +    this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues);
                                                                                                +
                                                                                                +    return this
                                                                                                +  }
                                                                                                +
                                                                                                +  DAOFactory.prototype.sync = function(options) {
                                                                                                +    options = Utils._.extend({}, this.options, options || {})
                                                                                                +
                                                                                                +    var self = this
                                                                                                +    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                +      var doQuery = function() {
                                                                                                +        self.QueryInterface
                                                                                                +          .createTable(self.tableName, self.attributes, options)
                                                                                                +          .success(function() { emitter.emit('success', self) })
                                                                                                +          .error(function(err) { emitter.emit('error', err) })
                                                                                                +          .on('sql', function(sql) { emitter.emit('sql', sql) })
                                                                                                +      }
                                                                                                +
                                                                                                +      if (options.force) {
                                                                                                +        self.drop().success(doQuery).error(function(err) { emitter.emit('error', err) })
                                                                                                +      } else {
                                                                                                +        doQuery()
                                                                                                +      }
                                                                                                +    }).run()
                                                                                                +  }
                                                                                                +
                                                                                                +  DAOFactory.prototype.drop = function() {
                                                                                                +    return this.QueryInterface.dropTable(this.tableName)
                                                                                                +  }
                                                                                                +
                                                                                                +  // alias for findAll
                                                                                                +  DAOFactory.prototype.all = function(options) {
                                                                                                +    return this.findAll(options)
                                                                                                +  }
                                                                                                +
                                                                                                +  DAOFactory.prototype.findAll = function(options) {
                                                                                                +    var hasJoin = false
                                                                                                +    var options = Utils._.clone(options)
                                                                                                +
                                                                                                +    if (typeof options === 'object') {
                                                                                                +      hasJoin = true
                                                                                                +
                                                                                                +      if (options.hasOwnProperty('include')) {
                                                                                                +        hasJoin = true
                                                                                                +
                                                                                                +        options.include = options.include.map(function(include) {
                                                                                                +          return validateIncludedElement.call(this, include)
                                                                                                +        }.bind(this))
                                                                                                +      }
                                                                                                +
                                                                                                +      // whereCollection is used for non-primary key updates
                                                                                                +      this.options.whereCollection = options.where || null
                                                                                                +    }
                                                                                                +
                                                                                                +    return this.QueryInterface.select(this, this.tableName, options, {
                                                                                                +      type:    'SELECT',
                                                                                                +      hasJoin: hasJoin
                                                                                                +    })
                                                                                                +  }
                                                                                                +
                                                                                                +  //right now, the caller (has-many-double-linked) is in charge of the where clause
                                                                                                +  DAOFactory.prototype.findAllJoin = function(joinTableName, options) {
                                                                                                +    var optcpy = Utils._.clone(options)
                                                                                                +    optcpy.attributes = optcpy.attributes || [Utils.addTicks(this.tableName)+".*"]
                                                                                                +
                                                                                                +    // whereCollection is used for non-primary key updates
                                                                                                +    this.options.whereCollection = optcpy.where || null;
                                                                                                +
                                                                                                +    return this.QueryInterface.select(this, [this.tableName, joinTableName], optcpy, { type: 'SELECT' })
                                                                                                +  }
                                                                                                +
                                                                                                + /**
                                                                                                +  * Search for an instance.
                                                                                                +  *
                                                                                                +  * @param  {Object} options Options to describe the scope of the search.
                                                                                                +  *   @param {Array} include A list of associations which shall get eagerly loaded. Supported is either { include: [ DaoFactory1, DaoFactory2, ...] } or { include: [ { daoFactory: DaoFactory1, as: 'Alias' } ] }.
                                                                                                +  * @return {Object}         A promise which fires `success`, `error`, `complete` and `sql`.
                                                                                                +  */
                                                                                                +  DAOFactory.prototype.find = function(options) {
                                                                                                +    var hasJoin = false
                                                                                                +
                                                                                                +    // no options defined?
                                                                                                +    // return an emitter which emits null
                                                                                                +    if ([null, undefined].indexOf(options) !== -1) {
                                                                                                +      return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                +        setTimeout(function() { emitter.emit('success', null) }, 10)
                                                                                                +      }).run()
                                                                                                +    }
                                                                                                +
                                                                                                +    var primaryKeys = this.primaryKeys
                                                                                                +
                                                                                                +    // options is not a hash but an id
                                                                                                +    if (typeof options === 'number') {
                                                                                                +      options = { where: options }
                                                                                                +    } else if (Utils._.size(primaryKeys) && Utils.argsArePrimaryKeys(arguments, primaryKeys)) {
                                                                                                +      var where = {}
                                                                                                +        , self  = this
                                                                                                +        , keys = Object.keys(primaryKeys)
                                                                                                +
                                                                                                +      Utils._.each(arguments, function(arg, i) {
                                                                                                +        var key = keys[i]
                                                                                                +        where[key] = arg
                                                                                                +      })
                                                                                                +
                                                                                                +      options = { where: where }
                                                                                                +    } else if (typeof options === 'string' && parseInt(options, 10).toString() === options) {
                                                                                                +      var parsedId = parseInt(options, 10)
                                                                                                +
                                                                                                +      if (!Utils._.isFinite(parsedId)) {
                                                                                                +        throw new Error('Invalid argument to find(). Must be an id or an options object.')
                                                                                                +      }
                                                                                                +
                                                                                                +      options = { where: parsedId }
                                                                                                +    } else if (typeof options === 'object') {
                                                                                                +      options = Utils._.clone(options)
                                                                                                +
                                                                                                +      if (options.hasOwnProperty('include')) {
                                                                                                +        hasJoin = true
                                                                                                +
                                                                                                +        options.include = options.include.map(function(include) {
                                                                                                +          return validateIncludedElement.call(this, include)
                                                                                                +        }.bind(this))
                                                                                                +      }
                                                                                                +
                                                                                                +      // whereCollection is used for non-primary key updates
                                                                                                +      this.options.whereCollection = options.where || null
                                                                                                +    }
                                                                                                +
                                                                                                +    options.limit = 1
                                                                                                +
                                                                                                +    return this.QueryInterface.select(this, this.tableName, options, {
                                                                                                +      plain: true,
                                                                                                +      type: 'SELECT',
                                                                                                +      hasJoin: hasJoin
                                                                                                +    })
                                                                                                +  }
                                                                                                +
                                                                                                +  DAOFactory.prototype.count = function(options) {
                                                                                                +    options = Utils._.extend({ attributes: [] }, options || {})
                                                                                                +    options.attributes.push(['count(*)', 'count'])
                                                                                                +    options.parseInt = true
                                                                                                +
                                                                                                +    return this.QueryInterface.rawSelect(this.tableName, options, 'count')
                                                                                                +  }
                                                                                                +
                                                                                                +  DAOFactory.prototype.max = function(field, options) {
                                                                                                +    options = Utils._.extend({ attributes: [] }, options || {})
                                                                                                +    options.attributes.push(['max(' + field + ')', 'max'])
                                                                                                +    options.parseInt = true
                                                                                                +
                                                                                                +    return this.QueryInterface.rawSelect(this.tableName, options, 'max')
                                                                                                +  }
                                                                                                +  DAOFactory.prototype.min = function(field, options) {
                                                                                                +    options = Utils._.extend({ attributes: [] }, options || {})
                                                                                                +    options.attributes.push(['min(' + field + ')', 'min'])
                                                                                                +    options.parseInt = true
                                                                                                +
                                                                                                +    return this.QueryInterface.rawSelect(this.tableName, options, 'min')
                                                                                                +  }
                                                                                                +
                                                                                                +  DAOFactory.prototype.build = function(values, options) {
                                                                                                +    options = options || { isNewRecord: true }
                                                                                                +
                                                                                                +    var self     = this
                                                                                                +      , instance = new this.DAO(values, this.options, options.isNewRecord)
                                                                                                +
                                                                                                +    instance.isNewRecord    = options.isNewRecord
                                                                                                +    instance.daoFactoryName = this.name
                                                                                                +    instance.daoFactory     = this
                                                                                                +
                                                                                                +    return instance
                                                                                                +  }
                                                                                                +
                                                                                                +  DAOFactory.prototype.create = function(values, fields) {
                                                                                                +    return this.build(values).save(fields)
                                                                                                +  }
                                                                                                +
                                                                                                +  DAOFactory.prototype.findOrCreate = function (params, defaults) {
                                                                                                +    var self = this;
                                                                                                +
                                                                                                +    return new Utils.CustomEventEmitter(function (emitter) {
                                                                                                +      self.find({
                                                                                                +        where: params
                                                                                                +      }).success(function (instance) {
                                                                                                +        if (instance === null) {
                                                                                                +          for (var attrname in defaults) {
                                                                                                +            params[attrname] = defaults[attrname]
                                                                                                +          }
                                                                                                +
                                                                                                +          self.create(params)
                                                                                                +            .success(function (instance) {
                                                                                                +              emitter.emit('success', instance)
                                                                                                +            })
                                                                                                +            .error( function (error) {
                                                                                                +              emitter.emit('error', error)
                                                                                                +            })
                                                                                                +        } else {
                                                                                                +          emitter.emit('success', instance)
                                                                                                +        }
                                                                                                +      }).error(function (error) {
                                                                                                +        emitter.emit('error', error)
                                                                                                +      });
                                                                                                +    }).run()
                                                                                                +  }
                                                                                                +
                                                                                                +  // private
                                                                                                +
                                                                                                +  var query = function() {
                                                                                                +    var args      = Utils._.map(arguments, function(arg, _) { return arg })
                                                                                                +      , sequelize = this.daoFactoryManager.sequelize
                                                                                                +
                                                                                                +    // add this as the second argument
                                                                                                +    if (arguments.length === 1) {
                                                                                                +      args.push(this)
                                                                                                +    }
                                                                                                +
                                                                                                +    // add {} as options
                                                                                                +    if (args.length === 2) {
                                                                                                +      args.push({})
                                                                                                +    }
                                                                                                +
                                                                                                +    return sequelize.query.apply(sequelize, args)
                                                                                                +  }
                                                                                                +
                                                                                                +  var addOptionalClassMethods = function() {
                                                                                                +    var self = this
                                                                                                +    Utils._.each(this.options.classMethods || {}, function(fct, name) { self[name] = fct })
                                                                                                +  }
                                                                                                +
                                                                                                +  var addDefaultAttributes = function() {
                                                                                                +    var self              = this
                                                                                                +      , defaultAttributes = {
                                                                                                +        id: {
                                                                                                +          type: DataTypes.INTEGER,
                                                                                                +          allowNull: false,
                                                                                                +          primaryKey: true,
                                                                                                +          autoIncrement: true
                                                                                                +        }
                                                                                                +      }
                                                                                                +
                                                                                                +    if (this.hasPrimaryKeys) {
                                                                                                +      defaultAttributes = {}
                                                                                                +    }
                                                                                                +
                                                                                                +    if (this.options.timestamps) {
                                                                                                +      defaultAttributes[Utils._.underscoredIf('createdAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
                                                                                                +      defaultAttributes[Utils._.underscoredIf('updatedAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
                                                                                                +
                                                                                                +      if (this.options.paranoid)
                                                                                                +        defaultAttributes[Utils._.underscoredIf('deletedAt', this.options.underscored)] = {type: DataTypes.DATE}
                                                                                                +    }
                                                                                                +
                                                                                                +    Utils._.each(defaultAttributes, function(value, attr) {
                                                                                                +      self.rawAttributes[attr] = value
                                                                                                +    })
                                                                                                +  }
                                                                                                +
                                                                                                +  var findAutoIncrementField = function() {
                                                                                                +    var fields = this.QueryGenerator.findAutoIncrementField(this)
                                                                                                +
                                                                                                +    this.autoIncrementField = null
                                                                                                +
                                                                                                +    fields.forEach(function(field) {
                                                                                                +      if (this.autoIncrementField) {
                                                                                                +        throw new Error('Invalid DAO definition. Only one autoincrement field allowed.')
                                                                                                +      } else {
                                                                                                +        this.autoIncrementField = field
                                                                                                +      }
                                                                                                +    }.bind(this))
                                                                                                +  }
                                                                                                +
                                                                                                +  var validateIncludedElement = function(include) {
                                                                                                +    if (include instanceof DAOFactory) {
                                                                                                +      include = { daoFactory: include, as: include.tableName }
                                                                                                +    }
                                                                                                +
                                                                                                +    if (typeof include === 'object') {
                                                                                                +      if (include.hasOwnProperty('model')) {
                                                                                                +        include.daoFactory = include.model
                                                                                                +        delete include.model
                                                                                                +      }
                                                                                                +
                                                                                                +      if (include.hasOwnProperty('daoFactory') && (include.hasOwnProperty('as'))) {
                                                                                                +        var usesAlias   = (include.as !== include.daoFactory.tableName)
                                                                                                +          , association = (usesAlias ? this.getAssociationByAlias(include.as) : this.getAssociation(include.daoFactory))
                                                                                                +
                                                                                                +        // check if the current daoFactory is actually associated with the passed daoFactory
                                                                                                +        if (!!association && (!association.options.as || (association.options.as === include.as))) {
                                                                                                +          include.association = association
                                                                                                +
                                                                                                +          return include
                                                                                                +        } else {
                                                                                                +          var msg = include.daoFactory.name
                                                                                                +
                                                                                                +          if (usesAlias) {
                                                                                                +            msg += " (" + include.as + ")"
                                                                                                +          }
                                                                                                +
                                                                                                +          msg += " is not associated to " + this.name + "!"
                                                                                                +
                                                                                                +          throw new Error(msg)
                                                                                                +        }
                                                                                                +      } else {
                                                                                                +        throw new Error('Include malformed. Expected attributes: daoFactory, as!')
                                                                                                +      }
                                                                                                +    } else {
                                                                                                +      throw new Error('Include unexpected. Element has to be either an instance of DAOFactory or an object.')
                                                                                                +    }
                                                                                                +  }
                                                                                                +
                                                                                                +  Utils._.extend(DAOFactory.prototype, require("./associations/mixin"))
                                                                                                +
                                                                                                +  return DAOFactory
                                                                                                +})()
                                                                                                +
                                                                                                +    
                                                                                                +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                + + + + + + + + + + diff --git a/docs/files/lib_dialects_abstract_query.js.html b/docs/files/lib_dialects_abstract_query.js.html new file mode 100644 index 000000000000..d71fe6e0a67c --- /dev/null +++ b/docs/files/lib_dialects_abstract_query.js.html @@ -0,0 +1,553 @@ + + + + + lib/dialects/abstract/query.js + + + + + + + + +
                                                                                                +
                                                                                                +
                                                                                                + +

                                                                                                + +
                                                                                                +
                                                                                                + API Docs for: +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + Show: + + + + + + + +
                                                                                                + + +
                                                                                                +
                                                                                                +
                                                                                                +

                                                                                                File: lib/dialects/abstract/query.js

                                                                                                + +
                                                                                                +
                                                                                                +var Utils              = require('../../utils')
                                                                                                +  , CustomEventEmitter = require("../../emitters/custom-event-emitter")
                                                                                                +  , Dot                = require('dottie')
                                                                                                +
                                                                                                +module.exports = (function() {
                                                                                                +  var AbstractQuery = function(database, sequelize, callee, options) {}
                                                                                                +
                                                                                                +  /**
                                                                                                +    Inherit from CustomEventEmitter
                                                                                                +  */
                                                                                                +  Utils.inherit(AbstractQuery, CustomEventEmitter)
                                                                                                +
                                                                                                +  /**
                                                                                                +   * Execute the passed sql query.
                                                                                                +   *
                                                                                                +   * Examples:
                                                                                                +   *
                                                                                                +   *     query.run('SELECT 1')
                                                                                                +   *
                                                                                                +   * @param {String} sql - The SQL query which should be executed.
                                                                                                +   * @api public
                                                                                                +   */
                                                                                                +  AbstractQuery.prototype.run = function(sql) {
                                                                                                +    throw new Error("The run method wasn't overwritten!")
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +   * Check the logging option of the instance and print deprecation warnings.
                                                                                                +   *
                                                                                                +   * @return {void}
                                                                                                +   */
                                                                                                +  AbstractQuery.prototype.checkLoggingOption = function() {
                                                                                                +    if (this.options.logging === true) {
                                                                                                +      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
                                                                                                +      this.options.logging = console.log
                                                                                                +    }
                                                                                                +
                                                                                                +    if (this.options.logging === console.log) {
                                                                                                +      // using just console.log will break in node < 0.6
                                                                                                +      this.options.logging = function(s) { console.log(s) }
                                                                                                +    }
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +   * High level function that handles the results of a query execution.
                                                                                                +   *
                                                                                                +   *
                                                                                                +   * Example:
                                                                                                +   *  query.formatResults([
                                                                                                +   *    {
                                                                                                +   *      id: 1,              // this is from the main table
                                                                                                +   *      attr2: 'snafu',     // this is from the main table
                                                                                                +   *      Tasks.id: 1,        // this is from the associated table
                                                                                                +   *      Tasks.title: 'task' // this is from the associated table
                                                                                                +   *    }
                                                                                                +   *  ])
                                                                                                +   *
                                                                                                +   * @param {Array} data - The result of the query execution.
                                                                                                +   */
                                                                                                +  AbstractQuery.prototype.formatResults = function(data) {
                                                                                                +    var result  = this.callee
                                                                                                +
                                                                                                +    if (isInsertQuery.call(this, data)) {
                                                                                                +      handleInsertQuery.call(this, data)
                                                                                                +    }
                                                                                                +
                                                                                                +    if (isSelectQuery.call(this)) {
                                                                                                +      result = handleSelectQuery.call(this, data)
                                                                                                +    } else if (isShowTableQuery.call(this)) {
                                                                                                +      result = handleShowTableQuery.call(this, data)
                                                                                                +    } else if (isShowOrDescribeQuery.call(this)) {
                                                                                                +      result = data
                                                                                                +
                                                                                                +      if (this.sql.toLowerCase().indexOf('describe') === 0) {
                                                                                                +        result = {}
                                                                                                +
                                                                                                +        data.forEach(function(_result) {
                                                                                                +          result[_result.Field] = {
                                                                                                +            type:         _result.Type.toUpperCase(),
                                                                                                +            allowNull:    (_result.Null === 'YES'),
                                                                                                +            defaultValue: _result.Default
                                                                                                +          }
                                                                                                +        })
                                                                                                +      } else if (this.sql.toLowerCase().indexOf('show index from') === 0) {
                                                                                                +        result = Utils._.uniq(result.map(function(result) {
                                                                                                +          return {
                                                                                                +            name:       result.Key_name,
                                                                                                +            tableName:  result.Table,
                                                                                                +            unique:     (result.Non_unique !== 1)
                                                                                                +          }
                                                                                                +        }), false, function(row) {
                                                                                                +          return row.name
                                                                                                +        })
                                                                                                +      }
                                                                                                +    } else if (isCallQuery.call(this)) {
                                                                                                +      result = data[0]
                                                                                                +    }
                                                                                                +
                                                                                                +    return result
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +    Shortcut methods (success, ok) for listening for success events.
                                                                                                +
                                                                                                +    Params:
                                                                                                +      - fct: A function that gets executed once the *success* event was triggered.
                                                                                                +
                                                                                                +    Result:
                                                                                                +      The function returns the instance of the query.
                                                                                                +  */
                                                                                                +  AbstractQuery.prototype.success =
                                                                                                +  AbstractQuery.prototype.ok =
                                                                                                +  function(fct) {
                                                                                                +    this.on('success', fct)
                                                                                                +    return this
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +    Shortcut methods (failure, fail, error) for listening for error events.
                                                                                                +
                                                                                                +    Params:
                                                                                                +      - fct: A function that gets executed once the *error* event was triggered.
                                                                                                +
                                                                                                +    Result:
                                                                                                +      The function returns the instance of the query.
                                                                                                +  */
                                                                                                +  AbstractQuery.prototype.failure =
                                                                                                +  AbstractQuery.prototype.fail =
                                                                                                +  AbstractQuery.prototype.error =
                                                                                                +  function(fct) {
                                                                                                +    this.on('error', fct)
                                                                                                +    return this
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +   * This function is a wrapper for private methods.
                                                                                                +   *
                                                                                                +   * @param {String} fctName The name of the private method.
                                                                                                +   *
                                                                                                +   */
                                                                                                +  AbstractQuery.prototype.send = function(fctName/*, arg1, arg2, arg3, ...*/) {
                                                                                                +    var args = Array.prototype.slice.call(arguments).slice(1)
                                                                                                +    return eval(fctName).apply(this, args)
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +   * Get the attributes of an insert query, which contains the just inserted id.
                                                                                                +   *
                                                                                                +   * @return {String} The field name.
                                                                                                +   */
                                                                                                +  AbstractQuery.prototype.getInsertIdField = function() {
                                                                                                +    return 'insertId'
                                                                                                +  }
                                                                                                +
                                                                                                +  /////////////
                                                                                                +  // private //
                                                                                                +  /////////////
                                                                                                +
                                                                                                +  /**
                                                                                                +   * Iterate over all known tables and search their names inside the sql query.
                                                                                                +   * This method will also check association aliases ('as' option).
                                                                                                +   *
                                                                                                +   * @param  {String} attribute An attribute of a SQL query. (?)
                                                                                                +   * @return {String}           The found tableName / alias.
                                                                                                +   */
                                                                                                +  var findTableNameInAttribute = function(attribute) {
                                                                                                +    if (!this.options.include) {
                                                                                                +      return null
                                                                                                +    }
                                                                                                +
                                                                                                +    var tableNames = this.options.include.map(function(include) {
                                                                                                +      return include.as
                                                                                                +    }).filter(function(include) {
                                                                                                +      return attribute.indexOf(include + '.') === 0
                                                                                                +    })
                                                                                                +
                                                                                                +    if (tableNames.length === 1) {
                                                                                                +      return tableNames[0]
                                                                                                +    } else {
                                                                                                +      return null
                                                                                                +    }
                                                                                                +  }
                                                                                                +
                                                                                                +  var queryResultHasJoin = function(results) {
                                                                                                +    if (!!results[0]) {
                                                                                                +      var keys = Object.keys(results[0])
                                                                                                +
                                                                                                +      for (var i = 0; i < keys.length; i++) {
                                                                                                +        if (!!findTableNameInAttribute.call(this, keys[i])) {
                                                                                                +          return true
                                                                                                +        }
                                                                                                +      }
                                                                                                +    }
                                                                                                +
                                                                                                +    return false
                                                                                                +  }
                                                                                                +
                                                                                                +  var isInsertQuery = function(results, metaData) {
                                                                                                +    var result = true
                                                                                                +
                                                                                                +    // is insert query if sql contains insert into
                                                                                                +    result = result && (this.sql.toLowerCase().indexOf('insert into') === 0)
                                                                                                +
                                                                                                +    // is insert query if no results are passed or if the result has the inserted id
                                                                                                +    result = result && (!results || results.hasOwnProperty(this.getInsertIdField()))
                                                                                                +
                                                                                                +    // is insert query if no metadata are passed or if the metadata has the inserted id
                                                                                                +    result = result && (!metaData || metaData.hasOwnProperty(this.getInsertIdField()))
                                                                                                +
                                                                                                +    return result
                                                                                                +  }
                                                                                                +
                                                                                                +  var handleInsertQuery = function(results, metaData) {
                                                                                                +    if (this.callee) {
                                                                                                +      // add the inserted row id to the instance
                                                                                                +      var autoIncrementField = this.callee.__factory.autoIncrementField
                                                                                                +        , id                 = null
                                                                                                +
                                                                                                +      id = id || (results && results[this.getInsertIdField()])
                                                                                                +      id = id || (metaData && metaData[this.getInsertIdField()])
                                                                                                +
                                                                                                +      this.callee[autoIncrementField] = id
                                                                                                +    }
                                                                                                +  }
                                                                                                +
                                                                                                +  var isShowTableQuery = function() {
                                                                                                +    return (this.sql.toLowerCase().indexOf('show tables') === 0)
                                                                                                +  }
                                                                                                +
                                                                                                +  var handleShowTableQuery = function(results) {
                                                                                                +    return Utils._.flatten(results.map(function(resultSet) {
                                                                                                +      return Utils._.values(resultSet)
                                                                                                +    }))
                                                                                                +  }
                                                                                                +
                                                                                                +  var isSelectQuery = function() {
                                                                                                +    return this.options.type === 'SELECT';
                                                                                                +  }
                                                                                                +
                                                                                                +  var isUpdateQuery = function() {
                                                                                                +    return (this.sql.toLowerCase().indexOf('update') === 0)
                                                                                                +  }
                                                                                                +
                                                                                                +  var handleSelectQuery = function(results) {
                                                                                                +    var result = null
                                                                                                +
                                                                                                +    if (this.options.raw) {
                                                                                                +      result = results.map(function(result) {
                                                                                                +        var o = {}
                                                                                                +
                                                                                                +        for (var key in result) {
                                                                                                +          if (result.hasOwnProperty(key)) {
                                                                                                +            o[key] = result[key]
                                                                                                +          }
                                                                                                +        }
                                                                                                +
                                                                                                +        return o
                                                                                                +      })
                                                                                                +
                                                                                                +      result = result.map(Dot.transform)
                                                                                                +    } else if (this.options.hasJoin === true) {
                                                                                                +      result = prepareJoinData.call(this, results)
                                                                                                +      result = groupDataByCalleeFactory.call(this, result).map(function(result) {
                                                                                                +        // let's build the actual dao instance first...
                                                                                                +        var dao = this.callee.build(result[this.callee.tableName], { isNewRecord: false })
                                                                                                +
                                                                                                +        // ... and afterwards the prefetched associations
                                                                                                +        for (var tableName in result) {
                                                                                                +          if (result.hasOwnProperty(tableName) && (tableName !== this.callee.tableName)) {
                                                                                                +            buildAssociatedDaoInstances.call(this, tableName, result[tableName], dao)
                                                                                                +          }
                                                                                                +        }
                                                                                                +
                                                                                                +        return dao
                                                                                                +      }.bind(this))
                                                                                                +    } else {
                                                                                                +      result = results.map(function(result) {
                                                                                                +        return this.callee.build(result, { isNewRecord: false })
                                                                                                +      }.bind(this))
                                                                                                +    }
                                                                                                +
                                                                                                +    // return the first real model instance if options.plain is set (e.g. Model.find)
                                                                                                +    if (this.options.plain) {
                                                                                                +      result = (result.length === 0) ? null : result[0]
                                                                                                +    }
                                                                                                +
                                                                                                +    return result
                                                                                                +  }
                                                                                                +
                                                                                                +  var buildAssociatedDaoInstances = function(tableName, associationData, dao) {
                                                                                                +    var associatedDaoFactory = this.sequelize.daoFactoryManager.getDAO(tableName, { attribute: 'tableName' })
                                                                                                +      , association          = null
                                                                                                +
                                                                                                +    if (!!associatedDaoFactory) {
                                                                                                +      association = this.callee.getAssociation(associatedDaoFactory)
                                                                                                +    } else {
                                                                                                +      associatedDaoFactory = this.sequelize.daoFactoryManager.getDAO(Utils.pluralize(tableName), { attribute: 'tableName' })
                                                                                                +
                                                                                                +      if (!!associatedDaoFactory) {
                                                                                                +        association = this.callee.getAssociation(associatedDaoFactory)
                                                                                                +      } else {
                                                                                                +        association          = this.callee.getAssociationByAlias(tableName)
                                                                                                +        associatedDaoFactory = association.target
                                                                                                +      }
                                                                                                +    }
                                                                                                +
                                                                                                +    var accessor = Utils._.camelize(tableName)
                                                                                                +
                                                                                                +    // downcase the first char
                                                                                                +    accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
                                                                                                +
                                                                                                +    associationData.forEach(function(data) {
                                                                                                +      var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false })
                                                                                                +        , isEmpty = ! Utils.firstValueOfHash(daoInstance.identifiers)
                                                                                                +
                                                                                                +      if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) {
                                                                                                +        accessor = Utils.singularize(accessor)
                                                                                                +        dao[accessor] = isEmpty ? null : daoInstance
                                                                                                +      } else {
                                                                                                +        dao[accessor] = dao[accessor] || []
                                                                                                +
                                                                                                +        if (!isEmpty) {
                                                                                                +          dao[accessor].push(daoInstance)
                                                                                                +        }
                                                                                                +      }
                                                                                                +
                                                                                                +      // add the accessor to the eagerly loaded associations array
                                                                                                +      dao.__eagerlyLoadedAssociations = Utils._.uniq(dao.__eagerlyLoadedAssociations.concat([accessor]))
                                                                                                +    })
                                                                                                +  }
                                                                                                +
                                                                                                +  var isShowOrDescribeQuery = function() {
                                                                                                +    var result = false
                                                                                                +
                                                                                                +    result = result || (this.sql.toLowerCase().indexOf('show') === 0)
                                                                                                +    result = result || (this.sql.toLowerCase().indexOf('describe') === 0)
                                                                                                +
                                                                                                +    return  result
                                                                                                +  }
                                                                                                +
                                                                                                +  var isCallQuery = function() {
                                                                                                +    var result = false
                                                                                                +
                                                                                                +    result = result || (this.sql.toLowerCase().indexOf('call') === 0)
                                                                                                +
                                                                                                +    return result
                                                                                                +  }
                                                                                                +
                                                                                                +
                                                                                                +  /**
                                                                                                +    The function takes the result of the query execution and groups
                                                                                                +    the associated data by the callee.
                                                                                                +
                                                                                                +    Example:
                                                                                                +      groupDataByCalleeFactory([
                                                                                                +        {
                                                                                                +          callee: { some: 'data', id: 1 },
                                                                                                +          association: { foo: 'bar', id: 1 }
                                                                                                +        }, {
                                                                                                +          callee: { some: 'data', id: 1 },
                                                                                                +          association: { foo: 'bar', id: 2 }
                                                                                                +        }, {
                                                                                                +          callee: { some: 'data', id: 1 },
                                                                                                +          association: { foo: 'bar', id: 3 }
                                                                                                +        }
                                                                                                +      ])
                                                                                                +
                                                                                                +    Result:
                                                                                                +      Something like this:
                                                                                                +
                                                                                                +      [
                                                                                                +        {
                                                                                                +          callee:  { some: 'data', id: 1 },
                                                                                                +          association: [
                                                                                                +            { foo: 'bar', id: 1 },
                                                                                                +            { foo: 'bar', id: 2 },
                                                                                                +            { foo: 'bar', id: 3 }
                                                                                                +          ]
                                                                                                +        }
                                                                                                +      ]
                                                                                                +  */
                                                                                                +  var groupDataByCalleeFactory = function(data) {
                                                                                                +    var result          = []
                                                                                                +      , calleeTableName = this.callee.tableName
                                                                                                +
                                                                                                +    data.forEach(function(row) {
                                                                                                +      var calleeData    = row[calleeTableName]
                                                                                                +        , existingEntry = result.filter(function(groupedRow) {
                                                                                                +            return Utils._.isEqual(groupedRow[calleeTableName], calleeData)
                                                                                                +          })[0]
                                                                                                +
                                                                                                +      if (!existingEntry) {
                                                                                                +        existingEntry = {}
                                                                                                +        result.push(existingEntry)
                                                                                                +        existingEntry[calleeTableName] = calleeData
                                                                                                +      }
                                                                                                +
                                                                                                +      for (var attrName in row) {
                                                                                                +        if (row.hasOwnProperty(attrName) && (attrName !== calleeTableName)) {
                                                                                                +          existingEntry[attrName] = existingEntry[attrName] || []
                                                                                                +          existingEntry[attrName].push(row[attrName])
                                                                                                +        }
                                                                                                +      }
                                                                                                +    })
                                                                                                +
                                                                                                +    return result
                                                                                                +  }
                                                                                                +
                                                                                                +
                                                                                                +  /**
                                                                                                +   * This function will prepare the result of select queries with joins.
                                                                                                +   *
                                                                                                +   * @param  {Array} data This array contains objects.
                                                                                                +   * @return {Array}      The array will have the needed format for groupDataByCalleeFactory.
                                                                                                +   */
                                                                                                +  var prepareJoinData = function(data) {
                                                                                                +    var result = data.map(function(row) {
                                                                                                +      var nestedRow = {}
                                                                                                +
                                                                                                +      for (var key in row) {
                                                                                                +        if (row.hasOwnProperty(key)) {
                                                                                                +          var tableName = findTableNameInAttribute.call(this, key)
                                                                                                +
                                                                                                +          if (!!tableName) {
                                                                                                +            nestedRow[tableName] = nestedRow[tableName] || {}
                                                                                                +            nestedRow[tableName][key.replace(tableName + '.', '')] = row[key]
                                                                                                +          } else {
                                                                                                +            nestedRow[this.callee.tableName] = nestedRow[this.callee.tableName] || {}
                                                                                                +            nestedRow[this.callee.tableName][key] = row[key]
                                                                                                +          }
                                                                                                +        }
                                                                                                +      }
                                                                                                +
                                                                                                +      return nestedRow
                                                                                                +    }.bind(this))
                                                                                                +
                                                                                                +    return result
                                                                                                +  }
                                                                                                +
                                                                                                +  return AbstractQuery
                                                                                                +})()
                                                                                                +
                                                                                                +    
                                                                                                +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                + + + + + + + + + + diff --git a/docs/files/lib_dialects_sqlite_query-interface.js.html b/docs/files/lib_dialects_sqlite_query-interface.js.html new file mode 100644 index 000000000000..df306954438f --- /dev/null +++ b/docs/files/lib_dialects_sqlite_query-interface.js.html @@ -0,0 +1,234 @@ + + + + + lib/dialects/sqlite/query-interface.js + + + + + + + + +
                                                                                                +
                                                                                                +
                                                                                                + +

                                                                                                + +
                                                                                                +
                                                                                                + API Docs for: +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + Show: + + + + + + + +
                                                                                                + + +
                                                                                                +
                                                                                                +
                                                                                                +

                                                                                                File: lib/dialects/sqlite/query-interface.js

                                                                                                + +
                                                                                                +
                                                                                                +var Utils = require("../../utils")
                                                                                                +
                                                                                                +/**
                                                                                                + Returns an object that treats SQLite's inabilities to do certain queries.
                                                                                                +
                                                                                                + @class QueryInterface
                                                                                                + @static
                                                                                                + */
                                                                                                +var QueryInterface = module.exports = {
                                                                                                +  /**
                                                                                                +    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
                                                                                                +    new table with the same name but without the obsolete column.
                                                                                                +
                                                                                                +    @method removeColumn
                                                                                                +    @for    QueryInterface
                                                                                                +
                                                                                                +    @param  {String} tableName     The name of the table.
                                                                                                +    @param  {String} attributeName The name of the attribute that we want to remove.
                                                                                                +    @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
                                                                                                +   */
                                                                                                +  removeColumn: function(tableName, attributeName, emitter, queryAndEmit) {
                                                                                                +    this.describeTable(tableName).complete(function(err, fields) {
                                                                                                +      if (err) {
                                                                                                +        emitter.emit('error', err)
                                                                                                +      } else {
                                                                                                +        delete fields[attributeName]
                                                                                                +
                                                                                                +        var sql        = this.QueryGenerator.removeColumnQuery(tableName, fields)
                                                                                                +          , subQueries = sql.split(';').filter(function(q) { return q !== '' })
                                                                                                +
                                                                                                +        QueryInterface.execMultiQuery.call(this, subQueries, 'removeColumn', emitter, queryAndEmit)
                                                                                                +      }
                                                                                                +    }.bind(this))
                                                                                                +  },
                                                                                                +
                                                                                                +  /**
                                                                                                +    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
                                                                                                +    new table with the same name but with a modified version of the respective column.
                                                                                                +
                                                                                                +    @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  {CustomEventEmitter} emitter The EventEmitter from outside.
                                                                                                +    @param  {Function} queryAndEmit The function from outside that triggers some events to get triggered.
                                                                                                +
                                                                                                +    @since 1.6.0
                                                                                                +   */
                                                                                                +  changeColumn: function(tableName, attributes, emitter, queryAndEmit) {
                                                                                                +    var attributeName = Utils._.keys(attributes)[0]
                                                                                                +
                                                                                                +    this.describeTable(tableName).complete(function(err, fields) {
                                                                                                +      if (err) {
                                                                                                +        emitter.emit('error', err)
                                                                                                +      } else {
                                                                                                +        fields[attributeName] = attributes[attributeName]
                                                                                                +
                                                                                                +        var sql        = this.QueryGenerator.removeColumnQuery(tableName, fields)
                                                                                                +          , subQueries = sql.split(';').filter(function(q) { return q !== '' })
                                                                                                +
                                                                                                +        QueryInterface.execMultiQuery.call(this, subQueries, 'changeColumn', emitter, queryAndEmit)
                                                                                                +      }
                                                                                                +    }.bind(this))
                                                                                                +  },
                                                                                                +
                                                                                                +  /**
                                                                                                +    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
                                                                                                +    new table with the same name but with a renamed version of the respective column.
                                                                                                +
                                                                                                +    @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} attrNameAfter The name of the attribute after it was renamed.
                                                                                                +    @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
                                                                                                +   */
                                                                                                +  renameColumn: function(tableName, attrNameBefore, attrNameAfter, emitter, queryAndEmit) {
                                                                                                +    this.describeTable(tableName).complete(function(err, fields) {
                                                                                                +      if (err) {
                                                                                                +        emitter.emit('error', err)
                                                                                                +      } else {
                                                                                                +        fields[attrNameAfter] = Utils._.clone(fields[attrNameBefore])
                                                                                                +        delete fields[attrNameBefore]
                                                                                                +
                                                                                                +        var sql        = this.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields)
                                                                                                +          , subQueries = sql.split(';').filter(function(q) { return q !== '' })
                                                                                                +
                                                                                                +        QueryInterface.execMultiQuery.call(this, subQueries, 'renameColumn', emitter, queryAndEmit)
                                                                                                +      }
                                                                                                +    }.bind(this))
                                                                                                +  },
                                                                                                +
                                                                                                +  execMultiQuery: function(queries, methodName, emitter, queryAndEmit) {
                                                                                                +    var chainer = new Utils.QueryChainer()
                                                                                                +
                                                                                                +    queries.splice(0, queries.length - 1).forEach(function(query) {
                                                                                                +      chainer.add(this.sequelize, 'query', [query + ";", null, { raw: true }])
                                                                                                +    }.bind(this))
                                                                                                +
                                                                                                +    chainer
                                                                                                +      .runSerially()
                                                                                                +      .complete(function(err) {
                                                                                                +        if (err) {
                                                                                                +          emitter.emit(methodName, err)
                                                                                                +          emitter.emit('error', err)
                                                                                                +        } else {
                                                                                                +          queryAndEmit.call(this, queries.splice(queries.length - 1)[0], methodName, {}, emitter)
                                                                                                +        }
                                                                                                +      }.bind(this))
                                                                                                +  }
                                                                                                +}
                                                                                                +
                                                                                                +    
                                                                                                +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                + + + + + + + + + + diff --git a/docs/files/lib_sequelize.js.html b/docs/files/lib_sequelize.js.html new file mode 100644 index 000000000000..2f5d5cc3b276 --- /dev/null +++ b/docs/files/lib_sequelize.js.html @@ -0,0 +1,335 @@ + + + + + lib/sequelize.js + + + + + + + + +
                                                                                                +
                                                                                                +
                                                                                                + +

                                                                                                + +
                                                                                                +
                                                                                                + API Docs for: +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + Show: + + + + + + + +
                                                                                                + + +
                                                                                                +
                                                                                                +
                                                                                                +

                                                                                                File: lib/sequelize.js

                                                                                                + +
                                                                                                +
                                                                                                +var Utils             = require("./utils")
                                                                                                +  , DAOFactory        = require("./dao-factory")
                                                                                                +  , DataTypes         = require('./data-types')
                                                                                                +  , DAOFactoryManager = require("./dao-factory-manager")
                                                                                                +  , QueryInterface    = require("./query-interface")
                                                                                                +
                                                                                                +if (typeof process != 'undefined' && parseFloat(process.version.replace('v', '')) < 0.6) {
                                                                                                +  console.log("DEPRECATION WARNING: Support for Node.JS < v0.6 will be canceled in the next minor release.")
                                                                                                +}
                                                                                                +
                                                                                                +module.exports = (function() {
                                                                                                +  /**
                                                                                                +    Main class of the project.
                                                                                                +
                                                                                                +    @param {String} database The name of the database.
                                                                                                +    @param {String} username The username which is used to authenticate against the database.
                                                                                                +    @param {String} [password=null] The password which is used to authenticate against the database.
                                                                                                +    @param {Object} [options={}] An object with options.
                                                                                                +      @param {String} [options.dialect='mysql'] The dialect of the relational database.
                                                                                                +      @param {String} [options.host='localhost'] The host of the relational database.
                                                                                                +      @param {Integer} [options.port=3306] The port of the relational database.
                                                                                                +      @param {String} [options.protocol='tcp'] The protocol of the relational database.
                                                                                                +      @param {Object} [options.define={}] Options, which shall be default for every model definition.
                                                                                                +      @param {Object} [options.query={}] I have absolutely no idea.
                                                                                                +      @param {Object} [options.sync={}] Options, which shall be default for every `sync` call.
                                                                                                +      @param {Function} [options.logging=console.log] A function that gets executed everytime Sequelize would log something.
                                                                                                +      @param {Boolean} [options.omitNull=false] A flag that defines if null values should be passed to SQL queries or not.
                                                                                                +      @param {Boolean} [options.queue=true] I have absolutely no idea.
                                                                                                +      @param {Boolean} [options.native=false] A flag that defines if native library shall be used or not.
                                                                                                +      @param {Boolean} [options.replication=false] I have absolutely no idea.
                                                                                                +      @param {Object} [options.pool={}] Something.
                                                                                                +
                                                                                                +    @example
                                                                                                +        // without password and options
                                                                                                +        var sequelize = new Sequelize('database', 'username')
                                                                                                +
                                                                                                +        // without options
                                                                                                +        var sequelize = new Sequelize('database', 'username', 'password')
                                                                                                +
                                                                                                +        // without password / with blank password
                                                                                                +        var sequelize = new Sequelize('database', 'username', null, {})
                                                                                                +
                                                                                                +        // with password and options
                                                                                                +        var sequelize = new Sequelize('my_database', 'john', 'doe', {})
                                                                                                +
                                                                                                +    @class Sequelize
                                                                                                +    @constructor
                                                                                                +  */
                                                                                                +  var Sequelize = function(database, username, password, options) {
                                                                                                +    this.options = Utils._.extend({
                                                                                                +      dialect: 'mysql',
                                                                                                +      host: 'localhost',
                                                                                                +      port: 3306,
                                                                                                +      protocol: 'tcp',
                                                                                                +      define: {},
                                                                                                +      query: {},
                                                                                                +      sync: {},
                                                                                                +      logging: console.log,
                                                                                                +      omitNull: false,
                                                                                                +      queue: true,
                                                                                                +      native: false,
                                                                                                +      replication: false,
                                                                                                +      pool: {}
                                                                                                +    }, options || {})
                                                                                                +
                                                                                                +    if (this.options.logging === true) {
                                                                                                +      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
                                                                                                +      this.options.logging = console.log
                                                                                                +    }
                                                                                                +
                                                                                                +    this.config = {
                                                                                                +      database: database,
                                                                                                +      username: username,
                                                                                                +      password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
                                                                                                +      host    : this.options.host,
                                                                                                +      port    : this.options.port,
                                                                                                +      pool    : this.options.pool,
                                                                                                +      protocol: this.options.protocol,
                                                                                                +      queue   : this.options.queue,
                                                                                                +      native  : this.options.native,
                                                                                                +      replication: this.options.replication,
                                                                                                +      maxConcurrentQueries: this.options.maxConcurrentQueries
                                                                                                +    }
                                                                                                +
                                                                                                +    var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")
                                                                                                +
                                                                                                +    this.daoFactoryManager = new DAOFactoryManager(this)
                                                                                                +    this.connectorManager  = new ConnectorManager(this, this.config)
                                                                                                +
                                                                                                +    this.importCache = {}
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +    Reference to Utils
                                                                                                +  */
                                                                                                +  Sequelize.Utils = Utils
                                                                                                +
                                                                                                +  for (var dataType in DataTypes) {
                                                                                                +    Sequelize[dataType] = DataTypes[dataType]
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +   Returns an instance of QueryInterface.
                                                                                                +
                                                                                                +   @method getQueryInterface
                                                                                                +   @return {QueryInterface} An instance (singleton) of QueryInterface.
                                                                                                +   */
                                                                                                +  Sequelize.prototype.getQueryInterface = function() {
                                                                                                +    this.queryInterface = this.queryInterface || new QueryInterface(this)
                                                                                                +    return this.queryInterface
                                                                                                +  }
                                                                                                +
                                                                                                +  /**
                                                                                                +   Returns an instance (singleton) of Migrator.
                                                                                                +
                                                                                                +   @method getMigrator
                                                                                                +   @param {Object} [options={}] Some options
                                                                                                +   @param {Boolean} [force=false] A flag that defines if the migrator should get instantiated or not.
                                                                                                +   @return {Migrator} An instance of Migrator.
                                                                                                +   */
                                                                                                +  Sequelize.prototype.getMigrator = function(options, force) {
                                                                                                +    var Migrator = require("./migrator")
                                                                                                +
                                                                                                +    if (force) {
                                                                                                +      this.migrator = new Migrator(this, options)
                                                                                                +    } else {
                                                                                                +      this.migrator = this.migrator || new Migrator(this, options)
                                                                                                +    }
                                                                                                +
                                                                                                +    return this.migrator
                                                                                                +  }
                                                                                                +
                                                                                                +  Sequelize.prototype.define = function(daoName, attributes, options) {
                                                                                                +    options = options || {}
                                                                                                +    var globalOptions = this.options
                                                                                                +
                                                                                                +    if (globalOptions.define) {
                                                                                                +      options = Utils._.extend({}, globalOptions.define, options)
                                                                                                +      Utils._(['classMethods', 'instanceMethods']).each(function(key) {
                                                                                                +        if (globalOptions.define[key]) {
                                                                                                +          options[key] = options[key] || {}
                                                                                                +          Utils._.extend(options[key], globalOptions.define[key])
                                                                                                +        }
                                                                                                +      })
                                                                                                +    }
                                                                                                +    options.omitNull = globalOptions.omitNull
                                                                                                +
                                                                                                +    var factory = new DAOFactory(daoName, attributes, options)
                                                                                                +    this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
                                                                                                +    return factory
                                                                                                +  }
                                                                                                +
                                                                                                +  Sequelize.prototype.isDefined = function(daoName) {
                                                                                                +    var daos = this.daoFactoryManager.daos
                                                                                                +    return (daos.filter(function(dao) { return dao.name === daoName }).length !== 0)
                                                                                                +  }
                                                                                                +
                                                                                                +  Sequelize.prototype.import = function(path) {
                                                                                                +    if (!this.importCache[path]) {
                                                                                                +      var defineCall = require(path)
                                                                                                +      this.importCache[path] = defineCall(this, DataTypes)
                                                                                                +    }
                                                                                                +
                                                                                                +    return this.importCache[path]
                                                                                                +  }
                                                                                                +
                                                                                                +  Sequelize.prototype.migrate = function(options) {
                                                                                                +    this.getMigrator().migrate(options)
                                                                                                +  }
                                                                                                +
                                                                                                +  Sequelize.prototype.query = function(sql, callee, options, replacements) {
                                                                                                +    if (arguments.length === 4) {
                                                                                                +      sql = Utils.format([sql].concat(replacements))
                                                                                                +    } else if (arguments.length === 3) {
                                                                                                +      options = options
                                                                                                +    } else if (arguments.length === 2) {
                                                                                                +      options = {}
                                                                                                +    } else {
                                                                                                +      options = { raw: true }
                                                                                                +    }
                                                                                                +
                                                                                                +    options = Utils._.extend(Utils._.clone(this.options.query), options)
                                                                                                +    options = Utils._.defaults(options, {
                                                                                                +      logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log,
                                                                                                +      type: (sql.toLowerCase().indexOf('select') === 0) ? 'SELECT' : false
                                                                                                +    })
                                                                                                +
                                                                                                +    return this.connectorManager.query(sql, callee, options)
                                                                                                +  }
                                                                                                +
                                                                                                +  Sequelize.prototype.sync = function(options) {
                                                                                                +    options = options || {}
                                                                                                +
                                                                                                +    if (this.options.sync) {
                                                                                                +      options = Utils._.extend({}, this.options.sync, options)
                                                                                                +    }
                                                                                                +
                                                                                                +    var chainer = new Utils.QueryChainer()
                                                                                                +
                                                                                                +    this.daoFactoryManager.daos.forEach(function(dao) {
                                                                                                +      chainer.add(dao.sync(options))
                                                                                                +    })
                                                                                                +
                                                                                                +    return chainer.run()
                                                                                                +  }
                                                                                                +
                                                                                                +  Sequelize.prototype.drop = function() {
                                                                                                +    var self = this
                                                                                                +
                                                                                                +    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                +      var chainer = new Utils.QueryChainer
                                                                                                +
                                                                                                +      self.daoFactoryManager.daos.forEach(function(dao) { chainer.add(dao.drop()) })
                                                                                                +
                                                                                                +      chainer
                                                                                                +        .run()
                                                                                                +        .success(function() { emitter.emit('success', null) })
                                                                                                +        .error(function(err) { emitter.emit('error', err) })
                                                                                                +    }).run()
                                                                                                +  }
                                                                                                +
                                                                                                +  return Sequelize
                                                                                                +})()
                                                                                                +
                                                                                                +    
                                                                                                +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                + + + + + + + + + + diff --git a/docs/index.html b/docs/index.html index 5655e17894c5..15ab53a535bb 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,193 +1,126 @@ -Sequelize

                                                                                                Sequelize

                                                                                                index.html

                                                                                                \ No newline at end of file + + + + + + + + + + + + + +
                                                                                                +
                                                                                                +
                                                                                                + +

                                                                                                + +
                                                                                                +
                                                                                                + API Docs for: +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + Show: + + + + + + + +
                                                                                                + + +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +

                                                                                                + Browse to a module or class using the sidebar to view its API documentation. +

                                                                                                + +

                                                                                                Keyboard Shortcuts

                                                                                                + +
                                                                                                  +
                                                                                                • Press s to focus the API search box.

                                                                                                • + +
                                                                                                • Use Up and Down to select classes, modules, and search results.

                                                                                                • + +
                                                                                                • With the API search box or sidebar focused, use -Left or -Right to switch sidebar tabs.

                                                                                                • + +
                                                                                                • With the API search box or sidebar focused, use Ctrl+Left and Ctrl+Right to switch sidebar tabs.

                                                                                                • +
                                                                                                +
                                                                                                +
                                                                                                + + + +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                + + + + + + + + + + diff --git a/docs/migration.js.html b/docs/migration.js.html deleted file mode 100644 index 451555136c36..000000000000 --- a/docs/migration.js.html +++ /dev/null @@ -1,261 +0,0 @@ -Sequelize

                                                                                                Sequelize

                                                                                                declaration

                                                                                                moment

                                                                                                moment
                                                                                                  var moment         = require("moment")
                                                                                                  -  , Utils          = require("./utils")
                                                                                                  -  , DataTypes      = require("./data-types")
                                                                                                  -  , QueryInterface = require("./query-interface")
                                                                                                  -
                                                                                                  -module.exports = (function() {
                                                                                                  -  var Migration = function(migrator, path) {
                                                                                                  -    var split = path.split('/')
                                                                                                  -
                                                                                                  -    this.migrator       = migrator
                                                                                                  -    this.path           = path
                                                                                                  -    this.filename       = Utils._.last(this.path.split('/'))
                                                                                                  -
                                                                                                  -    var parsed          = Migration.parseFilename(this.filename)
                                                                                                  -
                                                                                                  -    this.migrationId    = parsed.id
                                                                                                  -    this.date           = parsed.date;
                                                                                                  -    this.queryInterface = this.migrator.queryInterface
                                                                                                  -    this.undoneMethods  = 0
                                                                                                  -  }
                                                                                                  -
                                                                                                  -  ///////////////
                                                                                                  -  // static /////
                                                                                                  -  ///////////////
                                                                                                  -
                                                                                                  -  Migration.parseFilename = function(s) {
                                                                                                  -    var matches = s.match(/^((\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}))[-_].+/)
                                                                                                  -
                                                                                                  -    if(matches === null) {
                                                                                                  -      throw new Error(s + ' is not a valid migration name! Use YYYYMMDDHHmmss-migration-name format.')
                                                                                                  -    }
                                                                                                  -
                                                                                                  -    return {
                                                                                                  -      id: parseInt(matches[1]),
                                                                                                  -      date: moment(matches.slice(2, 8).join('-'), 'YYYYMMDDHHmmss')
                                                                                                  -    }
                                                                                                  -  }
                                                                                                  -
                                                                                                  -  Migration.migrationHasInterfaceCalls = function(func) {
                                                                                                  -    var functionString = Utils.removeCommentsFromFunctionString(func.toString())
                                                                                                  -      , hasCalls       = false
                                                                                                  -
                                                                                                  -    for(var method in QueryInterface.prototype) {
                                                                                                  -      var regex = new RegExp('[\\s\\n\\r]*\\.[\\s\\n\\r]*' + method)
                                                                                                  -      hasCalls = hasCalls || regex.test(functionString)
                                                                                                  -    }
                                                                                                  -
                                                                                                  -    return hasCalls
                                                                                                  -  }
                                                                                                  -
                                                                                                  -  ///////////////
                                                                                                  -  // member /////
                                                                                                  -  ///////////////
                                                                                                  -
                                                                                                  -  Object.defineProperty(Migration.prototype, 'migration', {
                                                                                                  -    get: function() {
                                                                                                  -      return require(this.path)
                                                                                                  -    }
                                                                                                  -  })
                                                                                                  -
                                                                                                  -  Migration.prototype.execute = function(options) {
                                                                                                  -    var self = this
                                                                                                  -
                                                                                                  -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                  -      options = Utils._.extend({
                                                                                                  -        method: 'up'
                                                                                                  -      }, options || {})
                                                                                                  -
                                                                                                  -      var onSuccess = function() { emitter.emit('success', null) }
                                                                                                  -        , func      = self.migration[options.method]
                                                                                                  -
                                                                                                  -      extendMigrationWithQueryInterfaceMethods.call(self, onSuccess)
                                                                                                  -      func.call(null, self, DataTypes)
                                                                                                  -
                                                                                                  -      if(!Migration.migrationHasInterfaceCalls(func))
                                                                                                  -        onSuccess()
                                                                                                  -    }).run()
                                                                                                  -  }
                                                                                                  -
                                                                                                  -  Migration.prototype.isBefore = function(dateString, options) {
                                                                                                  -    options = Utils._.extend({
                                                                                                  -      withoutEquals: false
                                                                                                  -    }, options || {})
                                                                                                  -
                                                                                                  -    var date = Migration.parseFilename(dateString.toString() + '-foo.js').date
                                                                                                  -
                                                                                                  -    return options.withoutEqual ? (date > this.date) : (date >= this.date)
                                                                                                  -  }
                                                                                                  -
                                                                                                  -  Migration.prototype.isAfter = function(dateString, options) {
                                                                                                  -    options = Utils._.extend({
                                                                                                  -      withoutEquals: false
                                                                                                  -    }, options || {})
                                                                                                  -
                                                                                                  -    var date = Migration.parseFilename(dateString.toString() + '-foo.js').date
                                                                                                  -
                                                                                                  -    return options.withoutEqual ? (date < this.date) : (date <= this.date)
                                                                                                  -  }
                                                                                                  -
                                                                                                  -  // extends the Migration prototype with all methods of QueryInterface.prototype
                                                                                                  -  // with additional tracking of start and finish. this is done in order to minimize
                                                                                                  -  // asynchronous handling in migrations
                                                                                                  -  var extendMigrationWithQueryInterfaceMethods = function(callback) {
                                                                                                  -    var self = this
                                                                                                  -
                                                                                                  -    for(var method in QueryInterface.prototype) {
                                                                                                  -      (function(_method) {
                                                                                                  -        self[_method] = function() {
                                                                                                  -          var emitter = self.QueryInterface
                                                                                                  -            , args    = Utils._.map(arguments, function(arg, _) { return arg })
                                                                                                  -
                                                                                                  -          self.undoneMethods++
                                                                                                  -
                                                                                                  -          // bind listeners to the query interface
                                                                                                  -          // the event will have the same name like the method
                                                                                                  -          self.queryInterface.on(_method, function(err) {
                                                                                                  -            self.undoneMethods--
                                                                                                  -            if(err)
                                                                                                  -              throw new Error(err)
                                                                                                  -            else
                                                                                                  -              (self.undoneMethods == 0) && callback && callback()
                                                                                                  -          })
                                                                                                  -
                                                                                                  -          self.queryInterface[_method].apply(self.queryInterface, args)
                                                                                                  -        }
                                                                                                  -      })(method)
                                                                                                  -    }
                                                                                                  -  }
                                                                                                  -
                                                                                                  -  return Migration
                                                                                                  -})()
                                                                                                  \ No newline at end of file diff --git a/docs/migrator.js.html b/docs/migrator.js.html deleted file mode 100644 index 788e2cea7654..000000000000 --- a/docs/migrator.js.html +++ /dev/null @@ -1,359 +0,0 @@ -Sequelize

                                                                                                  Sequelize

                                                                                                    const fs     = require("fs")
                                                                                                    -    , path   = require("path")
                                                                                                    -    , moment = require("moment")
                                                                                                    -
                                                                                                    -var Utils          = require("./utils")
                                                                                                    -  , Migration      = require("./migration")
                                                                                                    -  , DataTypes      = require("./data-types")
                                                                                                    -
                                                                                                    -module.exports = (function() {
                                                                                                    -  var Migrator = function(sequelize, options) {
                                                                                                    -    this.sequelize = sequelize
                                                                                                    -    this.options   = Utils._.extend({
                                                                                                    -      path: __dirname + '/../migrations',
                                                                                                    -      from: null,
                                                                                                    -      to: null,
                                                                                                    -      logging: console.log
                                                                                                    -    }, options || {})
                                                                                                    -
                                                                                                    -    if(this.options.logging === true) {
                                                                                                    -      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
                                                                                                    -      this.options.logging = console.log
                                                                                                    -    }
                                                                                                    -
                                                                                                    -    if(this.options.logging == console.log) {
                                                                                                    -      // using just console.log will break in node < 0.6
                                                                                                    -      this.options.logging = function(s) { console.log(s) }
                                                                                                    -    }
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  Object.defineProperty(Migrator.prototype, "queryInterface", {
                                                                                                    -    get: function() {
                                                                                                    -      return this.sequelize.getQueryInterface()
                                                                                                    -    }
                                                                                                    -  })
                                                                                                    -
                                                                                                    -  Migrator.prototype.migrate = function(options) {
                                                                                                    -    var self = this
                                                                                                    -
                                                                                                    -    options = Utils._.extend({
                                                                                                    -      method: 'up'
                                                                                                    -    }, options || {})
                                                                                                    -
                                                                                                    -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                    -      self.getUndoneMigrations(function(err, migrations) {
                                                                                                    -        if(err) {
                                                                                                    -          emitter.emit('error', err)
                                                                                                    -        } else {
                                                                                                    -          var chainer = new Utils.QueryChainer
                                                                                                    -            , from    = migrations[0]
                                                                                                    -
                                                                                                    -          if(options.method == 'down') {
                                                                                                    -            migrations.reverse()
                                                                                                    -            from = migrations[0]
                                                                                                    -          }
                                                                                                    -
                                                                                                    -          migrations.forEach(function(migration) {
                                                                                                    -            chainer.add(migration, 'execute', [options], {
                                                                                                    -              before: function(migration) {
                                                                                                    -                if(self.options.logging !== false)
                                                                                                    -                  self.options.logging('Executing migration: ' + migration.filename)
                                                                                                    -              },
                                                                                                    -              after: function(migration) {
                                                                                                    -                if(self.options.logging !== false)
                                                                                                    -                  self.options.logging('Executed migration: ' + migration.filename)
                                                                                                    -              },
                                                                                                    -              success: function(migration, callback) {
                                                                                                    -                if(options.method == 'down')
                                                                                                    -                  deleteUndoneMigration.call(self, from, migration, callback)
                                                                                                    -                else
                                                                                                    -                  saveSuccessfulMigration.call(self, from, migration, callback)
                                                                                                    -              }
                                                                                                    -            })
                                                                                                    -          })
                                                                                                    -
                                                                                                    -          chainer
                                                                                                    -            .runSerially({ skipOnError: true })
                                                                                                    -            .success(function() { emitter.emit('success', null) })
                                                                                                    -            .error(function(err) { emitter.emit('error', err) })
                                                                                                    -        }
                                                                                                    -      })
                                                                                                    -    }).run()
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  Migrator.prototype.getUndoneMigrations = function(callback)  {
                                                                                                    -    var self = this
                                                                                                    -
                                                                                                    -    var filterFrom = function(migrations, from, callback, options) {
                                                                                                    -      var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
                                                                                                    -      callback && callback(null, result)
                                                                                                    -    }
                                                                                                    -    var filterTo = function(migrations, to, callback, options) {
                                                                                                    -      var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
                                                                                                    -      callback && callback(null, result)
                                                                                                    -    }
                                                                                                    -
                                                                                                    -    var migrationFiles = fs.readdirSync(this.options.path)
                                                                                                    -
                                                                                                    -    var migrations = migrationFiles.map(function(file) {
                                                                                                    -      return new Migration(self, self.options.path + '/' + file)
                                                                                                    -    })
                                                                                                    -
                                                                                                    -    migrations = migrations.sort(function(a,b){
                                                                                                    -      return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
                                                                                                    -    })
                                                                                                    -
                                                                                                    -    if(this.options.from) {
                                                                                                    -      filterFrom(migrations, this.options.from, function(err, migrations) {
                                                                                                    -        if(self.options.to)
                                                                                                    -          filterTo(migrations, self.options.to, callback)
                                                                                                    -        else
                                                                                                    -          callback && callback(null, migrations)
                                                                                                    -      })
                                                                                                    -    } else {
                                                                                                    -      getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
                                                                                                    -        if(lastMigrationId) {
                                                                                                    -          filterFrom(migrations, lastMigrationId, function(err, migrations) {
                                                                                                    -            if(self.options.to)
                                                                                                    -              filterTo(migrations, self.options.to, callback)
                                                                                                    -            else
                                                                                                    -              callback && callback(null, migrations)
                                                                                                    -          }, { withoutEqual: true })
                                                                                                    -        } else {
                                                                                                    -          if(self.options.to)
                                                                                                    -            filterTo(migrations, self.options.to, callback)
                                                                                                    -          else
                                                                                                    -            callback && callback(null, migrations)
                                                                                                    -        }
                                                                                                    -      }).error(function(err) {
                                                                                                    -        callback && callback(err, null)
                                                                                                    -      })
                                                                                                    -    }
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  Migrator.prototype.findOrCreateSequelizeMetaDAO = function(syncOptions) {
                                                                                                    -    var self = this
                                                                                                    -
                                                                                                    -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                    -      var storedDAO   = self.sequelize.daoFactoryManager.getDAO('SequelizeMeta')
                                                                                                    -        , SequelizeMeta = storedDAO
                                                                                                    -
                                                                                                    -      if(!storedDAO) {
                                                                                                    -        SequelizeMeta = self.sequelize.define('SequelizeMeta', {
                                                                                                    -          from: DataTypes.STRING,
                                                                                                    -          to:   DataTypes.STRING
                                                                                                    -        }, {
                                                                                                    -          timestamps: false
                                                                                                    -        })
                                                                                                    -      }
                                                                                                    -
                                                                                                    -      // force sync when model has newly created or if syncOptions are passed
                                                                                                    -      if(!storedDAO || syncOptions) {
                                                                                                    -        SequelizeMeta
                                                                                                    -          .sync(syncOptions || {})
                                                                                                    -          .success(function() { emitter.emit('success', SequelizeMeta) })
                                                                                                    -          .error(function(err) { emitter.emit('error', err) })
                                                                                                    -      } else {
                                                                                                    -        emitter.emit('success', SequelizeMeta)
                                                                                                    -      }
                                                                                                    -    }).run()
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  // private
                                                                                                    -
                                                                                                    -  var getLastMigrationFromDatabase = function() {
                                                                                                    -    var self = this
                                                                                                    -
                                                                                                    -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                    -      self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
                                                                                                    -        SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) {
                                                                                                    -          emitter.emit('success', meta ? meta : null)
                                                                                                    -        }).error(function(err) { emitter.emit('error', err) })
                                                                                                    -      }).error(function(err) { emitter.emit(err) })
                                                                                                    -    }).run()
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  var getLastMigrationIdFromDatabase = function() {
                                                                                                    -    var self = this
                                                                                                    -
                                                                                                    -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                    -      getLastMigrationFromDatabase.call(self)
                                                                                                    -        .success(function(meta) {
                                                                                                    -          emitter.emit('success', meta ? meta.to : null)
                                                                                                    -        })
                                                                                                    -        .error(function(err) {
                                                                                                    -          emitter.emit('error', err)
                                                                                                    -        })
                                                                                                    -    }).run()
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  var getFormattedDateString = function(s) {
                                                                                                    -    var result = null
                                                                                                    -
                                                                                                    -    try {
                                                                                                    -      result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
                                                                                                    -    } catch(e) {
                                                                                                    -      throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
                                                                                                    -    }
                                                                                                    -
                                                                                                    -    return result
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  var stringToDate = function(s) {
                                                                                                    -    return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  var saveSuccessfulMigration = function(from, to, callback) {
                                                                                                    -    var self = this
                                                                                                    -
                                                                                                    -    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
                                                                                                    -      SequelizeMeta
                                                                                                    -        .create({ from: from.migrationId, to: to.migrationId })
                                                                                                    -        .success(callback)
                                                                                                    -    })
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  var deleteUndoneMigration = function(from, to, callback) {
                                                                                                    -    var self = this
                                                                                                    -
                                                                                                    -    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
                                                                                                    -      SequelizeMeta
                                                                                                    -        .find({ from: from.migrationId, to: to.migrationId })
                                                                                                    -        .success(function(meta) {
                                                                                                    -          meta.destroy().success(callback)
                                                                                                    -        })
                                                                                                    -    })
                                                                                                    -  }
                                                                                                    -
                                                                                                    -  return Migrator
                                                                                                    -})()
                                                                                                    \ No newline at end of file diff --git a/docs/modules/Sequelize.html b/docs/modules/Sequelize.html new file mode 100644 index 000000000000..df801ceeab8a --- /dev/null +++ b/docs/modules/Sequelize.html @@ -0,0 +1,155 @@ + + + + + Sequelize + + + + + + + + +
                                                                                                    +
                                                                                                    +
                                                                                                    + +

                                                                                                    + +
                                                                                                    +
                                                                                                    + API Docs for: +
                                                                                                    +
                                                                                                    +
                                                                                                    + +
                                                                                                    + +
                                                                                                    +
                                                                                                    +
                                                                                                    + Show: + + + + + + + +
                                                                                                    + + +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Sequelize Module

                                                                                                    +
                                                                                                    + + + + + +
                                                                                                    + Defined in: lib/sequelize.js:12 +
                                                                                                    + + + +
                                                                                                    + + + +
                                                                                                    +

                                                                                                    The entry point.

                                                                                                    +
                                                                                                    + + + +
                                                                                                    +
                                                                                                    + +

                                                                                                    This module provides the following classes:

                                                                                                    + + + +
                                                                                                    + +
                                                                                                    + +
                                                                                                    +
                                                                                                    + +
                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    + + + + + + + + + + diff --git a/docs/modules/index.html b/docs/modules/index.html new file mode 100644 index 000000000000..487fe15b2ad0 --- /dev/null +++ b/docs/modules/index.html @@ -0,0 +1,10 @@ + + + + Redirector + + + + Click here to redirect + + diff --git a/docs/query-chainer.js.html b/docs/query-chainer.js.html deleted file mode 100644 index 25cbcb95a163..000000000000 --- a/docs/query-chainer.js.html +++ /dev/null @@ -1,272 +0,0 @@ -Sequelize

                                                                                                    Sequelize

                                                                                                    declaration

                                                                                                    Utils

                                                                                                    Utils
                                                                                                      var Utils = require("./utils")
                                                                                                      -
                                                                                                      -module.exports = (function() {
                                                                                                      -  var QueryChainer = function(emitters) {
                                                                                                      -    var self = this
                                                                                                      -
                                                                                                      -    this.finishedEmits  = 0
                                                                                                      -    this.emitters       = []
                                                                                                      -    this.serials        = []
                                                                                                      -    this.fails          = []
                                                                                                      -    this.serialResults  = []
                                                                                                      -    this.emitterResults = []
                                                                                                      -    this.finished       = false
                                                                                                      -    this.wasRunning     = false
                                                                                                      -    this.eventEmitter   = null
                                                                                                      -
                                                                                                      -    emitters = emitters || []
                                                                                                      -    emitters.forEach(function(emitter) {
                                                                                                      -      if(Array.isArray(emitter)) {
                                                                                                      -        self.add.apply(self, emitter)
                                                                                                      -      } else {
                                                                                                      -        self.add(emitter)
                                                                                                      -      }
                                                                                                      -    })
                                                                                                      -  }
                                                                                                      -
                                                                                                      -  QueryChainer.prototype.add = function(emitterOrKlass, method, params, options) {
                                                                                                      -    if(!!method) {
                                                                                                      -      this.serials.push({ klass: emitterOrKlass, method: method, params: params, options: options })
                                                                                                      -    } else {
                                                                                                      -      observeEmitter.call(this, emitterOrKlass)
                                                                                                      -      this.emitters.push(emitterOrKlass)
                                                                                                      -    }
                                                                                                      -
                                                                                                      -    return this
                                                                                                      -  }
                                                                                                      -
                                                                                                      -  QueryChainer.prototype.run = function() {
                                                                                                      -    var self = this
                                                                                                      -    this.eventEmitter = new Utils.CustomEventEmitter(function() {
                                                                                                      -      self.wasRunning = true
                                                                                                      -      finish.call(self, 'emitterResults')
                                                                                                      -    })
                                                                                                      -    return this.eventEmitter.run()
                                                                                                      -  }
                                                                                                      -
                                                                                                      -  QueryChainer.prototype.runSerially = function(options) {
                                                                                                      -    var self       = this
                                                                                                      -      , serialCopy = Utils._.clone(this.serials)
                                                                                                      -
                                                                                                      -    options = Utils._.extend({
                                                                                                      -      skipOnError: false
                                                                                                      -    }, options)
                                                                                                      -
                                                                                                      -    var exec = function() {
                                                                                                      -      var serial = self.serials.pop()
                                                                                                      -
                                                                                                      -      if(serial) {
                                                                                                      -        serial.options = serial.options || {}
                                                                                                      -        serial.options.before && serial.options.before(serial.klass)
                                                                                                      -
                                                                                                      -        var onSuccess = function() {
                                                                                                      -          serial.options.after && serial.options.after(serial.klass)
                                                                                                      -          self.finishedEmits++
                                                                                                      -          exec()
                                                                                                      -        }
                                                                                                      -
                                                                                                      -        var onError = function(err) {
                                                                                                      -          serial.options.after && serial.options.after(serial.klass)
                                                                                                      -          self.finishedEmits++
                                                                                                      -          self.fails.push(err)
                                                                                                      -          exec()
                                                                                                      -        }
                                                                                                      -
                                                                                                      -        if(options.skipOnError && (self.fails.length > 0)) {
                                                                                                      -          onError('Skipped due to earlier error!')
                                                                                                      -        } else {
                                                                                                      -          var emitter = serial.klass[serial.method].apply(serial.klass, serial.params)
                                                                                                      -
                                                                                                      -          emitter.success(function(result) {
                                                                                                      -            self.serialResults[serialCopy.indexOf(serial)] = result
                                                                                                      -
                                                                                                      -            if(serial.options.success) {
                                                                                                      -              serial.options.success(serial.klass, onSuccess)
                                                                                                      -            } else {
                                                                                                      -              onSuccess()
                                                                                                      -            }
                                                                                                      -          }).error(onError)
                                                                                                      -        }
                                                                                                      -      } else {
                                                                                                      -        self.wasRunning = true
                                                                                                      -        finish.call(self, 'serialResults')
                                                                                                      -      }
                                                                                                      -    }
                                                                                                      -
                                                                                                      -    this.serials.reverse()
                                                                                                      -    this.eventEmitter = new Utils.CustomEventEmitter(exec)
                                                                                                      -    return this.eventEmitter.run()
                                                                                                      -  }
                                                                                                      -
                                                                                                      -  // private
                                                                                                      -
                                                                                                      -  var observeEmitter = function(emitter) {
                                                                                                      -    var self = this
                                                                                                      -
                                                                                                      -    emitter
                                                                                                      -      .success(function(result) {
                                                                                                      -        self.emitterResults[self.emitters.indexOf(emitter)] = result
                                                                                                      -        self.finishedEmits++
                                                                                                      -        finish.call(self, 'emitterResults')
                                                                                                      -      })
                                                                                                      -      .error(function(err) {
                                                                                                      -        self.finishedEmits++
                                                                                                      -        self.fails.push(err)
                                                                                                      -        finish.call(self, 'emitterResults')
                                                                                                      -      })
                                                                                                      -      .on('sql', function(sql) {
                                                                                                      -        if(self.eventEmitter) {
                                                                                                      -          self.eventEmitter.emit('sql', sql)
                                                                                                      -        }
                                                                                                      -      })
                                                                                                      -  }
                                                                                                      -
                                                                                                      -  var finish = function(resultsName) {
                                                                                                      -    this.finished = true
                                                                                                      -
                                                                                                      -    if(this.emitters.length > 0) {
                                                                                                      -      this.finished = (this.finishedEmits == this.emitters.length)
                                                                                                      -    } else if(this.serials.length > 0) {
                                                                                                      -      this.finished = (this.finishedEmits == this.serials.length)
                                                                                                      -    }
                                                                                                      -
                                                                                                      -    if(this.finished && this.wasRunning) {
                                                                                                      -      var status = (this.fails.length == 0 ? 'success' : 'error')
                                                                                                      -        , result = (this.fails.length == 0 ? this[resultsName] : this.fails)
                                                                                                      -
                                                                                                      -      this.eventEmitter.emit.apply(this.eventEmitter, [status, result].concat(result))
                                                                                                      -    }
                                                                                                      -  }
                                                                                                      -
                                                                                                      -  return QueryChainer
                                                                                                      -})()
                                                                                                      \ No newline at end of file diff --git a/docs/query-interface.js.html b/docs/query-interface.js.html deleted file mode 100644 index 76dc08524f3c..000000000000 --- a/docs/query-interface.js.html +++ /dev/null @@ -1,402 +0,0 @@ -Sequelize

                                                                                                      Sequelize

                                                                                                      declaration

                                                                                                      Utils

                                                                                                      Utils
                                                                                                        var Utils     = require('./utils')
                                                                                                        -  , DataTypes = require('./data-types')
                                                                                                        -
                                                                                                        -module.exports = (function() {
                                                                                                        -  var QueryInterface = function(sequelize) {
                                                                                                        -    this.sequelize = sequelize
                                                                                                        -    this.QueryGenerator = require('./dialects/' + this.sequelize.options.dialect + '/query-generator')
                                                                                                        -	 this.QueryGenerator.options = this.sequelize.options;
                                                                                                        -  }
                                                                                                        -  Utils.addEventEmitter(QueryInterface)
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.createTable = function(tableName, attributes, options) {
                                                                                                        -    var attributeHashes = {}
                                                                                                        -
                                                                                                        -    Utils._.each(attributes, function(dataTypeOrOptions, attributeName) {
                                                                                                        -      if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1)
                                                                                                        -        attributeHashes[attributeName] = { type: dataTypeOrOptions }
                                                                                                        -      else
                                                                                                        -        attributeHashes[attributeName] = dataTypeOrOptions
                                                                                                        -    })
                                                                                                        -
                                                                                                        -    attributes = this.QueryGenerator.attributesToSQL(attributeHashes)
                                                                                                        -
                                                                                                        -    var sql = this.QueryGenerator.createTableQuery(tableName, attributes, options)
                                                                                                        -    return queryAndEmit.call(this, sql, 'createTable')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.dropTable = function(tableName) {
                                                                                                        -    var sql = this.QueryGenerator.dropTableQuery(tableName)
                                                                                                        -    return queryAndEmit.call(this, sql, 'dropTable')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.dropAllTables = function() {
                                                                                                        -    var self = this
                                                                                                        -
                                                                                                        -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                        -      var chainer = new Utils.QueryChainer()
                                                                                                        -
                                                                                                        -      self.showAllTables().success(function(tableNames) {
                                                                                                        -        tableNames.forEach(function(tableName) {
                                                                                                        -          chainer.add(self.dropTable(tableName))
                                                                                                        -        })
                                                                                                        -        chainer
                                                                                                        -          .run()
                                                                                                        -          .success(function() {
                                                                                                        -            self.emit('dropAllTables', null)
                                                                                                        -            emitter.emit('success', null)
                                                                                                        -          })
                                                                                                        -          .error(function(err) {
                                                                                                        -            self.emit('dropAllTables', err)
                                                                                                        -            emitter.emit('error', err)
                                                                                                        -          })
                                                                                                        -      }).error(function(err) {
                                                                                                        -        self.emit('dropAllTables', err)
                                                                                                        -        emitter.emit('error', err)
                                                                                                        -      })
                                                                                                        -    }).run()
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.renameTable = function(before, after) {
                                                                                                        -    var sql = this.QueryGenerator.renameTableQuery(before, after)
                                                                                                        -    return queryAndEmit.call(this, sql, 'renameTable')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.showAllTables = function() {
                                                                                                        -    var self = this
                                                                                                        -
                                                                                                        -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                        -      var showTablesSql = self.QueryGenerator.showTablesQuery()
                                                                                                        -      self.sequelize.query(showTablesSql, null, { raw: true }).success(function(tableNames) {
                                                                                                        -        self.emit('showAllTables', null)
                                                                                                        -        emitter.emit('success', Utils._.flatten(tableNames))
                                                                                                        -      }).error(function(err) {
                                                                                                        -        self.emit('showAllTables', err)
                                                                                                        -        emitter.emit('error', err)
                                                                                                        -      })
                                                                                                        -    }).run()
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.describeTable = function(tableName) {
                                                                                                        -    var self = this
                                                                                                        -
                                                                                                        -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                        -      var sql;
                                                                                                        -      if (self.QueryGenerator.describeTableQuery) {
                                                                                                        -        sql = self.QueryGenerator.describeTableQuery(tableName)
                                                                                                        -      } else {
                                                                                                        -        sql = 'DESCRIBE `' + tableName + '`;'
                                                                                                        -      }
                                                                                                        -      self.sequelize.query(sql, null, { raw: true }).success(function(data) {
                                                                                                        -        emitter.emit('success', data)
                                                                                                        -      }).error(function(err) {
                                                                                                        -        emitter.emit('error', err)
                                                                                                        -      })
                                                                                                        -    }).run()
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.addColumn = function(tableName, attributeName, dataTypeOrOptions) {
                                                                                                        -    var attributes = {}
                                                                                                        -
                                                                                                        -    if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1)
                                                                                                        -      attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false }
                                                                                                        -    else
                                                                                                        -      attributes[attributeName] = dataTypeOrOptions
                                                                                                        -
                                                                                                        -    var options = this.QueryGenerator.attributesToSQL(attributes)
                                                                                                        -      , sql     = this.QueryGenerator.addColumnQuery(tableName, options)
                                                                                                        -
                                                                                                        -    return queryAndEmit.call(this, sql, 'addColumn')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.removeColumn = function(tableName, attributeName) {
                                                                                                        -    var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName)
                                                                                                        -    return queryAndEmit.call(this, sql, 'removeColumn')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.changeColumn = function(tableName, attributeName, dataTypeOrOptions) {
                                                                                                        -    var attributes = {}
                                                                                                        -
                                                                                                        -    if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1)
                                                                                                        -      attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false }
                                                                                                        -    else
                                                                                                        -      attributes[attributeName] = dataTypeOrOptions
                                                                                                        -
                                                                                                        -    var options = this.QueryGenerator.attributesToSQL(attributes)
                                                                                                        -      , sql     = this.QueryGenerator.changeColumnQuery(tableName, options)
                                                                                                        -
                                                                                                        -    return queryAndEmit.call(this, sql, 'changeColumn')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.renameColumn = function(tableName, attrNameBefore, attrNameAfter) {
                                                                                                        -    var self = this
                                                                                                        -
                                                                                                        -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                        -      self.describeTable(tableName).success(function(data) {
                                                                                                        -        data = data.filter(function(h) { return h.Field == attrNameBefore })[0]
                                                                                                        -
                                                                                                        -        var options =  {}
                                                                                                        -
                                                                                                        -        options[attrNameAfter] = {
                                                                                                        -          type: data.Type,
                                                                                                        -          allowNull: data.Null == 'YES',
                                                                                                        -          defaultValue: data.Default
                                                                                                        -        }
                                                                                                        -
                                                                                                        -        var sql = self.QueryGenerator.renameColumnQuery(tableName,
                                                                                                        -          attrNameBefore,
                                                                                                        -          self.QueryGenerator.attributesToSQL(options)
                                                                                                        -        )
                                                                                                        -
                                                                                                        -        self.sequelize.query(sql).success(function() {
                                                                                                        -          self.emit('renameColumn', null)
                                                                                                        -          emitter.emit('success', null)
                                                                                                        -        }).error(function(err) {
                                                                                                        -          self.emit('renameColumn', err)
                                                                                                        -          emitter.emit('error', err)
                                                                                                        -        })
                                                                                                        -      }).error(function(err) {
                                                                                                        -        self.emit('renameColumn', err)
                                                                                                        -        emitter.emit('error', err)
                                                                                                        -      })
                                                                                                        -    }).run()
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.addIndex = function(tableName, attributes, options) {
                                                                                                        -    var sql = this.QueryGenerator.addIndexQuery(tableName, attributes, options)
                                                                                                        -    return queryAndEmit.call(this, sql, 'addIndex')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.showIndex = function(tableName, options) {
                                                                                                        -    var sql = this.QueryGenerator.showIndexQuery(tableName, options)
                                                                                                        -    return queryAndEmit.call(this, sql, 'showIndex')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.removeIndex = function(tableName, indexNameOrAttributes) {
                                                                                                        -    var sql = this.QueryGenerator.removeIndexQuery(tableName, indexNameOrAttributes)
                                                                                                        -    return queryAndEmit.call(this, sql, "removeIndex")
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.insert = function(dao, tableName, values) {
                                                                                                        -    var sql = this.QueryGenerator.insertQuery(tableName, values)
                                                                                                        -    return queryAndEmit.call(this, [sql, dao], 'insert', {
                                                                                                        -      success: function(obj) { obj.isNewRecord = false }
                                                                                                        -    })
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.update = function(dao, tableName, values, identifier) {
                                                                                                        -    var sql = this.QueryGenerator.updateQuery(tableName, values, identifier)
                                                                                                        -    return queryAndEmit.call(this, [sql, dao], 'update')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.delete = function(dao, tableName, identifier) {
                                                                                                        -    var sql = this.QueryGenerator.deleteQuery(tableName, identifier)
                                                                                                        -    return queryAndEmit.call(this, [sql, dao], 'delete')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
                                                                                                        -    var sql = this.QueryGenerator.selectQuery(tableName, options)
                                                                                                        -    return queryAndEmit.call(this, [sql, factory, queryOptions], 'select')
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelector) {
                                                                                                        -    var self = this
                                                                                                        -
                                                                                                        -    if(attributeSelector == undefined) {
                                                                                                        -      throw new Error('Please pass an attribute selector!')
                                                                                                        -    }
                                                                                                        -
                                                                                                        -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                        -      var sql = self.QueryGenerator.selectQuery(tableName, options)
                                                                                                        -        , qry = self.sequelize.query(sql, null, { plain: true, raw: true })
                                                                                                        -
                                                                                                        -      qry
                                                                                                        -        .success(function(data) {
                                                                                                        -          var result = data[attributeSelector]
                                                                                                        -
                                                                                                        -          if (options && options.parseInt) {
                                                                                                        -            result = parseInt(result)
                                                                                                        -          }
                                                                                                        -
                                                                                                        -          self.emit('rawSelect', null)
                                                                                                        -          emitter.emit('success', result)
                                                                                                        -        })
                                                                                                        -        .error(function(err) {
                                                                                                        -          self.emit('rawSelect', err)
                                                                                                        -          emitter.emit('error', err)
                                                                                                        -        })
                                                                                                        -        .on('sql', function(sql) {
                                                                                                        -          emitter.emit('sql', sql)
                                                                                                        -        })
                                                                                                        -    }).run()
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  // private
                                                                                                        -
                                                                                                        -  var queryAndEmit = function(sqlOrQueryParams, methodName, options) {
                                                                                                        -    var self = this
                                                                                                        -
                                                                                                        -    options = Utils._.extend({
                                                                                                        -      success: function(obj){},
                                                                                                        -      error: function(err){}
                                                                                                        -    }, options || {})
                                                                                                        -
                                                                                                        -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                        -      var query = null
                                                                                                        -
                                                                                                        -      if(Array.isArray(sqlOrQueryParams)) {
                                                                                                        -        query = self.sequelize.query.apply(self.sequelize, sqlOrQueryParams)
                                                                                                        -      } else {
                                                                                                        -        query = self.sequelize.query(sqlOrQueryParams)
                                                                                                        -      }
                                                                                                        -
                                                                                                        -      // append the query for better testing
                                                                                                        -      emitter.query = query
                                                                                                        -
                                                                                                        -      query.success(function(obj) {
                                                                                                        -        options.success && options.success(obj)
                                                                                                        -        self.emit(methodName, null)
                                                                                                        -        emitter.emit('success', obj)
                                                                                                        -      }).error(function(err) {
                                                                                                        -        options.error && options.error(err)
                                                                                                        -        self.emit(methodName, err)
                                                                                                        -        emitter.emit('error', err)
                                                                                                        -      })
                                                                                                        -      query.on('sql', function(sql) {
                                                                                                        -        emitter.emit('sql', sql)
                                                                                                        -      })
                                                                                                        -    }).run()
                                                                                                        -  }
                                                                                                        -
                                                                                                        -  return QueryInterface
                                                                                                        -})()
                                                                                                        \ No newline at end of file diff --git a/docs/sequelize.js.html b/docs/sequelize.js.html deleted file mode 100644 index ccb1a00ef7f1..000000000000 --- a/docs/sequelize.js.html +++ /dev/null @@ -1,270 +0,0 @@ -Sequelize

                                                                                                        Sequelize

                                                                                                        function

                                                                                                        Sequelize

                                                                                                        Sequelize()

                                                                                                        Main constructor of the project.

                                                                                                        - -
                                                                                                        Params:
                                                                                                        -
                                                                                                        -  - `database`
                                                                                                        -  - `username`
                                                                                                        -  - `password`, optional, default: null
                                                                                                        -  - `options`, optional, default: {}
                                                                                                        -
                                                                                                        -Examples:
                                                                                                        -
                                                                                                        -    mymodule.write('foo')
                                                                                                        -    mymodule.write('foo', { stream: process.stderr })
                                                                                                        -
                                                                                                          var Sequelize = function(database, username, password, options) {
                                                                                                          -    this.options = Utils._.extend({
                                                                                                          -      dialect: 'mysql',
                                                                                                          -      host: 'localhost',
                                                                                                          -      port: 3306,
                                                                                                          -      protocol: 'tcp',
                                                                                                          -      define: {},
                                                                                                          -      query: {},
                                                                                                          -      sync: {},
                                                                                                          -      logging: console.log,
                                                                                                          -      omitNull: false
                                                                                                          -    }, options || {})
                                                                                                          -
                                                                                                          -    if(this.options.logging === true) {
                                                                                                          -      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
                                                                                                          -      this.options.logging = console.log
                                                                                                          -    }
                                                                                                          -
                                                                                                          -    this.config = {
                                                                                                          -      database: database,
                                                                                                          -      username: username,
                                                                                                          -      password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
                                                                                                          -      host    : this.options.host,
                                                                                                          -      port    : this.options.port,
                                                                                                          -      pool    : this.options.pool,
                                                                                                          -      protocol: this.options.protocol
                                                                                                          -    }
                                                                                                          -
                                                                                                          -    var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")
                                                                                                          -
                                                                                                          -    this.daoFactoryManager = new DAOFactoryManager(this)
                                                                                                          -    this.connectorManager  = new ConnectorManager(this, this.config)
                                                                                                          -
                                                                                                          -    this.importCache = {}
                                                                                                          -  }
                                                                                                          property

                                                                                                          Utils

                                                                                                          Sequelize.Utils

                                                                                                          Reference to Utils

                                                                                                            Sequelize.Utils = Utils
                                                                                                            -
                                                                                                            -  for (var dataType in DataTypes) {
                                                                                                            -    Sequelize[dataType] = DataTypes[dataType]
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.getQueryInterface = function() {
                                                                                                            -    this.queryInterface = this.queryInterface || new QueryInterface(this)
                                                                                                            -    return this.queryInterface
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.getMigrator = function(options, force) {
                                                                                                            -    if(force) {
                                                                                                            -      this.migrator = new Migrator(this, options)
                                                                                                            -    } else {
                                                                                                            -      this.migrator = this.migrator || new Migrator(this, options)
                                                                                                            -    }
                                                                                                            -
                                                                                                            -    return this.migrator
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.define = function(daoName, attributes, options) {
                                                                                                            -    options = options || {}
                                                                                                            -    if(this.options.define) {
                                                                                                            -      options = Sequelize.Utils.merge(options, this.options.define)
                                                                                                            -    }
                                                                                                            -    options.omitNull = this.options.omitNull
                                                                                                            -
                                                                                                            -    var factory = new DAOFactory(daoName, attributes, options)
                                                                                                            -    this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
                                                                                                            -    return factory
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.isDefined = function(daoName) {
                                                                                                            -    var daos = this.daoFactoryManager.daos
                                                                                                            -    return (daos.filter(function(dao) { return dao.name === daoName }).length !== 0)
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.import = function(path) {
                                                                                                            -    if (!this.importCache[path]) {
                                                                                                            -      var defineCall = require(path)
                                                                                                            -      this.importCache[path] = defineCall(this, DataTypes)
                                                                                                            -    }
                                                                                                            -
                                                                                                            -    return this.importCache[path]
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.migrate = function(options) {
                                                                                                            -    this.getMigrator().migrate(options)
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.query = function(sql, callee, options) {
                                                                                                            -    options = Utils._.extend(Utils._.clone(this.options.query), options || {})
                                                                                                            -    options = Utils._.extend(options, {
                                                                                                            -      logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log
                                                                                                            -    })
                                                                                                            -
                                                                                                            -    return this.connectorManager.query(sql, callee, options)
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.sync = function(options) {
                                                                                                            -    options = options || {}
                                                                                                            -
                                                                                                            -    if(this.options.sync) {
                                                                                                            -      options = Sequelize.Utils.merge(options, this.options.sync)
                                                                                                            -    }
                                                                                                            -
                                                                                                            -    var chainer = new Utils.QueryChainer()
                                                                                                            -
                                                                                                            -    this.daoFactoryManager.daos.forEach(function(dao) {
                                                                                                            -      chainer.add(dao.sync(options))
                                                                                                            -    })
                                                                                                            -
                                                                                                            -    return chainer.run()
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  Sequelize.prototype.drop = function() {
                                                                                                            -    var self = this
                                                                                                            -
                                                                                                            -    return new Utils.CustomEventEmitter(function(emitter) {
                                                                                                            -      var chainer = new Utils.QueryChainer
                                                                                                            -
                                                                                                            -      self.daoFactoryManager.daos.forEach(function(dao) { chainer.add(dao.drop()) })
                                                                                                            -
                                                                                                            -      chainer
                                                                                                            -        .run()
                                                                                                            -        .success(function() { emitter.emit('success', null) })
                                                                                                            -        .error(function(err) { emitter.emit('error', err) })
                                                                                                            -    }).run()
                                                                                                            -  }
                                                                                                            -
                                                                                                            -  return Sequelize
                                                                                                            -})()
                                                                                                            \ No newline at end of file diff --git a/docs/utils.js.html b/docs/utils.js.html deleted file mode 100644 index 4625d2238245..000000000000 --- a/docs/utils.js.html +++ /dev/null @@ -1,309 +0,0 @@ -Sequelize

                                                                                                            Sequelize

                                                                                                            declaration

                                                                                                            mysql

                                                                                                            mysql
                                                                                                              var mysql      = require("mysql")
                                                                                                              -  , connection = mysql.createConnection({})
                                                                                                              -  , util       = require("util")
                                                                                                              -  , DataTypes  = require("./data-types")
                                                                                                              -
                                                                                                              -var Utils = module.exports = {
                                                                                                              -  _: (function() {
                                                                                                              -    var _  = require("underscore")
                                                                                                              -      , _s = require('underscore.string')
                                                                                                              -
                                                                                                              -    _.mixin(_s.exports())
                                                                                                              -    _.mixin({
                                                                                                              -      includes: _s.include,
                                                                                                              -      camelizeIf: function(string, condition) {
                                                                                                              -        var result = string
                                                                                                              -
                                                                                                              -        if(condition) {
                                                                                                              -          result = _.camelize(string)
                                                                                                              -        }
                                                                                                              -
                                                                                                              -        return result
                                                                                                              -      },
                                                                                                              -      underscoredIf: function(string, condition) {
                                                                                                              -        var result = string
                                                                                                              -
                                                                                                              -        if(condition) {
                                                                                                              -          result = _.underscored(string)
                                                                                                              -        }
                                                                                                              -
                                                                                                              -        return result
                                                                                                              -      }
                                                                                                              -    })
                                                                                                              -
                                                                                                              -    return _
                                                                                                              -  })(),
                                                                                                              -  addEventEmitter: function(_class) {
                                                                                                              -    util.inherits(_class, require('events').EventEmitter)
                                                                                                              -  },
                                                                                                              -  TICK_CHAR: '`',
                                                                                                              -  addTicks: function(s) {
                                                                                                              -    return Utils.TICK_CHAR + Utils.removeTicks(s) + Utils.TICK_CHAR
                                                                                                              -  },
                                                                                                              -  removeTicks: function(s) {
                                                                                                              -    return s.replace("`", "")
                                                                                                              -  },
                                                                                                              -  escape: function(s) {
                                                                                                              -    return connection.escape(s).replace(/\\"/g, '"')
                                                                                                              -  },
                                                                                                              -  format: function(arr) {
                                                                                                              -    var query        = arr[0]
                                                                                                              -      , replacements = Utils._.compact(arr.map(function(obj) { return obj != query ? obj : null}))
                                                                                                              -
                                                                                                              -    return connection.format.apply(connection, [query, replacements])
                                                                                                              -  },
                                                                                                              -  isHash: function(obj) {
                                                                                                              -    return Utils._.isObject(obj) && !Utils._.isArray(obj);
                                                                                                              -  },
                                                                                                              -  toSqlDate: function(date) {
                                                                                                              -    return [
                                                                                                              -      [
                                                                                                              -        date.getFullYear(),
                                                                                                              -        ((date.getMonth() < 9 ? '0' : '') + (date.getMonth()+1)),
                                                                                                              -        ((date.getDate() < 10 ? '0' : '') + date.getDate())
                                                                                                              -      ].join("-"),
                                                                                                              -      date.toLocaleTimeString()
                                                                                                              -    ].join(" ")
                                                                                                              -  },
                                                                                                              -  argsArePrimaryKeys: function(args, primaryKeys) {
                                                                                                              -    var result = (args.length == Utils._.keys(primaryKeys).length)
                                                                                                              -    Utils._.each(args, function(arg) {
                                                                                                              -      if(result) {
                                                                                                              -        if(['number', 'string'].indexOf(typeof arg) > -1)
                                                                                                              -          result = true
                                                                                                              -        else
                                                                                                              -          result = (arg instanceof Date)
                                                                                                              -
                                                                                                              -      }
                                                                                                              -    })
                                                                                                              -    return result
                                                                                                              -  },
                                                                                                              -  combineTableNames: function(tableName1, tableName2) {
                                                                                                              -    return (tableName1.toLowerCase() < tableName2.toLowerCase()) ? (tableName1 + tableName2) : (tableName2 + tableName1)
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  singularize: function(s) {
                                                                                                              -    return Utils.Lingo.en.isSingular(s) ? s : Utils.Lingo.en.singularize(s)
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  pluralize: function(s) {
                                                                                                              -    return Utils.Lingo.en.isPlural(s) ? s : Utils.Lingo.en.pluralize(s)
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  merge: function(a, b){
                                                                                                              -    for(var key in b) {
                                                                                                              -      a[key] = b[key]
                                                                                                              -    }
                                                                                                              -    return a
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  removeCommentsFromFunctionString: function(s) {
                                                                                                              -    s = s.replace(/\s*(\/\/.*)/g, '')
                                                                                                              -    s = s.replace(/(\/\*[\n\r\s\S]*?\*\/)/mg, '')
                                                                                                              -
                                                                                                              -    return s
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  toDefaultValue: function(value) {
                                                                                                              -    return (value == DataTypes.NOW) ? new Date() : value
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  setAttributes: function(hash, identifier, instance, prefix) {
                                                                                                              -    prefix = prefix || ''
                                                                                                              -    if (this.isHash(identifier)) {
                                                                                                              -      this._.each(identifier, function(elem, key) {
                                                                                                              -        hash[prefix + key] = Utils._.isString(instance) ? instance : Utils._.isObject(instance) ? instance[elem.key || elem] : null
                                                                                                              -      })
                                                                                                              -    } else {
                                                                                                              -      hash[prefix + identifier] = Utils._.isString(instance) ? instance : Utils._.isObject(instance) ? instance.id : null
                                                                                                              -    }
                                                                                                              -
                                                                                                              -    return hash
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  removeNullValuesFromHash: function(hash, omitNull) {
                                                                                                              -    var result = hash
                                                                                                              -
                                                                                                              -    if(omitNull) {
                                                                                                              -      var _hash = {}
                                                                                                              -
                                                                                                              -      Utils._.each(hash, function(val, key) {
                                                                                                              -        if (key.match(/Id$/) || ((val !== null) && (val !== undefined))) {
                                                                                                              -          _hash[key] = val;
                                                                                                              -        }
                                                                                                              -      })
                                                                                                              -
                                                                                                              -      result = _hash
                                                                                                              -    }
                                                                                                              -
                                                                                                              -    return result
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  prependTableNameToHash: function(tableName, hash) {
                                                                                                              -    if (tableName) {
                                                                                                              -      var _hash = {}
                                                                                                              -
                                                                                                              -      for (var key in hash) {
                                                                                                              -        if (key.indexOf('.') === -1) {
                                                                                                              -          _hash[tableName + '.' + key] = hash[key]
                                                                                                              -        } else {
                                                                                                              -          _hash[key] = hash[key]
                                                                                                              -        }
                                                                                                              -      }
                                                                                                              -
                                                                                                              -      return _hash
                                                                                                              -    } else {
                                                                                                              -      return hash
                                                                                                              -    }
                                                                                                              -  },
                                                                                                              -
                                                                                                              -  inherit: function(subClass, superClass) {
                                                                                                              -    if (superClass.constructor == Function) {
                                                                                                              -      // Normal Inheritance
                                                                                                              -      subClass.prototype = new superClass();
                                                                                                              -      subClass.prototype.constructor = subClass;
                                                                                                              -      subClass.prototype.parent = superClass.prototype;
                                                                                                              -    } else {
                                                                                                              -      // Pure Virtual Inheritance
                                                                                                              -      subClass.prototype = superClass;
                                                                                                              -      subClass.prototype.constructor = subClass;
                                                                                                              -      subClass.prototype.parent = superClass;
                                                                                                              -    }
                                                                                                              -
                                                                                                              -    return subClass;
                                                                                                              -  }
                                                                                                              -}
                                                                                                              -
                                                                                                              -Utils.CustomEventEmitter = require("./emitters/custom-event-emitter")
                                                                                                              -Utils.QueryChainer = require("./query-chainer")
                                                                                                              -Utils.Lingo = require("lingo")
                                                                                                              \ No newline at end of file diff --git a/examples/Associations/app.js b/examples/Associations/app.js index 4ee7ad38d7ba..9a5f8fb512d1 100644 --- a/examples/Associations/app.js +++ b/examples/Associations/app.js @@ -5,11 +5,11 @@ 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. + The rest of the example is about setting and getting the associated data. */ var Sequelize = require(__dirname + "/../../index") - , config = require(__dirname + "/../../test/config") + , 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 }) @@ -36,13 +36,13 @@ sequelize.sync({force:true}).on('success', function() { .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) + console.log('my mom: ', mom.name) })}) person.setFather(father).on('success', function() { person.getFather().on('success', function(dad) { - console.log('my dad: ', dad.name) + 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 })) diff --git a/examples/Count/app.js b/examples/Count/app.js index a8346e10801b..37973827e8a5 100644 --- a/examples/Count/app.js +++ b/examples/Count/app.js @@ -1,5 +1,5 @@ var Sequelize = require(__dirname + "/../../index") - , config = require(__dirname + "/../../test/config") + , 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 }) @@ -8,12 +8,12 @@ var Person = sequelize.define('Person', { name: Sequelize.STRING }) 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) { diff --git a/examples/MinMax/app.js b/examples/MinMax/app.js index 22bdf16309e7..bce62d94174f 100644 --- a/examples/MinMax/app.js +++ b/examples/MinMax/app.js @@ -1,8 +1,8 @@ var Sequelize = require(__dirname + "/../../index") - , config = require(__dirname + "/../../test/config") + , config = require(__dirname + "/../../spec/config/config") , sequelize = new Sequelize(config.database, config.username, config.password, {logging: false}) -var Person = sequelize.define('Person', +var Person = sequelize.define('Person', { name: Sequelize.STRING, age : Sequelize.INTEGER @@ -12,12 +12,12 @@ var Person = sequelize.define('Person', 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) { diff --git a/examples/Performance/app.js b/examples/Performance/app.js index ddb5d5bc68f0..05208138b441 100644 --- a/examples/Performance/app.js +++ b/examples/Performance/app.js @@ -1,5 +1,5 @@ var Sequelize = require(__dirname + "/../../index") - , config = require("../../test/config") + , 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") @@ -10,16 +10,16 @@ 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") }) @@ -35,7 +35,7 @@ Person.sync({force: true}).on('success', function() { for(var i = 0; i < count; i++) { createPerson() } - + }).on('failure', function(err) { console.log(err) }) \ No newline at end of file diff --git a/examples/image-handling/app.js b/examples/image-handling/app.js index b7c0ab99e748..4b774b78b521 100644 --- a/examples/image-handling/app.js +++ b/examples/image-handling/app.js @@ -1,21 +1,28 @@ -var fs = require("fs") - , Sequelize = require("sequelize") - , sequelize = new Sequelize('sequelize_test', 'root', null, {logging: false}) - , Image = sequelize.define('Image', { data: Sequelize.TEXT }) +/* + Title: Default values -Image.sync({force: true}).on('success', function() { - console.log("reading image") - var image = fs.readFileSync(__dirname + '/source.png').toString("base64") - console.log("done\n") - - console.log("creating database entry") - Image.create({data: image}).on('success', function(img) { - console.log("done\n") - - console.log("writing file") - fs.writeFileSync(__dirname + '/target.png', img.data, "base64") - console.log("done\n") - - console.log("you might open the file ./target.png") + 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 diff --git a/examples/method-passing/app.js b/examples/method-passing/app.js index ae4d7223e8c3..58e1dcb9e8a5 100644 --- a/examples/method-passing/app.js +++ b/examples/method-passing/app.js @@ -1,14 +1,14 @@ /* 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 + "/../../test/config") + , config = require(__dirname + "/../../spec/config/config") , sequelize = new Sequelize(config.database, config.username, config.password, {logging: false}) -// model definition +// model definition var Task = sequelize.define("Task", { name: Sequelize.STRING, deadline: Sequelize.DATE, @@ -51,7 +51,7 @@ Task.sync({force: true}).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) { diff --git a/examples/sequelize-with-options/app.js b/examples/sequelize-with-options/app.js index df275a311ce6..b90fc340af9b 100644 --- a/examples/sequelize-with-options/app.js +++ b/examples/sequelize-with-options/app.js @@ -1,10 +1,10 @@ var Sequelize = require(__dirname + "/../../index") - , config = require(__dirname + "/../../test/config") + , 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 }) diff --git a/examples/using-multiple-model-files/app.js b/examples/using-multiple-model-files/app.js index ee6df5b3fc0e..d984a225f18d 100644 --- a/examples/using-multiple-model-files/app.js +++ b/examples/using-multiple-model-files/app.js @@ -1,12 +1,12 @@ var Sequelize = require(__dirname + "/../../index") - , config = require(__dirname + "/../../test/config") + , 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' }) diff --git a/index.js b/index.js index 753b8bad11f2..7c7f8a8426d1 100644 --- a/index.js +++ b/index.js @@ -1 +1,5 @@ +/** + The entry point. + @module Sequelize +**/ module.exports = require("./lib/sequelize") diff --git a/lib/associations/belongs-to.js b/lib/associations/belongs-to.js index 2e6582fccb20..ce82c607a852 100644 --- a/lib/associations/belongs-to.js +++ b/lib/associations/belongs-to.js @@ -1,5 +1,6 @@ var Utils = require("./../utils") , DataTypes = require('./../data-types') + , Helpers = require('./helpers') module.exports = (function() { var BelongsTo = function(srcDAO, targetDAO, options) { @@ -9,7 +10,7 @@ module.exports = (function() { this.options = options this.isSelfAssociation = (this.source.tableName == this.target.tableName) - if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) { + if (this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) { this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.source.options.underscored) } @@ -24,10 +25,11 @@ module.exports = (function() { this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.target.tableName) + "Id", this.source.options.underscored) newAttributes[this.identifier] = { type: DataTypes.INTEGER } + Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.target, this.source, this.options) Utils._.defaults(this.source.rawAttributes, newAttributes) // Sync attributes to DAO proto each time a new assoc is added - this.target.DAO.prototype.attributes = Object.keys(this.target.DAO.prototype.rawAttributes); + this.source.DAO.prototype.attributes = Object.keys(this.source.DAO.prototype.rawAttributes); return this } diff --git a/lib/associations/has-many-double-linked.js b/lib/associations/has-many-double-linked.js index 33a0641528fd..2cb7013df680 100644 --- a/lib/associations/has-many-double-linked.js +++ b/lib/associations/has-many-double-linked.js @@ -15,7 +15,7 @@ module.exports = (function() { //fully qualify where[self.__factory.connectorDAO.tableName+"."+self.__factory.identifier] = self.instance.id - var primaryKeys = Utils._.keys(self.__factory.connectorDAO.rawAttributes) + var primaryKeys = Object.keys(self.__factory.connectorDAO.rawAttributes) , foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0] where[self.__factory.connectorDAO.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+".id"} @@ -51,7 +51,11 @@ module.exports = (function() { var chainer = new Utils.QueryChainer , association = self.__factory.target.associations[self.__factory.associationAccessor] , foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier - , unassociatedObjects = newAssociations.filter(function(obj) { return !obj.equalsOneOf(oldAssociations) }) + , unassociatedObjects = newAssociations.filter(function (obj) { + return !Utils._.find(oldAssociations, function (old) { + return obj.id === old.id + }) + }) unassociatedObjects.forEach(function(unassociatedObject) { var attributes = {} @@ -69,6 +73,20 @@ module.exports = (function() { }) } + HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation) { + var attributes = {} + , association = this.__factory.target.associations[this.__factory.associationAccessor] + , foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier; + + attributes[this.__factory.identifier] = this.instance.id + attributes[foreignIdentifier] = newAssociation.id + + this.__factory.connectorDAO.create(attributes) + .success(function() { emitterProxy.emit('success', newAssociation) }) + .error(function(err) { emitterProxy.emit('error', err) }) + .on('sql', function(sql) { emitterProxy.emit('sql', sql) }) + } + // private var destroyObsoleteAssociations = function(oldAssociations, newAssociations) { @@ -77,15 +95,20 @@ module.exports = (function() { return new Utils.CustomEventEmitter(function(emitter) { var chainer = new Utils.QueryChainer() var foreignIdentifier = self.__factory.target.associations[self.__factory.associationAccessor].identifier - var obsoleteAssociations = oldAssociations.filter(function(obj) { return !obj.equalsOneOf(newAssociations) }) + var obsoleteAssociations = oldAssociations.filter(function (old) { + // Return only those old associations that are not found in new + return !Utils._.find(newAssociations, function (obj) { + return obj.id === old.id + }) + }) - if(obsoleteAssociations.length === 0) { + if (obsoleteAssociations.length === 0) { return emitter.emit('success', null) } obsoleteAssociations.forEach(function(associatedObject) { var where = {} - , primaryKeys = Utils._.keys(self.__factory.connectorDAO.rawAttributes) + , primaryKeys = Object.keys(self.__factory.connectorDAO.rawAttributes) , foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0] , notFoundEmitters = [] @@ -95,13 +118,13 @@ module.exports = (function() { self.__factory.connectorDAO .find({ where: where }) .success(function(connector) { - if(connector === null) { + if (connector === null) { notFoundEmitters.push(null) } else { chainer.add(connector.destroy()) } - if((chainer.emitters.length + notFoundEmitters.length) === obsoleteAssociations.length) { + if ((chainer.emitters.length + notFoundEmitters.length) === obsoleteAssociations.length) { // found all obsolete connectors and will delete them now chainer .run() diff --git a/lib/associations/has-many-single-linked.js b/lib/associations/has-many-single-linked.js index 4045df71e3cb..1380c8263c93 100644 --- a/lib/associations/has-many-single-linked.js +++ b/lib/associations/has-many-single-linked.js @@ -19,15 +19,25 @@ module.exports = (function() { var self = this , options = this.__factory.options , chainer = new Utils.QueryChainer() + , obsoleteAssociations = oldAssociations.filter(function (old) { + return !Utils._.find(newAssociations, function (obj) { + return obj.id === old.id + }) + }) + , unassociatedObjects = newAssociations.filter(function (obj) { + return !Utils._.find(oldAssociations, function (old) { + return obj.id === old.id + }) + }) // clear the old associations - oldAssociations.forEach(function(associatedObject) { + obsoleteAssociations.forEach(function(associatedObject) { associatedObject[self.__factory.identifier] = null chainer.add(associatedObject.save()) }) // set the new associations - newAssociations.forEach(function(associatedObject) { + unassociatedObjects.forEach(function(associatedObject) { associatedObject[self.__factory.identifier] = self.instance.id chainer.add(associatedObject.save()) }) @@ -38,5 +48,14 @@ module.exports = (function() { .error(function(err) { emitter.emit('error', err) }) } + HasManySingleLinked.prototype.injectAdder = function(emitterProxy, newAssociation) { + newAssociation[this.__factory.identifier] = this.instance.id + + newAssociation.save() + .success(function() { emitterProxy.emit('success', newAssociation) }) + .error(function(err) { emitterProxy.emit('error', err) }) + .on('sql', function(sql) { emitterProxy.emit('sql', sql) }) + } + return HasManySingleLinked })() diff --git a/lib/associations/has-many.js b/lib/associations/has-many.js index 865366eb9b62..e0b4e36b0734 100644 --- a/lib/associations/has-many.js +++ b/lib/associations/has-many.js @@ -1,5 +1,6 @@ var Utils = require("./../utils") , DataTypes = require('./../data-types') + , Helpers = require('./helpers') var HasManySingleLinked = require("./has-many-single-linked") , HasManyMultiLinked = require("./has-many-double-linked") @@ -41,7 +42,7 @@ module.exports = (function() { // or is the association on the model itself? if ((this.isSelfAssociation && this.useJunctionTable) || multiAssociation) { // remove the obsolete association identifier from the source - if(this.isSelfAssociation) { + if (this.isSelfAssociation) { this.foreignIdentifier = Utils._.underscoredIf((this.options.as || this.target.tableName) + 'Id', this.options.underscored) } else { this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier @@ -55,16 +56,17 @@ module.exports = (function() { this.connectorDAO = this.source.daoFactoryManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options) - if(!this.isSelfAssociation) { + if (!this.isSelfAssociation) { this.target.associations[this.associationAccessor].connectorDAO = this.connectorDAO } - if(this.options.syncOnAssociation) { + if (this.options.syncOnAssociation) { this.connectorDAO.sync() } } else { var newAttributes = {} newAttributes[this.identifier] = { type: DataTypes.INTEGER } + Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, this.options) Utils._.defaults(this.target.rawAttributes, newAttributes) } @@ -127,7 +129,7 @@ module.exports = (function() { var self = this obj[this.accessors.set] = function(newAssociatedObjects) { - if(newAssociatedObjects === null) { + if (newAssociatedObjects === null) { newAssociatedObjects = [] } @@ -151,19 +153,18 @@ module.exports = (function() { obj[this.accessors.add] = function(newAssociatedObject) { var instance = this - var customEventEmitter = new Utils.CustomEventEmitter(function() { - instance[self.accessors.get]() - .error(function(err){ customEventEmitter.emit('error', err)}) + return new Utils.CustomEventEmitter(function(emitter) { + instance[self.accessors.get]({ where: { id: newAssociatedObject.id }}) + .error(function(err){ emitter.emit('error', err)}) .success(function(currentAssociatedObjects) { - if(!newAssociatedObject.equalsOneOf(currentAssociatedObjects)) - currentAssociatedObjects.push(newAssociatedObject) - - instance[self.accessors.set](currentAssociatedObjects) - .success(function(instances) { customEventEmitter.emit('success', instances) }) - .error(function(err) { customEventEmitter.emit('error', err) }) + if (currentAssociatedObjects.length === 0) { + var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked + new Class(self, instance).injectAdder(emitter, newAssociatedObject) + } else { + emitter.emit('success', newAssociatedObject); + } }) - }) - return customEventEmitter.run() + }).run() } obj[this.accessors.remove] = function(oldAssociatedObject) { @@ -173,7 +174,7 @@ module.exports = (function() { var newAssociations = [] currentAssociatedObjects.forEach(function(association) { - if(!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) + if (!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) newAssociations.push(association) }) diff --git a/lib/associations/has-one.js b/lib/associations/has-one.js index 956bf002d1e6..6fe8121a9c2d 100644 --- a/lib/associations/has-one.js +++ b/lib/associations/has-one.js @@ -1,5 +1,6 @@ var Utils = require("./../utils") , DataTypes = require('./../data-types') + , Helpers = require("./helpers") module.exports = (function() { var HasOne = function(srcDAO, targetDAO, options) { @@ -9,7 +10,7 @@ module.exports = (function() { this.options = options this.isSelfAssociation = (this.source.tableName == this.target.tableName) - if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) { + if (this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) { this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.options.underscored) } @@ -29,6 +30,7 @@ module.exports = (function() { this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored) newAttributes[this.identifier] = { type: DataTypes.INTEGER } + Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, this.options) Utils._.defaults(this.target.rawAttributes, newAttributes) // Sync attributes to DAO proto each time a new assoc is added @@ -68,12 +70,12 @@ module.exports = (function() { var instance = this; return new Utils.CustomEventEmitter(function(emitter) { instance[self.accessors.get]().success(function(oldObj) { - if(oldObj) { + if (oldObj) { oldObj[self.identifier] = null oldObj.save() } - if(associatedObject) { + if (associatedObject) { associatedObject[self.identifier] = instance.id associatedObject .save() diff --git a/lib/associations/helpers.js b/lib/associations/helpers.js new file mode 100644 index 000000000000..7ea29f4d3527 --- /dev/null +++ b/lib/associations/helpers.js @@ -0,0 +1,25 @@ +var Utils = require("./../utils") + +module.exports = { + + addForeignKeyConstraints: function(newAttribute, source, target, options) { + // FK constraints are opt-in: users must either rset `foreignKeyConstraints` + // on the association, or request an `onDelete` or `onUpdate` behaviour + + if(options.foreignKeyConstraint || options.onDelete || options.onUpdate) { + + // Find primary keys: composite keys not supported with this approach + var primaryKeys = Utils._.filter(Utils._.keys(source.rawAttributes), function(key) { + return source.rawAttributes[key].primaryKey + }) + + if(primaryKeys.length == 1) { + newAttribute.references = source.tableName, + newAttribute.referencesKey = primaryKeys[0] + newAttribute.onDelete = options.onDelete, + newAttribute.onUpdate = options.onUpdate + } + } + } + +} diff --git a/lib/associations/mixin.js b/lib/associations/mixin.js index 01f4bd0453f3..16a2161ddd7a 100644 --- a/lib/associations/mixin.js +++ b/lib/associations/mixin.js @@ -19,11 +19,11 @@ Mixin.hasOne = function(associatedDAO, options) { Mixin.belongsTo = function(associatedDAO, options) { // the id is in this table - var association = new BelongsTo(this, associatedDAO, Utils._.extend((options||{}), this.options)) + var association = new BelongsTo(this, associatedDAO, Utils._.extend((options || {}), this.options)) this.associations[association.associationAccessor] = association.injectAttributes() - association.injectGetter(this.DAO.prototype); - association.injectSetter(this.DAO.prototype); + association.injectGetter(this.DAO.prototype) + association.injectSetter(this.DAO.prototype) return this } @@ -33,8 +33,8 @@ Mixin.hasMany = function(associatedDAO, options) { var association = new HasMany(this, associatedDAO, Utils._.extend((options||{}), this.options)) this.associations[association.associationAccessor] = association.injectAttributes() - association.injectGetter(this.DAO.prototype); - association.injectSetter(this.DAO.prototype); + association.injectGetter(this.DAO.prototype) + association.injectSetter(this.DAO.prototype) return this } diff --git a/lib/dao-factory-manager.js b/lib/dao-factory-manager.js index 092edfa83483..a42ed8d3447b 100644 --- a/lib/dao-factory-manager.js +++ b/lib/dao-factory-manager.js @@ -1,3 +1,5 @@ +var Toposort = require('toposort-class') + module.exports = (function() { var DAOFactoryManager = function(sequelize) { this.daos = [] @@ -31,5 +33,34 @@ module.exports = (function() { return this.daos }) + /** + * Iterate over DAOs in an order suitable for e.g. creating tables. Will + * take foreign key constraints into account so that dependencies are visited + * before dependents. + */ + DAOFactoryManager.prototype.forEachDAO = function(iterator) { + var daos = {} + , sorter = new Toposort() + + this.daos.forEach(function(dao) { + daos[dao.tableName] = dao + var deps = [] + + for(var attrName in dao.rawAttributes) { + if(dao.rawAttributes.hasOwnProperty(attrName)) { + if(dao.rawAttributes[attrName].references) { + deps.push(dao.rawAttributes[attrName].references) + } + } + } + + sorter.add(dao.tableName, deps) + }) + + sorter.sort().reverse().forEach(function(name) { + iterator(daos[name]) + }) + } + return DAOFactoryManager })() diff --git a/lib/dao-factory.js b/lib/dao-factory.js index c7309cc636f0..92ba40e25af3 100644 --- a/lib/dao-factory.js +++ b/lib/dao-factory.js @@ -15,11 +15,18 @@ module.exports = (function() { freezeTableName: false, underscored: false, syncOnAssociation: true, - paranoid: false - }, options || {}) + paranoid: false, + whereCollection: null, + schema: null, + schemaDelimiter: '' + }, options || {}) this.name = name - this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name) + if (!this.options.tableName) { + this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name) + } else { + this.tableName = this.options.tableName + } this.rawAttributes = attributes this.daoFactoryManager = null // defined in init function this.associations = {} @@ -49,12 +56,12 @@ module.exports = (function() { this.primaryKeys = {}; Utils._.each(this.attributes, function(dataTypeString, attributeName) { - if((attributeName != 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) { + if ((attributeName !== 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) { self.primaryKeys[attributeName] = dataTypeString } }) - this.primaryKeyCount = Utils._.keys(this.primaryKeys).length; + this.primaryKeyCount = Object.keys(this.primaryKeys).length; this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0; addDefaultAttributes.call(this) @@ -68,21 +75,43 @@ module.exports = (function() { Util.inherits(this.DAO, DAO); this.DAO.prototype.rawAttributes = this.rawAttributes; + if (this.options.instanceMethods) { Utils._.each(this.options.instanceMethods, function(fct, name) { self.DAO.prototype[name] = fct }) } + + Utils._.each(['Get', 'Set'], function(type) { + var prop = type.toLowerCase(), + opt = prop + 'terMethods', + meth = '__define' + type + 'ter__', + funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {} + ; + + Utils._.each(self.rawAttributes, function(attr, name) { + if (attr.hasOwnProperty(prop)) + funcs[name] = attr[prop] + }); + + Utils._.each(funcs, function(fct, name) { + if (!Utils._.isFunction(fct)) + throw new Error(type + 'ter for "' + name + '" is not a function.') + + self.DAO.prototype[meth](name, fct); + }) + }) + this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes); this.DAO.prototype.booleanValues = []; this.DAO.prototype.defaultValues = {}; this.DAO.prototype.validators = {}; Utils._.each(this.rawAttributes, function (definition, name) { - if(((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) { + if (((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) { self.DAO.prototype.booleanValues.push(name); } - if(definition.hasOwnProperty('defaultValue')) { + if (definition.hasOwnProperty('defaultValue')) { self.DAO.prototype.defaultValues[name] = function() { return Utils.toDefaultValue(definition.defaultValue); } @@ -106,17 +135,17 @@ module.exports = (function() { return new Utils.CustomEventEmitter(function(emitter) { var doQuery = function() { self.QueryInterface - .createTable(self.tableName, self.attributes, options) + .createTable(self.getTableName(), self.attributes, options) .success(function() { emitter.emit('success', self) }) .error(function(err) { emitter.emit('error', err) }) .on('sql', function(sql) { emitter.emit('sql', sql) }) } - if(options.force) + if (options.force) { self.drop().success(doQuery).error(function(err) { emitter.emit('error', err) }) - else + } else { doQuery() - + } }).run() } @@ -124,30 +153,56 @@ module.exports = (function() { return this.QueryInterface.dropTable(this.tableName) } - // alias for findAll - DAOFactory.prototype.all = function(options) { - return this.findAll(options) + DAOFactory.prototype.dropSchema = function(schema) { + return this.QueryInterface.dropSchema(schema) } - DAOFactory.prototype.findAll = function(options) { - var hasJoin = false; + DAOFactory.prototype.schema = function(schema, options) { + this.options.schema = schema - if ((typeof options === 'object') && (options.hasOwnProperty('include'))) { - var includes = options.include + if (!!options) { + if (typeof options === "string") { + this.options.schemaDelimiter = options + } else { + if (!!options.schemaDelimiter) { + this.options.schemaDelimiter = options.schemaDelimiter + } + } + } - hasJoin = true; - options.include = {} + return this + } - includes.forEach(function(daoName) { - options.include[daoName] = this.daoFactoryManager.getDAO(daoName) + DAOFactory.prototype.getTableName = function() { + return this.QueryGenerator.addSchema(this) + } - if (!options.include[daoName]) { - options.include[daoName] = this.getAssociationByAlias(daoName).target - } - }.bind(this)) + // alias for findAll + DAOFactory.prototype.all = function(options, queryOptions) { + return this.findAll(options, queryOptions) + } + + DAOFactory.prototype.findAll = function(options, queryOptions) { + var hasJoin = false + var options = Utils._.clone(options) + + if (typeof options === 'object') { + if (options.hasOwnProperty('include')) { + hasJoin = true + + options.include = options.include.map(function(include) { + return validateIncludedElement.call(this, include) + }.bind(this)) + } + + // whereCollection is used for non-primary key updates + this.options.whereCollection = options.where || null } - return this.QueryInterface.select(this, this.tableName, options, { type: 'SELECT', hasJoin: hasJoin }) + return this.QueryInterface.select(this, this.tableName, options, Utils._.defaults({ + type: 'SELECT', + hasJoin: hasJoin + }, queryOptions)) } //right now, the caller (has-many-double-linked) is in charge of the where clause @@ -155,28 +210,40 @@ module.exports = (function() { var optcpy = Utils._.clone(options) optcpy.attributes = optcpy.attributes || [Utils.addTicks(this.tableName)+".*"] - return this.QueryInterface.select(this, [this.tableName, joinTableName], optcpy, { type: 'SELECT' }) + // whereCollection is used for non-primary key updates + this.options.whereCollection = optcpy.where || null; + + return this.QueryInterface.select(this, [this.getTableName(), joinTableName], optcpy, { type: 'SELECT' }) } - DAOFactory.prototype.find = function(options) { - var hasJoin = false; + /** + * Search for an instance. + * + * @param {Object} options Options to describe the scope of the search. + * @param {Array} include A list of associations which shall get eagerly loaded. Supported is either { include: [ DaoFactory1, DaoFactory2, ...] } or { include: [ { daoFactory: DaoFactory1, as: 'Alias' } ] }. + * @param {Object} set the query options, e.g. raw, specifying that you want raw data instead of built DAOs + * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`. + */ + DAOFactory.prototype.find = function(options, queryOptions) { + var hasJoin = false + // no options defined? // return an emitter which emits null - if([null, undefined].indexOf(options) !== -1) { + if ([null, undefined].indexOf(options) !== -1) { return new Utils.CustomEventEmitter(function(emitter) { setTimeout(function() { emitter.emit('success', null) }, 10) }).run() } - var primaryKeys = this.primaryKeys; + var primaryKeys = this.primaryKeys // options is not a hash but an id - if(typeof options === 'number') { + if (typeof options === 'number') { options = { where: options } } else if (Utils._.size(primaryKeys) && Utils.argsArePrimaryKeys(arguments, primaryKeys)) { var where = {} , self = this - , keys = Utils._.keys(primaryKeys) + , keys = Object.keys(primaryKeys) Utils._.each(arguments, function(arg, i) { var key = keys[i] @@ -184,32 +251,36 @@ module.exports = (function() { }) options = { where: where } - } else if ((typeof options === 'string') && (parseInt(options, 10).toString() === options)) { - var parsedId = parseInt(options, 10); + } else if (typeof options === 'string' && parseInt(options, 10).toString() === options) { + var parsedId = parseInt(options, 10) - if(!Utils._.isFinite(parsedId)) { + if (!Utils._.isFinite(parsedId)) { throw new Error('Invalid argument to find(). Must be an id or an options object.') } options = { where: parsedId } - } else if ((typeof options === 'object') && (options.hasOwnProperty('include'))) { - var includes = options.include - hasJoin = true; + } else if (typeof options === 'object') { + options = Utils._.clone(options) - options.include = {} + if (options.hasOwnProperty('include')) { + hasJoin = true - includes.forEach(function(daoName) { - options.include[daoName] = this.daoFactoryManager.getDAO(daoName) + options.include = options.include.map(function(include) { + return validateIncludedElement.call(this, include) + }.bind(this)) + } - if (!options.include[daoName]) { - options.include[daoName] = this.getAssociationByAlias(daoName).target - } - }.bind(this)) + // whereCollection is used for non-primary key updates + this.options.whereCollection = options.where || null } options.limit = 1 - return this.QueryInterface.select(this, this.tableName, options, { plain: true, type: 'SELECT', hasJoin: hasJoin }) + return this.QueryInterface.select(this, this.getTableName(), options, Utils._.defaults({ + plain: true, + type: 'SELECT', + hasJoin: hasJoin + }, queryOptions)) } DAOFactory.prototype.count = function(options) { @@ -217,31 +288,33 @@ module.exports = (function() { options.attributes.push(['count(*)', 'count']) options.parseInt = true - return this.QueryInterface.rawSelect(this.tableName, options, 'count') + return this.QueryInterface.rawSelect(this.getTableName(), options, 'count') } DAOFactory.prototype.max = function(field, options) { options = Utils._.extend({ attributes: [] }, options || {}) options.attributes.push(['max(' + field + ')', 'max']) - options.parseInt = true + options.parseFloat = true - return this.QueryInterface.rawSelect(this.tableName, options, 'max') + return this.QueryInterface.rawSelect(this.getTableName(), options, 'max') } DAOFactory.prototype.min = function(field, options) { options = Utils._.extend({ attributes: [] }, options || {}) options.attributes.push(['min(' + field + ')', 'min']) - options.parseInt = true + options.parseFloat = true - return this.QueryInterface.rawSelect(this.tableName, options, 'min') + return this.QueryInterface.rawSelect(this.getTableName(), options, 'min') } DAOFactory.prototype.build = function(values, options) { - options = options || {} + options = options || { isNewRecord: true } var self = this - , instance = new this.DAO(values, this.options) + , instance = new this.DAO(values, this.options, options.isNewRecord) - instance.isNewRecord = options.hasOwnProperty('isNewRecord') ? options.isNewRecord : true + instance.isNewRecord = options.isNewRecord + instance.daoFactoryName = this.name + instance.daoFactory = this return instance } @@ -259,16 +332,16 @@ module.exports = (function() { }).success(function (instance) { if (instance === null) { for (var attrname in defaults) { - params[attrname] = defaults[attrname]; + params[attrname] = defaults[attrname] } self.create(params) - .success(function (instance) { + .success(function (instance) { emitter.emit('success', instance) }) - .error( function (error) { + .error( function (error) { emitter.emit('error', error) - }); + }) } else { emitter.emit('success', instance) } @@ -278,6 +351,110 @@ module.exports = (function() { }).run() } + /** + * Create and insert multiple instances + * + * @param {Array} records List of objects (key/value pairs) to create instances from + * @param {Array} fields Fields to insert (defaults to all fields) + * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`. + * + * Note: the `success` handler is not passed any arguments. To obtain DAOs for + * the newly created values, you will need to query for them again. This is + * because MySQL and SQLite do not make it easy to obtain back automatically + * generated IDs and other default values in a way that can be mapped to + * multiple records + */ + DAOFactory.prototype.bulkCreate = function(records, fields) { + var self = this + , daos = records.map(function(v) { return self.build(v) }) + , updatedAtAttr = self.options.underscored ? 'updated_at' : 'updatedAt' + , createdAtAttr = self.options.underscored ? 'created_at' : 'createdAt' + + // we will re-create from DAOs, which may have set up default attributes + records = [] + + if (fields) { + + // Always insert updated and created time stamps + if (self.options.timestamps) { + if (fields.indexOf(updatedAtAttr) === -1) { + fields.push(updatedAtAttr) + } + + if (fields.indexOf(createdAtAttr) === -1) { + fields.push(createdAtAttr) + } + } + + // Build records for the fields we know about + daos.forEach(function(dao) { + var values = {}; + fields.forEach(function(field) { + values[field] = dao.values[field] + }) + if (self.options.timestamps) { + values[updatedAtAttr] = Utils.now() + } + records.push(values); + }) + + } else { + daos.forEach(function(dao) { + records.push(dao.values) + }) + } + + // Validate enums + records.forEach(function(values) { + for (var attrName in self.rawAttributes) { + if (self.rawAttributes.hasOwnProperty(attrName)) { + var definition = self.rawAttributes[attrName] + , isEnum = (definition.type && (definition.type.toString() === DataTypes.ENUM.toString())) + , hasValue = (typeof values[attrName] !== 'undefined') + , valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1) + + if (isEnum && hasValue && valueOutOfScope) { + throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', ')) + } + } + } + }) + + return self.QueryInterface.bulkInsert(self.tableName, records) + } + + /** + * Delete multiple instances + * + * @param {Object} where Options to describe the scope of the search. + * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`. + */ + DAOFactory.prototype.destroy = function(where) { + if (this.options.timestamps && this.options.paranoid) { + var attr = this.options.underscored ? 'deleted_at' : 'deletedAt' + var attrValueHash = {} + attrValueHash[attr] = Utils.now() + return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where) + } else { + return this.QueryInterface.bulkDelete(this.tableName, where) + } + } + + /** + * Update multiple instances + * + * @param {Object} attrValueHash A hash of fields to change and their new values + * @param {Object} where Options to describe the scope of the search. + * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`. + */ + DAOFactory.prototype.update = function(attrValueHash, where) { + if(this.options.timestamps) { + var attr = this.options.underscored ? 'updated_at' : 'updatedAt' + attrValueHash[attr] = Utils.now() + } + return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where) + } + // private var query = function() { @@ -313,18 +490,22 @@ module.exports = (function() { } } - if(this.hasPrimaryKeys) defaultAttributes = {} + if (this.hasPrimaryKeys) { + defaultAttributes = {} + } - if(this.options.timestamps) { + if (this.options.timestamps) { defaultAttributes[Utils._.underscoredIf('createdAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false} defaultAttributes[Utils._.underscoredIf('updatedAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false} - if(this.options.paranoid) + if (this.options.paranoid) defaultAttributes[Utils._.underscoredIf('deletedAt', this.options.underscored)] = {type: DataTypes.DATE} } Utils._.each(defaultAttributes, function(value, attr) { - self.rawAttributes[attr] = value + if (Utils._.isUndefined(self.rawAttributes[attr])) { + self.rawAttributes[attr] = value + } }) } @@ -342,6 +523,45 @@ module.exports = (function() { }.bind(this)) } + var validateIncludedElement = function(include) { + if (include instanceof DAOFactory) { + include = { daoFactory: include, as: include.tableName } + } + + if (typeof include === 'object') { + if (include.hasOwnProperty('model')) { + include.daoFactory = include.model + delete include.model + } + + if (include.hasOwnProperty('daoFactory') && (include.hasOwnProperty('as'))) { + var usesAlias = (include.as !== include.daoFactory.tableName) + , association = (usesAlias ? this.getAssociationByAlias(include.as) : this.getAssociation(include.daoFactory)) + + // check if the current daoFactory is actually associated with the passed daoFactory + if (!!association && (!association.options.as || (association.options.as === include.as))) { + include.association = association + + return include + } else { + var msg = include.daoFactory.name + + if (usesAlias) { + msg += " (" + include.as + ")" + } + + msg += " is not associated to " + this.name + "!" + + throw new Error(msg) + } + } else { + throw new Error('Include malformed. Expected attributes: daoFactory, as!') + } + } else { + throw new Error('Include unexpected. Element has to be either an instance of DAOFactory or an object.') + } + } + Utils._.extend(DAOFactory.prototype, require("./associations/mixin")) return DAOFactory diff --git a/lib/dao.js b/lib/dao.js index 2b0fbd90c53a..e8a2ba0f34a1 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -4,29 +4,18 @@ var Utils = require("./utils") , DataTypes = require("./data-types") module.exports = (function() { - var DAO = function(values, options) { - var self = this; - this.__options = options; - this.hasPrimaryKeys = options.hasPrimaryKeys; - this.selectedValues = values; - - initAttributes.call(this, values) - - if (this.hasDefaultValues) { - Utils._.each(this.defaultValues, function (value, name) { - if(typeof self[name] === 'undefined') { - self.addAttribute(name, value()); - } - }) - } + var DAO = function(values, options, isNewRecord) { + var self = this - if (this.booleanValues.length) { - this.booleanValues.forEach(function (name) { - //transform integer 0,1 into boolean - self[name] = !!self[name]; - }); - } + this.dataValues = {} + this.__options = options + this.hasPrimaryKeys = options.hasPrimaryKeys + this.selectedValues = values + this.__eagerlyLoadedAssociations = [] + + initAttributes.call(this, values, isNewRecord) } + Utils._.extend(DAO.prototype, Mixin.prototype) Object.defineProperty(DAO.prototype, 'sequelize', { @@ -40,7 +29,7 @@ module.exports = (function() { Object.defineProperty(DAO.prototype, 'isDeleted', { get: function() { var result = this.__options.timestamps && this.__options.paranoid - result = result && this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] !== null + result = result && this.dataValues[this.__options.underscored ? 'deleted_at' : 'deletedAt'] !== null return result } @@ -51,8 +40,11 @@ module.exports = (function() { var result = {} , self = this - this.attributes.forEach(function(attr) { - result[attr] = self[attr] + this.attributes.concat(this.__eagerlyLoadedAssociations).forEach(function(attr) { + result[attr] = self.dataValues.hasOwnProperty(attr) + ? self.dataValues[attr] + : self[attr] + ; }) return result @@ -65,7 +57,7 @@ module.exports = (function() { , self = this Utils._.each(this.__factory.primaryKeys, function(_, attr) { - result[attr] = self[attr] + result[attr] = self.dataValues[attr] }) return result @@ -74,21 +66,30 @@ module.exports = (function() { Object.defineProperty(DAO.prototype, "identifiers", { get: function() { - var primaryKeys = Utils._.keys(this.__factory.primaryKeys) + var primaryKeys = Object.keys(this.__factory.primaryKeys) , result = {} , self = this - if(!this.__factory.hasPrimaryKeys) + if (!this.__factory.hasPrimaryKeys) { primaryKeys = ['id'] + } primaryKeys.forEach(function(identifier) { - result[identifier] = self[identifier] + result[identifier] = self.dataValues[identifier] }) return result } }) + DAO.prototype.getDataValue = function(name) { + return this.dataValues && this.dataValues.hasOwnProperty(name) ? this.dataValues[name] : this[name] + } + + DAO.prototype.setDataValue = function(name, value) { + this.dataValues[name] = value + } + // if an array with field names is passed to save() // only those fields will be updated DAO.prototype.save = function(fields) { @@ -107,33 +108,106 @@ module.exports = (function() { fields.push(createdAtAttr) } } + + var tmpVals = self.values + fields.forEach(function(field) { - if (self.values[field] !== undefined) { - values[field] = self.values[field] + if (tmpVals[field] !== undefined) { + values[field] = tmpVals[field] } }) } - if(this.__options.timestamps && this.hasOwnProperty(updatedAtAttr)) { - this[updatedAtAttr] = values[updatedAtAttr] = new Date() - } + for (var attrName in this.daoFactory.rawAttributes) { + if (this.daoFactory.rawAttributes.hasOwnProperty(attrName)) { + var definition = this.daoFactory.rawAttributes[attrName] + , isEnum = (definition.type && (definition.type.toString() === DataTypes.ENUM.toString())) + , isHstore = (!!definition.type && !!definition.type.type && definition.type.type === DataTypes.HSTORE.type) + , hasValue = (typeof values[attrName] !== 'undefined') + , valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1) + + if (isEnum && hasValue && valueOutOfScope) { + throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', ')) + } - if(this.isNewRecord) { - // added by Scott Rutherford to set created post class creation - if(this.__options.timestamps && this.hasOwnProperty(createdAtAttr)) { - this[createdAtAttr] = values[createdAtAttr] = new Date() + if (isHstore) { + if (typeof values[attrName] === "object") { + var text = [] + Utils._.each(values[attrName], function(value, key){ + if (typeof value !== "string" && typeof value !== "number") { + throw new Error('Value for HSTORE must be a string or number.') + } + + text.push(this.QueryInterface.QueryGenerator.addQuotes(key) + '=>' + (typeof value === "string" ? this.QueryInterface.QueryGenerator.addQuotes(value) : value)) + }.bind(this)) + values[attrName] = text.join(',') + } + } } + } + + if (this.__options.timestamps && this.dataValues.hasOwnProperty(updatedAtAttr)) { + this.dataValues[updatedAtAttr] = values[updatedAtAttr] = Utils.now() + } - return this.QueryInterface.insert(this, this.__factory.tableName, values) + var errors = this.validate() + + if (!!errors) { + return new Utils.CustomEventEmitter(function(emitter) { + emitter.emit('error', errors) + }).run() + } + else if (this.isNewRecord) { + if (this.__options.timestamps && this.dataValues.hasOwnProperty(createdAtAttr)) { + this.dataValues[createdAtAttr] = values[createdAtAttr] = Utils.now() + } + return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values) } else { - var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id - , tableName = this.__factory.tableName + var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id; + + if (identifier === null && this.__options.whereCollection !== null) { + identifier = this.__options.whereCollection; + } + + var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory) , query = this.QueryInterface.update(this, tableName, values, identifier) return query } } + /* + * Refresh the current instance in-place, i.e. update the object with current data from the DB and return the same object. + * This is different from doing a `find(DAO.id)`, because that would create and return a new object. With this method, + * all references to the DAO are updated with the new data and no new objects are created. + * + * @return {Object} A promise which fires `success`, `error`, `complete` and `sql`. + */ + DAO.prototype.reload = function() { + var where = [ + this.QueryInterface.QueryGenerator.addQuotes(this.__factory.tableName) + '.' + this.QueryInterface.QueryGenerator.addQuotes('id')+'=?', + this.id + ] + + return new Utils.CustomEventEmitter(function(emitter) { + this.__factory.find({ + where: where, + limit: 1, + include: this.__eagerlyLoadedOptions || [] + }) + .on('sql', function(sql) { emitter.emit('sql', sql) }) + .on('error', function(error) { emitter.emit('error', error) }) + .on('success', function(obj) { + for (var valueName in obj.values) { + if (obj.values.hasOwnProperty(valueName)) { + this[valueName] = obj.values[valueName] + } + } + emitter.emit('success', this) + }.bind(this)) + }.bind(this)).run() + } + /* * Validate this dao's attribute values according to validation rules set in the dao definition. * @@ -146,69 +220,76 @@ module.exports = (function() { // for each field and value Utils._.each(self.values, function(value, field) { - // if field has validators - if (self.validators.hasOwnProperty(field)) { - // for each validator - Utils._.each(self.validators[field], function(details, validatorType) { - - var is_custom_fn = false // if true then it's a custom validation method - var fn_method = null // the validation function to call - var fn_args = [] // extra arguments to pass to validation function - var fn_msg = "" // the error message to return if validation fails - - // is it a custom validator function? - if (Utils._.isFunction(details)) { - is_custom_fn = true - fn_method = Utils._.bind(details, self, value) - } - // is it a validator module function? - else { - // extra args - fn_args = details.hasOwnProperty("args") ? details.args : details - if (!Utils._.isArray(fn_args)) - fn_args = [fn_args] - // error msg - fn_msg = details.hasOwnProperty("msg") ? details.msg : false - // check method exists - var v = Validator.check(value, fn_msg) - if (!Utils._.isFunction(v[validatorType])) - throw new Error("Invalid validator function: " + validatorType) - // bind to validator obj - fn_method = Utils._.bind(v[validatorType], v) - } + // Bypass if the field is a nested JSON + if (typeof self.rawAttributes[field] != 'undefined') { + // if field has validators + var hasAllowedNull = (self.rawAttributes[field].allowNull && self.rawAttributes[field].allowNull === true + && (value === null || value === undefined + || ((typeof self.rawAttributes[field].defaultValue != 'undefined') && value === self.rawAttributes[field].defaultValue))); + + if (self.validators.hasOwnProperty(field) && !hasAllowedNull) { + // for each validator + Utils._.each(self.validators[field], function(details, validatorType) { + + var is_custom_fn = false // if true then it's a custom validation method + var fn_method = null // the validation function to call + var fn_args = [] // extra arguments to pass to validation function + var fn_msg = "" // the error message to return if validation fails + + // is it a custom validator function? + if (Utils._.isFunction(details)) { + is_custom_fn = true + fn_method = Utils._.bind(details, self, value) + } + // is it a validator module function? + else { + // extra args + fn_args = details.hasOwnProperty("args") ? details.args : details + if (!Array.isArray(fn_args)) + fn_args = [fn_args] + // error msg + fn_msg = details.hasOwnProperty("msg") ? details.msg : false + // check method exists + var v = Validator.check(value, fn_msg) + if (!Utils._.isFunction(v[validatorType])) + throw new Error("Invalid validator function: " + validatorType) + // bind to validator obj + fn_method = Utils._.bind(v[validatorType], v) + } - try { - fn_method.apply(null, fn_args) - } catch (err) { - err = err.message - // if we didn't provide a custom error message then augment the default one returned by the validator - if (!fn_msg && !is_custom_fn) - err += ": " + field - // each field can have multiple validation failures stored against it - if (failures.hasOwnProperty(field)) { - failures[field].push(err) - } else { - failures[field] = [err] + try { + fn_method.apply(null, fn_args) + } catch (err) { + err = err.message + // if we didn't provide a custom error message then augment the default one returned by the validator + if (!fn_msg && !is_custom_fn) + err += ": " + field + // each field can have multiple validation failures stored against it + if (failures.hasOwnProperty(field)) { + failures[field].push(err) + } else { + failures[field] = [err] + } } - } - }) // for each validator for this field - } // if field has validator set + }) // for each validator for this field + } // if field has validator set + } // Bypass the field if it is a nested JSON }) // for each field return (Utils._.isEmpty(failures) ? null : failures) } - DAO.prototype.updateAttributes = function(updates) { + DAO.prototype.updateAttributes = function(updates, fields) { this.setAttributes(updates) - return this.save() + return this.save(fields) } DAO.prototype.setAttributes = function(updates) { var self = this - var readOnlyAttributes = Utils._.keys(this.__factory.primaryKeys) + var readOnlyAttributes = Object.keys(this.__factory.primaryKeys) readOnlyAttributes.push('id') readOnlyAttributes.push('createdAt') @@ -226,22 +307,54 @@ module.exports = (function() { } DAO.prototype.destroy = function() { - if(this.__options.timestamps && this.__options.paranoid) { + if (this.__options.timestamps && this.__options.paranoid) { var attr = this.__options.underscored ? 'deleted_at' : 'deletedAt' - this[attr] = new Date() + this.dataValues[attr] = new Date() return this.save() } else { var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id - return this.QueryInterface.delete(this, this.__factory.tableName, identifier) + return this.QueryInterface.delete(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory.tableName, this.__factory.options.schema), identifier) + } + } + + DAO.prototype.increment = function(fields, count) { + var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id, + values = {} + + if (count === undefined) count = 1; + + if (Utils._.isString(fields)) { + values[fields] = count; + } else if (Utils._.isArray(fields)) { + Utils._.each(fields, function (field) { + values[field] = count + }) + } else { // Assume fields is key-value pairs + values = fields; } + + return this.QueryInterface.increment(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory.tableName, this.__factory.options.schema), values, identifier) + } + + DAO.prototype.decrement = function (fields, count) { + if (!Utils._.isString(fields) && !Utils._.isArray(fields)) { // Assume fields is key-value pairs + Utils._.each(fields, function (value, field) { + fields[field] = -value; + }); + } + + return this.increment(fields, 0 - count); } DAO.prototype.equals = function(other) { var result = true - , self = this Utils._.each(this.values, function(value, key) { - result = result && (value == other[key]) + if(Utils._.isDate(value) && Utils._.isDate(other[key])) { + result = result && (value.getTime() == other[key].getTime()) + } else { + result = result && (value == other[key]) + } }) return result @@ -257,7 +370,37 @@ module.exports = (function() { } DAO.prototype.addAttribute = function(attribute, value) { - this[attribute] = value + if (typeof this.dataValues[attribute] !== 'undefined') + return; + + if (this.booleanValues.length && this.booleanValues.indexOf(attribute) !== -1) // transform integer 0,1 into boolean + value = !!value; + + var has = (function(o) { + var predef = Object.getOwnPropertyDescriptor(o, attribute); + + if (predef && predef.hasOwnProperty('value')) + return true; // true here means 'this property exist as a simple value property, do not place setters or getters at all' + + return { + get: (predef && predef.hasOwnProperty('get') ? predef.get : null) || o.__lookupGetter__(attribute), + set: (predef && predef.hasOwnProperty('set') ? predef.set : null) || o.__lookupSetter__(attribute) + }; + })(this); + + // @ node-v0.8.19: + // calling __defineGetter__ destroys any previously defined setters for the attribute in + // question *if* that property setter was defined on the object's prototype (which is what + // we do in dao-factory) ... therefore we need to [re]define both the setter and getter + // here with either the function that already existed OR the default/automatic definition + // + // (the same is true for __defineSetter and 'prototype' getters) + if (has !== true) { + this.__defineGetter__(attribute, has.get || function() { return this.dataValues[attribute]; }); + this.__defineSetter__(attribute, has.set || function(v) { this.dataValues[attribute] = v; }); + } + + this[attribute] = value; } DAO.prototype.setValidators = function(attribute, validators) { @@ -270,36 +413,62 @@ module.exports = (function() { // private - var initAttributes = function(values) { + var initAttributes = function(values, isNewRecord) { + // set id to null if not passed as value, a newly created dao has no id + var defaults = this.hasPrimaryKeys ? {} : { id: null }, + attrs = {}, + key; + // add all passed values to the dao and store the attribute names in this.attributes - for (var key in values) { + for (key in values) { if (values.hasOwnProperty(key)) { + if (typeof values[key] === "string" && !!this.__factory && !!this.__factory.rawAttributes[key] && !!this.__factory.rawAttributes[key].type && !!this.__factory.rawAttributes[key].type.type && this.__factory.rawAttributes[key].type.type === DataTypes.HSTORE.type) { + values[key] = this.QueryInterface.QueryGenerator.toHstore(values[key]) + } + this.addAttribute(key, values[key]) } } - // set id to null if not passed as value - // a newly created dao has no id - var defaults = this.hasPrimaryKeys ? {} : { id: null } + if (this.__options.timestamps && isNewRecord) { + defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = Utils.now() + defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = Utils.now() - if(this.__options.timestamps) { - defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = new Date() - defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = new Date() - - if(this.__options.paranoid) { + if (this.__options.paranoid) { defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null } + + if (this.hasDefaultValues) { + Utils._.each(this.defaultValues, function(valueFn, key) { + if (!defaults.hasOwnProperty(key)) + defaults[key] = valueFn() + }) + } } if (Utils._.size(defaults)) { - for (var attr in defaults) { - var value = defaults[attr] + for (key in defaults) { + attrs[key] = Utils.toDefaultValue(defaults[key]) + } + } + + Utils._.each(this.attributes, function(key) { + if (!attrs.hasOwnProperty(key)) { + attrs[key] = undefined + } + }) - if(!this.hasOwnProperty(attr)) { - this.addAttribute(attr, Utils.toDefaultValue(value)) + if (values) { + for (key in values) { + if (values.hasOwnProperty(key)) { + attrs[key] = values[key] } } } + + for (key in attrs) { + this.addAttribute(key, attrs[key]) + } } return DAO diff --git a/lib/data-types.js b/lib/data-types.js index 2feb510a1bf0..6b5bd6b35b2a 100644 --- a/lib/data-types.js +++ b/lib/data-types.js @@ -1,10 +1,190 @@ +var STRING = function(length, binary) { + if (this instanceof STRING) { + this._binary = !!binary; + if (typeof length === 'number') { + this._length = length; + } else { + this._length = 255; + } + } else { + return new STRING(length, binary); + } +}; + +STRING.prototype = { + get BINARY() { + this._binary = true; + return this; + }, + get type() { + return this.toString(); + }, + toString: function() { + return 'VARCHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : ''); + } +}; + +Object.defineProperty(STRING, 'BINARY', { + get: function() { + return new STRING(undefined, true); + } +}); + +var INTEGER = function() { + return INTEGER.prototype.construct.apply(this, [INTEGER].concat(Array.prototype.slice.apply(arguments))); +}; + +var BIGINT = function() { + return BIGINT.prototype.construct.apply(this, [BIGINT].concat(Array.prototype.slice.apply(arguments))); +}; + +var FLOAT = function() { + return FLOAT.prototype.construct.apply(this, [FLOAT].concat(Array.prototype.slice.apply(arguments))); +}; + +FLOAT._type = FLOAT; +FLOAT._typeName = 'FLOAT'; +INTEGER._type = INTEGER; +INTEGER._typeName = 'INTEGER'; +BIGINT._type = BIGINT; +BIGINT._typeName = 'BIGINT'; +STRING._type = STRING; +STRING._typeName = 'VARCHAR'; + +STRING.toString = INTEGER.toString = FLOAT.toString = BIGINT.toString = function() { + return new this._type().toString(); +}; + +FLOAT.prototype = BIGINT.prototype = INTEGER.prototype = { + + construct: function(RealType, length, decimals, unsigned, zerofill) { + if (this instanceof RealType) { + this._typeName = RealType._typeName; + this._unsigned = !!unsigned; + this._zerofill = !!zerofill; + if (typeof length === 'number') { + this._length = length; + } + if (typeof decimals === 'number') { + this._decimals = decimals; + } + } else { + return new RealType(length, decimals, unsigned, zerofill); + } + }, + + get type() { + return this.toString(); + }, + + get UNSIGNED() { + this._unsigned = true; + return this; + }, + + get ZEROFILL() { + this._zerofill = true; + return this; + }, + + toString: function() { + var result = this._typeName; + if (this._length) { + result += '(' + this._length; + if (typeof this._decimals === 'number') { + result += ',' + this._decimals; + } + result += ')'; + } + if (this._unsigned) { + result += ' UNSIGNED'; + } + if (this._zerofill) { + result += ' ZEROFILL'; + } + return result; + } +}; + +var unsignedDesc = { + get: function() { + return new this._type(undefined, undefined, true); + } +}; + +var zerofillDesc = { + get: function() { + return new this._type(undefined, undefined, undefined, true); + } +}; + +var typeDesc = { + get: function() { + return new this._type().toString(); + } +}; + +Object.defineProperty(STRING, 'type', typeDesc); +Object.defineProperty(INTEGER, 'type', typeDesc); +Object.defineProperty(BIGINT, 'type', typeDesc); +Object.defineProperty(FLOAT, 'type', typeDesc); + +Object.defineProperty(INTEGER, 'UNSIGNED', unsignedDesc); +Object.defineProperty(BIGINT, 'UNSIGNED', unsignedDesc); +Object.defineProperty(FLOAT, 'UNSIGNED', unsignedDesc); + +Object.defineProperty(INTEGER, 'ZEROFILL', zerofillDesc); +Object.defineProperty(BIGINT, 'ZEROFILL', zerofillDesc); +Object.defineProperty(FLOAT, 'ZEROFILL', zerofillDesc); + module.exports = { - STRING: 'VARCHAR(255)', + STRING: STRING, + TEXT: 'TEXT', - INTEGER: 'INTEGER', + INTEGER: INTEGER, + BIGINT: BIGINT, DATE: 'DATETIME', BOOLEAN: 'TINYINT(1)', - FLOAT: 'FLOAT', + FLOAT: FLOAT, NOW: 'NOW', - BIGINT: 'BIGINT' + + get ENUM() { + var result = function() { + return { + type: 'ENUM', + values: Array.prototype.slice.call(arguments).reduce(function(result, element) { + return result.concat(Array.isArray(element) ? element : [ element ]) + }, []) + } + } + + result.toString = result.valueOf = function() { return 'ENUM' } + + return result + }, + + get DECIMAL() { + var result = function(precision, scale) { + return 'DECIMAL(' + precision + ',' + scale + ')' + } + + result.toString = result.valueOf = function() { return 'DECIMAL' } + + return result + }, + + ARRAY: function(type) { return type + '[]' }, + + get HSTORE() { + var result = function() { + return { + type: 'HSTORE' + } + } + + result.type = 'HSTORE' + result.toString = result.valueOf = function() { return 'TEXT' } + + return result + } } diff --git a/lib/dialects/abstract/query.js b/lib/dialects/abstract/query.js index 88c57375aa1c..0ac71f1fa6ce 100644 --- a/lib/dialects/abstract/query.js +++ b/lib/dialects/abstract/query.js @@ -1,5 +1,6 @@ var Utils = require('../../utils') , CustomEventEmitter = require("../../emitters/custom-event-emitter") + , Dot = require('dottie') module.exports = (function() { var AbstractQuery = function(database, sequelize, callee, options) {} @@ -29,12 +30,12 @@ module.exports = (function() { * @return {void} */ AbstractQuery.prototype.checkLoggingOption = function() { - if(this.options.logging === true) { + if (this.options.logging === true) { console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log') this.options.logging = console.log } - if(this.options.logging == console.log) { + if (this.options.logging === console.log) { // using just console.log will break in node < 0.6 this.options.logging = function(s) { console.log(s) } } @@ -69,6 +70,28 @@ module.exports = (function() { result = handleShowTableQuery.call(this, data) } else if (isShowOrDescribeQuery.call(this)) { result = data + + if (this.sql.toLowerCase().indexOf('describe') === 0) { + result = {} + + data.forEach(function(_result) { + result[_result.Field] = { + type: _result.Type.toUpperCase(), + allowNull: (_result.Null === 'YES'), + defaultValue: _result.Default + } + }) + } else if (this.sql.toLowerCase().indexOf('show index from') === 0) { + result = Utils._.uniq(result.map(function(result) { + return { + name: result.Key_name, + tableName: result.Table, + unique: (result.Non_unique !== 1) + } + }), false, function(row) { + return row.name + }) + } } else if (isCallQuery.call(this)) { result = data[0] } @@ -141,34 +164,26 @@ module.exports = (function() { * @return {String} The found tableName / alias. */ var findTableNameInAttribute = function(attribute) { - var tableName = null - - this.sequelize.daoFactoryManager.daos.forEach(function(daoFactory) { - if (!!tableName) { - return - } else if (attribute.indexOf(daoFactory.tableName + ".") === 0) { - tableName = daoFactory.tableName - } else if (attribute.indexOf(Utils.singularize(daoFactory.tableName) + ".") === 0) { - tableName = Utils.singularize(daoFactory.tableName) - } else { - for (var associationName in daoFactory.associations) { - if (daoFactory.associations.hasOwnProperty(associationName)) { - var association = daoFactory.associations[associationName] + if (!this.options.include) { + return null + } - if (attribute.indexOf(association.options.as + ".") === 0) { - tableName = association.options.as - } - } - } - } + var tableNames = this.options.include.map(function(include) { + return include.as + }).filter(function(include) { + return attribute.indexOf(include + '.') === 0 }) - return tableName + if (tableNames.length === 1) { + return tableNames[0] + } else { + return null + } } var queryResultHasJoin = function(results) { if (!!results[0]) { - var keys = Utils._.keys(results[0]) + var keys = Object.keys(results[0]) for (var i = 0; i < keys.length; i++) { if (!!findTableNameInAttribute.call(this, keys[i])) { @@ -227,25 +242,24 @@ module.exports = (function() { } var handleSelectQuery = function(results) { - var result = null, self = this; + var result = null if (this.options.raw) { - result = results - } else if (this.options.hasJoin === true) { - result = prepareJoinData.call(this, results) - result = groupDataByCalleeFactory.call(this, result).map(function(result) { - // let's build the actual dao instance first... - var dao = this.callee.build(result[this.callee.tableName], { isNewRecord: false }) - - // ... and afterwards the prefetched associations - for (var tableName in result) { - if (result.hasOwnProperty(tableName) && (tableName !== this.callee.tableName)) { - buildAssociatedDaoInstances.call(this, tableName, result[tableName], dao) + result = results.map(function(result) { + var o = {} + + for (var key in result) { + if (result.hasOwnProperty(key)) { + o[key] = result[key] } } - return dao - }.bind(this)) + return o + }) + + result = result.map(Dot.transform) + } else if (this.options.hasJoin === true) { + result = transformRowsWithEagerLoadingIntoDaos.call(this, results) } else { result = results.map(function(result) { return this.callee.build(result, { isNewRecord: false }) @@ -253,13 +267,38 @@ module.exports = (function() { } // return the first real model instance if options.plain is set (e.g. Model.find) - if(this.options.plain) { + if (this.options.plain) { result = (result.length === 0) ? null : result[0] } return result } + var transformRowsWithEagerLoadingIntoDaos = function(results) { + var result = [] + + result = prepareJoinData.call(this, results) + result = groupDataByCalleeFactory.call(this, result).map(function(result) { + return transformRowWithEagerLoadingIntoDao.call(this, result) + }.bind(this)) + + return result + } + + var transformRowWithEagerLoadingIntoDao = function(result, dao) { + // let's build the actual dao instance first... + dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false }) + + // ... and afterwards the prefetched associations + for (var tableName in result) { + if (result.hasOwnProperty(tableName) && (tableName !== this.callee.tableName)) { + buildAssociatedDaoInstances.call(this, tableName, result[tableName], dao) + } + } + + return dao + } + var buildAssociatedDaoInstances = function(tableName, associationData, dao) { var associatedDaoFactory = this.sequelize.daoFactoryManager.getDAO(tableName, { attribute: 'tableName' }) , association = null @@ -284,15 +323,23 @@ module.exports = (function() { associationData.forEach(function(data) { var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false }) + , isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers) if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) { accessor = Utils.singularize(accessor) - dao[accessor] = daoInstance + dao[accessor] = isEmpty ? null : daoInstance } else { dao[accessor] = dao[accessor] || [] - dao[accessor].push(daoInstance) + + if (!isEmpty) { + dao[accessor].push(daoInstance) + } } - }) + + // add the accessor to the eagerly loaded associations array + dao.__eagerlyLoadedAssociations = Utils._.uniq(dao.__eagerlyLoadedAssociations.concat([accessor])) + dao.__eagerlyLoadedOptions = (this.options && this.options.include) ? this.options.include : [] + }.bind(this)) } var isShowOrDescribeQuery = function() { diff --git a/lib/dialects/mysql/connector-manager.js b/lib/dialects/mysql/connector-manager.js index d8f3eab94f73..645fac1a85f7 100644 --- a/lib/dialects/mysql/connector-manager.js +++ b/lib/dialects/mysql/connector-manager.js @@ -1,9 +1,12 @@ -var mysql = require("mysql") +var mysql , Pooling = require('generic-pool') , Query = require("./query") , Utils = require("../../utils") , without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) } +try { mysql = require("mysql") } catch (err) { + console.log("You need to install mysql package manually"); } + module.exports = (function() { var ConnectorManager = function(sequelize, config) { this.sequelize = sequelize @@ -16,7 +19,9 @@ module.exports = (function() { this.poolCfg = Utils._.defaults(this.config.pool, { maxConnections: 10, minConnections: 0, - maxIdleTime: 1000 + maxIdleTime: 1000, + handleDisconnects: false, + validate: validateConnection }); this.pendingQueries = 0; this.useReplicaton = !!config.replication; @@ -25,7 +30,7 @@ module.exports = (function() { var self = this if (this.useReplicaton) { - var reads = 0, + var reads = 0, writes = 0; // Init configs with options from config if not present @@ -80,6 +85,7 @@ module.exports = (function() { destroy: function(client) { disconnect.call(self, client) }, + validate: self.poolCfg.validate, max: self.poolCfg.maxConnections, min: self.poolCfg.minConnections, idleTimeoutMillis: self.poolCfg.maxIdleTime @@ -95,6 +101,7 @@ module.exports = (function() { destroy: function(client) { disconnect.call(self, client) }, + validate: self.poolCfg.validate, max: self.poolCfg.maxConnections, min: self.poolCfg.minConnections, idleTimeoutMillis: self.poolCfg.maxIdleTime @@ -112,6 +119,7 @@ module.exports = (function() { }, max: self.poolCfg.maxConnections, min: self.poolCfg.minConnections, + validate: self.poolCfg.validate, idleTimeoutMillis: self.poolCfg.maxIdleTime }) } @@ -133,7 +141,9 @@ module.exports = (function() { var isConnecting = false; ConnectorManager.prototype.query = function(sql, callee, options) { - if (!this.isConnected && !this.pool) this.connect(); + if (!this.isConnected && !this.pool) { + this.connect() + } if (this.useQueue) { var queueItem = { @@ -160,7 +170,10 @@ module.exports = (function() { } }); - if (!this.pool) query.run(sql); + if (!this.pool) { + query.run(sql); + } + else { this.pool.acquire(function(err, client) { if (err) return query.emit('error', err); @@ -197,9 +210,14 @@ module.exports = (function() { var disconnect = function(client) { var self = this; - if (!this.useQueue) this.client = null; + if (!this.useQueue) { + this.client = null; + } + client.end(function() { - if (!self.useQueue) return client.destroy(); + if (!self.useQueue) { + return client.destroy(); + } var intervalObj = null var cleanup = function () { @@ -228,17 +246,34 @@ module.exports = (function() { port: config.port, user: config.username, password: config.password, - database: config.database + database: config.database, + timezone: 'Z' }) + connection.query("SET time_zone = '+0:00'"); // client.setMaxListeners(self.maxConcurrentQueries) this.isConnecting = false - + if (config.pool.handleDisconnects) { + handleDisconnect(this.pool, connection) + } done(null, connection) } + var handleDisconnect = function(pool, client) { + client.on('error', function(err) { + if (err.code !== 'PROTOCOL_CONNECTION_LOST') { + throw err + } + pool.destroy(client) + }) + } + + var validateConnection = function(client) { + return client && client.state != 'disconnected' + } + var enqueue = function(queueItem, options) { options = options || {} - if(this.activeQueue.length < this.maxConcurrentQueries) { + if (this.activeQueue.length < this.maxConcurrentQueries) { this.activeQueue.push(queueItem) if (this.pool) { var self = this @@ -254,9 +289,7 @@ module.exports = (function() { execQueueItem.call(self, queueItem) return }, undefined, options.type) - } - else - { + } else { execQueueItem.call(this, queueItem) } } else { @@ -275,7 +308,7 @@ module.exports = (function() { var transferQueuedItems = function(count) { for(var i = 0; i < count; i++) { var queueItem = this.queue.shift(); - if(queueItem) { + if (queueItem) { enqueue.call(this, queueItem) } } @@ -323,4 +356,4 @@ module.exports = (function() { } return ConnectorManager -})() \ No newline at end of file +})() diff --git a/lib/dialects/mysql/query-generator.js b/lib/dialects/mysql/query-generator.js index ff37f59a6f2c..ca6515e619df 100644 --- a/lib/dialects/mysql/query-generator.js +++ b/lib/dialects/mysql/query-generator.js @@ -2,8 +2,51 @@ var Utils = require("../../utils") , DataTypes = require("../../data-types") , util = require("util") +var processAndEscapeValue = function(value) { + var processedValue = value + if (value instanceof Date) { + processedValue = Utils.toSqlDate(value) + } else if (typeof value === 'boolean') { + processedValue = value ? 1 : 0 + } + return Utils.escape(processedValue) +} + module.exports = (function() { var QueryGenerator = { + addSchema: function(opts) { + var tableName + var schema = (!!opts && !!opts.options && !!opts.options.schema ? opts.options.schema : undefined) + var schemaDelimiter = (!!opts && !!opts.options && !!opts.options.schemaDelimiter ? opts.options.schemaDelimiter : undefined) + + if (!!opts && !!opts.tableName) { + tableName = opts.tableName + } + else if (typeof opts === "string") { + tableName = opts + } + + if (!schema || schema.toString().trim() === "") { + return tableName + } + + return QueryGenerator.addQuotes(schema + (!schemaDelimiter ? '.' : schemaDelimiter) + tableName) + }, + + createSchema: function() { + var query = "SHOW TABLES" + return Utils._.template(query)({}) + }, + + dropSchema: function() { + var query = "SHOW TABLES" + return Utils._.template(query)({}) + }, + + showSchemasQuery: function() { + return "SHOW TABLES" + }, + createTableQuery: function(tableName, attributes, options) { options = Utils._.extend({ engine: 'InnoDB', @@ -12,6 +55,7 @@ module.exports = (function() { var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>) ENGINE=<%= engine %> <%= charset %>" , primaryKeys = [] + , foreignKeys = {} , attrStr = [] for (var attr in attributes) { @@ -20,25 +64,36 @@ module.exports = (function() { if (Utils._.includes(dataType, 'PRIMARY KEY')) { primaryKeys.push(attr) - attrStr.push(Utils.addTicks(attr) + " " + dataType.replace(/PRIMARY KEY/, '')) + attrStr.push(QueryGenerator.addQuotes(attr) + " " + dataType.replace(/PRIMARY KEY/, '')) + } else if (Utils._.includes(dataType, 'REFERENCES')) { + // MySQL doesn't support inline REFERENCES declarations: move to the end + var m = dataType.match(/^(.+) (REFERENCES.*)$/) + attrStr.push(QueryGenerator.addQuotes(attr) + " " + m[1]) + foreignKeys[attr] = m[2] } else { - attrStr.push(Utils.addTicks(attr) + " " + dataType) + attrStr.push(QueryGenerator.addQuotes(attr) + " " + dataType) } } } var values = { - table: Utils.addTicks(tableName), + table: QueryGenerator.addQuotes(tableName), attributes: attrStr.join(", "), engine: options.engine, charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "") } - , pkString = primaryKeys.map(function(pk) { return Utils.addTicks(pk) }).join(", ") + , pkString = primaryKeys.map(function(pk) { return QueryGenerator.addQuotes(pk) }).join(", ") - if(pkString.length > 0) { + if (pkString.length > 0) { values.attributes += ", PRIMARY KEY (" + pkString + ")" } + for (var fkey in foreignKeys) { + if(foreignKeys.hasOwnProperty(fkey)) { + values.attributes += ", FOREIGN KEY (" + QueryGenerator.addQuotes(fkey) + ") " + foreignKeys[fkey] + } + } + return Utils._.template(query)(values).trim() + ";" }, @@ -47,7 +102,9 @@ module.exports = (function() { var query = "DROP TABLE IF EXISTS <%= table %>;" - return Utils._.template(query)({table: Utils.addTicks(tableName)}) + return Utils._.template(query)({ + table: QueryGenerator.addQuotes(tableName) + }) }, renameTableQuery: function(before, after) { @@ -84,7 +141,7 @@ module.exports = (function() { var query = "ALTER TABLE `<%= tableName %>` CHANGE <%= attributes %>;" var attrString = [] - for (attrName in attributes) { + for (var attrName in attributes) { var definition = attributes[attrName] attrString.push(Utils._.template('`<%= attrName %>` `<%= attrName %>` <%= definition %>')({ @@ -114,174 +171,201 @@ module.exports = (function() { }, selectQuery: function(tableName, options) { - var query = "SELECT <%= attributes %> FROM <%= table %>" - , table = null + var table = null, + joinQuery = "" options = options || {} - options.table = table = Array.isArray(tableName) ? tableName.map(function(tbl){ return Utils.addTicks(tbl) }).join(", ") : Utils.addTicks(tableName) + options.table = table = Array.isArray(tableName) ? tableName.map(function(tbl){ return QueryGenerator.addQuotes(tbl) }).join(", ") : QueryGenerator.addQuotes(tableName) options.attributes = options.attributes && options.attributes.map(function(attr){ if(Array.isArray(attr) && attr.length == 2) { - return [attr[0], Utils.addTicks(attr[1])].join(' as ') + return [attr[0], QueryGenerator.addQuotes(attr[1])].join(' as ') } else { - return attr.indexOf(Utils.TICK_CHAR) < 0 ? Utils.addTicks(attr) : attr + return attr.indexOf(Utils.TICK_CHAR) < 0 ? QueryGenerator.addQuotes(attr) : attr } }).join(", ") options.attributes = options.attributes || '*' if (options.include) { - var optAttributes = [options.table + '.*'] - - for (var daoName in options.include) { - if (options.include.hasOwnProperty(daoName)) { - var dao = options.include[daoName] - , daoFactory = dao.daoFactoryManager.getDAO(tableName, { - attribute: 'tableName' - }) - , _tableName = Utils.addTicks(dao.tableName) - , association = dao.getAssociation(daoFactory) - - if (association.connectorDAO) { - var foreignIdentifier = Utils._.keys(association.connectorDAO.rawAttributes).filter(function(attrName) { - return (!!attrName.match(/.+Id$/) || !!attrName.match(/.+_id$/)) && (attrName !== association.identifier) - })[0] - - query += ' LEFT OUTER JOIN ' + Utils.addTicks(association.connectorDAO.tableName) + ' ON ' - query += Utils.addTicks(association.connectorDAO.tableName) + '.' - query += Utils.addTicks(foreignIdentifier) + '=' - query += Utils.addTicks(table) + '.' + Utils.addTicks('id') - - query += ' LEFT OUTER JOIN ' + Utils.addTicks(dao.tableName) + ' ON ' - query += Utils.addTicks(dao.tableName) + '.' - query += Utils.addTicks('id') + '=' - query += Utils.addTicks(association.connectorDAO.tableName) + '.' + Utils.addTicks(association.identifier) - } else { - query += ' LEFT OUTER JOIN ' + Utils.addTicks(dao.tableName) + ' ON ' - query += Utils.addTicks(association.associationType === 'BelongsTo' ? dao.tableName : tableName) + '.' - query += Utils.addTicks(association.identifier) + '=' - query += Utils.addTicks(association.associationType === 'BelongsTo' ? tableName : dao.tableName) + '.' + Utils.addTicks('id') - } + var optAttributes = options.attributes === '*' ? [options.table + '.*'] : [options.attributes] - var aliasAssoc = daoFactory.getAssociationByAlias(daoName) - , aliasName = !!aliasAssoc ? Utils.addTicks(daoName) : _tableName - - optAttributes = optAttributes.concat( - Utils._.keys(dao.attributes).map(function(attr) { - return '' + - [_tableName, Utils.addTicks(attr)].join('.') + - ' AS ' + - Utils.addTicks([aliasName, attr].join('.')) - }) - ) - } - } + options.include.forEach(function(include) { + var attributes = Object.keys(include.daoFactory.attributes).map(function(attr) { + return "`" + include.as + "`.`" + attr + "` AS `" + include.as + "." + attr + "`" + }) + + optAttributes = optAttributes.concat(attributes) + + var table = include.daoFactory.tableName + var as = include.as + var tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName) + var attrLeft = 'id' + var tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as) + var attrRight = include.association.identifier + joinQuery += " LEFT OUTER JOIN `" + table + "` AS `" + as + "` ON `" + tableLeft + "`.`" + attrLeft + "` = `" + tableRight + "`.`" + attrRight + "`" + + }) options.attributes = optAttributes.join(', ') } - if(options.where) { + var query = "SELECT " + options.attributes + " FROM " + options.table + query += joinQuery + + if (options.hasOwnProperty('where')) { options.where = this.getWhereConditions(options.where, tableName) - query += " WHERE <%= where %>" + query += " WHERE " + options.where } - if(options.group) { - options.group = Utils.addTicks(options.group) - query += " GROUP BY <%= group %>" + if (options.group) { + options.group = Array.isArray(options.group) ? options.group.map(function(grp){return QueryGenerator.addQuotes(grp)}).join(', ') : QueryGenerator.addQuotes(options.group) + query += " GROUP BY " + options.group } - if(options.order) { - query += " ORDER BY <%= order %>" + if (options.order) { + query += " ORDER BY " + options.order } - if(options.limit && !(options.include && (options.limit === 1))) { - if(options.offset) { - query += " LIMIT <%= offset %>, <%= limit %>" + if (options.limit && !(options.include && (options.limit === 1))) { + if (options.offset) { + query += " LIMIT " + options.offset + ", " + options.limit } else { - query += " LIMIT <%= limit %>" + query += " LIMIT " + options.limit } } query += ";" - return Utils._.template(query)(options) + return query }, insertQuery: function(tableName, attrValueHash) { attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) - var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);" + var table = QueryGenerator.addQuotes(tableName) + var attributes = Object.keys(attrValueHash).map(function(attr){return QueryGenerator.addQuotes(attr)}).join(",") + var values = Utils._.values(attrValueHash).map(processAndEscapeValue).join(",") - var replacements = { - table: Utils.addTicks(tableName), - attributes: Utils._.keys(attrValueHash).map(function(attr){return Utils.addTicks(attr)}).join(","), - values: Utils._.values(attrValueHash).map(function(value){ - return Utils.escape((value instanceof Date) ? Utils.toSqlDate(value) : value) - }).join(",") - } + var query = "INSERT INTO " + table + " (" + attributes + ") VALUES (" + values + ");" + + return query + }, + + bulkInsertQuery: function(tableName, attrValueHashes) { + var tuples = [] + + Utils._.forEach(attrValueHashes, function(attrValueHash) { + tuples.push("(" + + Utils._.values(attrValueHash).map(processAndEscapeValue).join(",") + + ")") + }) + + var table = QueryGenerator.addQuotes(tableName) + var attributes = Object.keys(attrValueHashes[0]).map(function(attr){return QueryGenerator.addQuotes(attr)}).join(",") + + var query = "INSERT INTO " + table + " (" + attributes + ") VALUES " + tuples.join(",") + ";" - return Utils._.template(query)(replacements) + return query }, updateQuery: function(tableName, attrValueHash, where) { attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) - var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>" - , values = [] + var values = [] for (var key in attrValueHash) { var value = attrValueHash[key] - , _value = (value instanceof Date) ? Utils.toSqlDate(value) : value + , _value = processAndEscapeValue(value) - values.push(Utils.addTicks(key) + "=" + Utils.escape(_value)) + values.push(QueryGenerator.addQuotes(key) + "=" + _value) } - var replacements = { - table: Utils.addTicks(tableName), - values: values.join(","), - where: QueryGenerator.getWhereConditions(where) - } + var query = "UPDATE " + QueryGenerator.addQuotes(tableName) + + " SET " + values.join(",") + + " WHERE " + QueryGenerator.getWhereConditions(where) - return Utils._.template(query)(replacements) + return query }, deleteQuery: function(tableName, where, options) { options = options || {} - options.limit = options.limit || 1 - var query = "DELETE FROM <%= table %> WHERE <%= where %> LIMIT <%= limit %>" - var replacements = { - table: Utils.addTicks(tableName), - where: QueryGenerator.getWhereConditions(where), - limit: Utils.escape(options.limit) + var table = QueryGenerator.addQuotes(tableName) + where = QueryGenerator.getWhereConditions(where) + var limit = "" + + if(Utils._.isUndefined(options.limit)) { + options.limit = 1; + } + + if(!!options.limit) { + limit = " LIMIT " + Utils.escape(options.limit) + } + + var query = "DELETE FROM " + table + " WHERE " + where + limit + + return query + }, + + bulkDeleteQuery: function(tableName, where, options) { + options = options || {} + + var table = QueryGenerator.addQuotes(tableName) + where = QueryGenerator.getWhereConditions(where) + + var query = "DELETE FROM " + table + " WHERE " + where + + return query + }, + + incrementQuery: function (tableName, attrValueHash, where) { + attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) + + var values = [] + + for (var key in attrValueHash) { + var value = attrValueHash[key] + , _value = processAndEscapeValue(value) + + values.push(QueryGenerator.addQuotes(key) + "=" + QueryGenerator.addQuotes(key) + " + " + _value) } - return Utils._.template(query)(replacements) + var table = QueryGenerator.addQuotes(tableName) + values = values.join(",") + where = QueryGenerator.getWhereConditions(where) + + var query = "UPDATE " + table + " SET " + values + " WHERE " + where + + return query }, addIndexQuery: function(tableName, attributes, options) { var transformedAttributes = attributes.map(function(attribute) { - if(typeof attribute == 'string') + if(typeof attribute === 'string') { return attribute - else { + } else { var result = "" - if(!attribute.attribute) + if (!attribute.attribute) { throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute)) + } result += attribute.attribute - if(attribute.length) + if (attribute.length) { result += '(' + attribute.length + ')' + } - if(attribute.order) + if (attribute.order) { result += ' ' + attribute.order + } return result } }) var onlyAttributeNames = attributes.map(function(attribute) { - return (typeof attribute == 'string') ? attribute : attribute.attribute + return (typeof attribute === 'string') ? attribute : attribute.attribute }) options = Utils._.extend({ @@ -310,8 +394,9 @@ module.exports = (function() { var sql = "DROP INDEX <%= indexName %> ON <%= tableName %>" , indexName = indexNameOrAttributes - if(typeof indexName != 'string') + if (typeof indexName !== 'string') { indexName = Utils._.underscored(tableName + '_' + indexNameOrAttributes.join('_')) + } return Utils._.template(sql)({ tableName: tableName, indexName: indexName }) }, @@ -319,7 +404,7 @@ module.exports = (function() { getWhereConditions: function(smth, tableName) { var result = null - if(Utils.isHash(smth)) { + if (Utils.isHash(smth)) { smth = Utils.prependTableNameToHash(tableName, smth) result = this.hashToWhereConditions(smth) } else if (typeof smth === 'number') { @@ -331,7 +416,7 @@ module.exports = (function() { result = Utils.format(smth) } - return result + return result ? result : '1=1' }, hashToWhereConditions: function(hash) { @@ -341,25 +426,23 @@ module.exports = (function() { var value = hash[key] //handle qualified key names - var _key = key.split('.').map(function(col){return Utils.addTicks(col)}).join(".") + var _key = key.split('.').map(function(col){return QueryGenerator.addQuotes(col)}).join(".") , _value = null if (Array.isArray(value)) { // is value an array? - if(value.length == 0) { value = [null] } - _value = "(" + value.map(function(subValue) { - return Utils.escape(subValue); - }).join(',') + ")" + if (value.length === 0) { value = [null] } + _value = "(" + value.map(processAndEscapeValue).join(',') + ")" result.push([_key, _value].join(" IN ")) - } else if ((value) && (typeof value == 'object')) { + } else if ((value) && (typeof value == 'object') && !(value instanceof Date)) { // is value an object? //using as sentinel for join column => value - _value = value.join.split('.').map(function(col){ return Utils.addTicks(col) }).join(".") + _value = value.join.split('.').map(function(col){ return QueryGenerator.addQuotes(col) }).join(".") result.push([_key, _value].join("=")) } else { - _value = Utils.escape(value) + _value = processAndEscapeValue(value) result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) } } @@ -373,32 +456,62 @@ module.exports = (function() { for (var name in attributes) { var dataType = attributes[name] - if(Utils.isHash(dataType)) { - var template = "<%= type %>" - , replacements = { type: dataType.type } + if (Utils.isHash(dataType)) { + var template - if(dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) { + if (dataType.type.toString() === DataTypes.ENUM.toString()) { + if (Array.isArray(dataType.values) && (dataType.values.length > 0)) { + template = "ENUM(" + Utils._.map(dataType.values, function(value) { + return Utils.escape(value) + }).join(", ") + ")" + } else { + throw new Error('Values for ENUM haven\'t been defined.') + } + } else { + template = dataType.type.toString(); + } + + if (dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) { template += " NOT NULL" } - if(dataType.autoIncrement) { + if (dataType.autoIncrement) { template += " auto_increment" } - if((dataType.defaultValue != undefined) && (dataType.defaultValue != DataTypes.NOW)) { - template += " DEFAULT <%= defaultValue %>" - replacements.defaultValue = Utils.escape(dataType.defaultValue) + if ((dataType.defaultValue !== undefined) && (dataType.defaultValue != DataTypes.NOW)) { + template += " DEFAULT " + Utils.escape(dataType.defaultValue) } - if(dataType.unique) { + if (dataType.unique) { template += " UNIQUE" } - if(dataType.primaryKey) { + if (dataType.primaryKey) { template += " PRIMARY KEY" } - result[name] = Utils._.template(template)(replacements) + if(dataType.references) { + template += " REFERENCES " + Utils.addTicks(dataType.references) + + + if(dataType.referencesKey) { + template += " (" + Utils.addTicks(dataType.referencesKey) + ")" + } else { + template += " (" + Utils.addTicks('id') + ")" + } + + if(dataType.onDelete) { + template += " ON DELETE " + dataType.onDelete.toUpperCase() + } + + if(dataType.onUpdate) { + template += " ON UPDATE " + dataType.onUpdate.toUpperCase() + } + + } + + result[name] = template } else { result[name] = dataType } @@ -421,6 +534,24 @@ module.exports = (function() { } return fields + }, + + enableForeignKeyConstraintsQuery: function() { + var sql = "SET FOREIGN_KEY_CHECKS = 1;" + return Utils._.template(sql, {}) + }, + + disableForeignKeyConstraintsQuery: function() { + var sql = "SET FOREIGN_KEY_CHECKS = 0;" + return Utils._.template(sql, {}) + }, + + addQuotes: function(s, quoteChar) { + return Utils.addTicks(s, quoteChar) + }, + + removeQuotes: function(s, quoteChar) { + return Utils.removeTicks(s, quoteChar) } } diff --git a/lib/dialects/mysql/query.js b/lib/dialects/mysql/query.js index 91d883c32e70..41d113dd83ee 100644 --- a/lib/dialects/mysql/query.js +++ b/lib/dialects/mysql/query.js @@ -19,7 +19,7 @@ module.exports = (function() { Query.prototype.run = function(sql) { this.sql = sql - if(this.options.logging !== false) { + if (this.options.logging !== false) { this.options.logging('Executing: ' + this.sql) } @@ -37,3 +37,5 @@ module.exports = (function() { return Query })() + + diff --git a/lib/dialects/postgres/connector-manager.js b/lib/dialects/postgres/connector-manager.js index 171ffa7aad24..1037c05da610 100644 --- a/lib/dialects/postgres/connector-manager.js +++ b/lib/dialects/postgres/connector-manager.js @@ -26,9 +26,14 @@ module.exports = (function() { ConnectorManager.prototype.query = function(sql, callee, options) { var self = this - if (this.client == null) this.connect() + if (this.client == null) { + this.connect() + } + var query = new Query(this.client, this.sequelize, callee, options || {}) + self.pendingQueries += 1 + return query.run(sql) .success(function() { self.endQuery.call(self) }) .error(function() { self.endQuery.call(self) }) @@ -46,9 +51,13 @@ module.exports = (function() { ConnectorManager.prototype.connect = function() { var self = this + var emitter = new (require('events').EventEmitter)() // in case database is slow to connect, prevent orphaning the client - if (this.isConnecting) return + if (this.isConnecting) { + return + } + this.isConnecting = true this.isConnected = false @@ -58,7 +67,7 @@ module.exports = (function() { self.isConnecting = false if (!!err) { - throw err + emitter.emit('error', err) } else if (client) { client.query("SET TIME ZONE 'UTC'") .on('end', function() { @@ -78,6 +87,8 @@ module.exports = (function() { this.client = new this.pg.Client(uri) this.client.connect(connectCallback) } + + return emitter } ConnectorManager.prototype.disconnect = function() { diff --git a/lib/dialects/postgres/query-generator.js b/lib/dialects/postgres/query-generator.js index 625ac923a0d6..c852a642df26 100644 --- a/lib/dialects/postgres/query-generator.js +++ b/lib/dialects/postgres/query-generator.js @@ -1,75 +1,45 @@ -var Utils = require("../../utils") - , util = require("util") - , tables = {} - , primaryKeys = {}; - -function removeQuotes(s, quoteChar) { - quoteChar = quoteChar || '"' - return s.replace(new RegExp(quoteChar, 'g'), '') -} - -function addQuotes(s, quoteChar) { - quoteChar = quoteChar || '"' - return removeQuotes(s, quoteChar) - .split('.') - .map(function(e) { return quoteChar + String(e) + quoteChar }) - .join('.') -} - -function pgEscape(val) { - if (val === undefined || val === null) { - return 'NULL'; - } +var Utils = require("../../utils") + , util = require("util") + , DataTypes = require("../../data-types") + , tables = {} + , primaryKeys = {} - switch (typeof val) { - case 'boolean': return (val) ? 'true' : 'false'; - case 'number': return val+''; - } +module.exports = (function() { + var QueryGenerator = { + options: {}, - if (val instanceof Date) { - val = pgSqlDate(val); - } + addSchema: function(opts) { + var tableName = undefined + var schema = (!!opts.options && !!opts.options.schema ? opts.options.schema : undefined) + var schemaDelimiter = (!!opts.options && !!opts.options.schemaDelimiter ? opts.options.schemaDelimiter : undefined) - // http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS - val = val.replace(/'/g, "''"); - return "'"+val+"'"; -} - -function padInt(i) { - return (i < 10) ? '0' + i.toString() : i.toString() -} -function pgSqlDate(dt) { - var date = [ dt.getUTCFullYear(), padInt(dt.getUTCMonth()+1), padInt(dt.getUTCDate()) ].join('-') - var time = [ dt.getUTCHours(), padInt(dt.getUTCMinutes()), padInt(dt.getUTCSeconds())].join(':') - return date + ' ' + time + '.' + ((dt.getTime() % 1000) * 1000) -} - -function pgDataTypeMapping(tableName, attr, dataType) { - if (Utils._.includes(dataType, 'PRIMARY KEY')) { - primaryKeys[tableName].push(attr) - dataType = dataType.replace(/PRIMARY KEY/, '') - } + if (!!opts.tableName) { + tableName = opts.tableName + } + else if (typeof opts === "string") { + tableName = opts + } - if (Utils._.includes(dataType, 'TINYINT(1)')) { - dataType = dataType.replace(/TINYINT\(1\)/, 'BOOLEAN') - } + if (!schema || schema.toString().trim() === "") { + return tableName + } - if (Utils._.includes(dataType, 'DATETIME')) { - dataType = dataType.replace(/DATETIME/, 'TIMESTAMP') - } + return QueryGenerator.addQuotes(schema) + '.' + QueryGenerator.addQuotes(tableName) + }, - if (Utils._.includes(dataType, 'SERIAL')) { - dataType = dataType.replace(/INTEGER/, '') - dataType = dataType.replace(/NOT NULL/, '') - tables[tableName][attr] = 'serial' - } + createSchema: function(schema) { + var query = "CREATE SCHEMA <%= schema%>;" + return Utils._.template(query)({schema: schema}) + }, - return dataType -} + dropSchema: function(schema) { + var query = "DROP SCHEMA <%= schema%> CASCADE;" + return Utils._.template(query)({schema: schema}) + }, -module.exports = (function() { - var QueryGenerator = { - options: {}, + showSchemasQuery: function() { + return "SELECT schema_name FROM information_schema.schemata WHERE schema_name <> 'information_schema' AND schema_name != 'public' AND schema_name !~ E'^pg_';" + }, createTableQuery: function(tableName, attributes, options) { options = Utils._.extend({ @@ -82,16 +52,23 @@ module.exports = (function() { , attrStr = [] for (var attr in attributes) { - var dataType = pgDataTypeMapping(tableName, attr, attributes[attr]) - attrStr.push(addQuotes(attr) + " " + dataType) + var dataType = QueryGenerator.pgDataTypeMapping(tableName, attr, attributes[attr]) + attrStr.push(QueryGenerator.addQuotes(attr) + " " + dataType) + + if (attributes[attr].match(/^ENUM\(/)) { + query = QueryGenerator.pgEnum(tableName, attr, attributes[attr]) + query + } } var values = { - table: addQuotes(tableName), - attributes: attrStr.join(", "), + table: QueryGenerator.addQuotes(tableName), + attributes: attrStr.join(", ") } - var pks = primaryKeys[tableName].map(function(pk){ return addQuotes(pk) }).join(",") + var pks = primaryKeys[tableName].map(function(pk){ + return QueryGenerator.addQuotes(pk) + }).join(",") + if (pks.length > 0) { values.attributes += ", PRIMARY KEY (" + pks + ")" } @@ -101,13 +78,19 @@ module.exports = (function() { dropTableQuery: function(tableName, options) { options = options || {} - var query = "DROP TABLE IF EXISTS <%= table %>;" - return Utils._.template(query)({ table: addQuotes(tableName) }) + var query = "DROP TABLE IF EXISTS <%= table %><%= cascade %>;" + return Utils._.template(query)({ + table: QueryGenerator.addQuotes(tableName), + cascade: options.cascade? " CASCADE" : "" + }) }, renameTableQuery: function(before, after) { var query = "ALTER TABLE <%= before %> RENAME TO <%= after %>;" - return Utils._.template(query)({ before: addQuotes(before), after: addQuotes(after) }) + return Utils._.template(query)({ + before: QueryGenerator.addQuotes(before), + after: QueryGenerator.addQuotes(after) + }) }, showTablesQuery: function() { @@ -116,7 +99,9 @@ module.exports = (function() { describeTableQuery: function(tableName) { var query = 'SELECT column_name as "Field", column_default as "Default", is_nullable as "Null", data_type as "Type" FROM information_schema.columns WHERE table_name = <%= table %>;' - return Utils._.template(query)({ table: addQuotes(tableName, "'") }) + return Utils._.template(query)({ + table: QueryGenerator.addQuotes(tableName, "'") + }) }, addColumnQuery: function(tableName, attributes) { @@ -127,17 +112,26 @@ module.exports = (function() { var definition = attributes[attrName] attrString.push(Utils._.template('<%= attrName %> <%= definition %>')({ - attrName: addQuotes(attrName), - definition: pgDataTypeMapping(tableName, attrName, definition) + attrName: QueryGenerator.addQuotes(attrName), + definition: QueryGenerator.pgDataTypeMapping(tableName, attrName, definition) })) + + if (definition.match(/^ENUM\(/)) { + query = QueryGenerator.pgEnum(tableName, attrName, definition) + query + } } - return Utils._.template(query)({ tableName: addQuotes(tableName), attributes: attrString.join(', ') }) + return Utils._.template(query)({ + tableName: QueryGenerator.addQuotes(tableName), + attributes: attrString.join(', ') }) }, removeColumnQuery: function(tableName, attributeName) { var query = "ALTER TABLE <%= tableName %> DROP COLUMN <%= attributeName %>;" - return Utils._.template(query)({ tableName: addQuotes(tableName), attributeName: addQuotes(attributeName) }) + return Utils._.template(query)({ + tableName: QueryGenerator.addQuotes(tableName), + attributeName: QueryGenerator.addQuotes(attributeName) + }) }, changeColumnQuery: function(tableName, attributes) { @@ -150,20 +144,40 @@ module.exports = (function() { if (definition.indexOf('NOT NULL') > 0) { attrSql += Utils._.template(query)({ - tableName: addQuotes(tableName), - query: addQuotes(attributeName) + ' SET NOT NULL' + tableName: QueryGenerator.addQuotes(tableName), + query: QueryGenerator.addQuotes(attributeName) + ' SET NOT NULL' }) + definition = definition.replace('NOT NULL', '').trim() } else { attrSql += Utils._.template(query)({ - tableName: addQuotes(tableName), - query: addQuotes(attributeName) + ' DROP NOT NULL' + tableName: QueryGenerator.addQuotes(tableName), + query: QueryGenerator.addQuotes(attributeName) + ' DROP NOT NULL' + }) + } + + if (definition.indexOf('DEFAULT') > 0) { + attrSql += Utils._.template(query)({ + tableName: QueryGenerator.addQuotes(tableName), + query: QueryGenerator.addQuotes(attributeName) + ' SET DEFAULT' + definition.match(/DEFAULT ([^;]+)/)[1] + }) + + definition = definition.replace(/(DEFAULT[^;]+)/, '').trim() + } else { + attrSql += Utils._.template(query)({ + tableName: QueryGenerator.addQuotes(tableName), + query: QueryGenerator.addQuotes(attributeName) + ' DROP DEFAULT' }) } + if (definition.match(/^ENUM\(/)) { + query = QueryGenerator.pgEnum(tableName, attributeName, definition) + query + definition = definition.replace(/^ENUM\(.+\)/, Utils.escape("enum_" + tableName + "_" + attributeName)) + } + attrSql += Utils._.template(query)({ - tableName: addQuotes(tableName), - query: addQuotes(attributeName) + ' TYPE ' + definition + tableName: QueryGenerator.addQuotes(tableName), + query: QueryGenerator.addQuotes(attributeName) + ' TYPE ' + definition }) sql.push(attrSql) @@ -178,12 +192,15 @@ module.exports = (function() { for (var attributeName in attributes) { attrString.push(Utils._.template('<%= before %> TO <%= after %>')({ - before: addQuotes(attrBefore), - after: addQuotes(attributeName), + before: QueryGenerator.addQuotes(attrBefore), + after: QueryGenerator.addQuotes(attributeName) })) } - return Utils._.template(query)({ tableName: addQuotes(tableName), attributes: attrString.join(', ') }) + return Utils._.template(query)({ + tableName: QueryGenerator.addQuotes(tableName), + attributes: attrString.join(', ') + }) }, selectQuery: function(tableName, options) { @@ -191,83 +208,80 @@ module.exports = (function() { , table = null options = options || {} - options.table = table = Array.isArray(tableName) ? tableName.map(function(t){return addQuotes(t);}).join(", ") : addQuotes(tableName) - options.attributes = options.attributes && options.attributes.map(function(attr){ - if(Array.isArray(attr) && attr.length == 2) { - return [attr[0], addQuotes(removeQuotes(attr[1], '`'))].join(' as ') + + if (Array.isArray(tableName)) { + options.table = table = tableName.map(function(t){ + return QueryGenerator.addQuotes(t) + }).join(", ") + } else { + options.table = table = QueryGenerator.addQuotes(tableName) + } + + options.attributes = options.attributes && options.attributes.map(function(attr) { + if (Array.isArray(attr) && attr.length === 2) { + return [ + attr[0], + QueryGenerator.addQuotes(QueryGenerator.removeQuotes(attr[1], '`')) + ].join(' as ') } else if (attr.indexOf('`') >= 0) { return attr.replace(/`/g, '"') } else { - return addQuotes(attr) + return options.table + '.' + QueryGenerator.addQuotes(attr) } }).join(", ") + options.attributes = options.attributes || '*' if (options.include) { - var optAttributes = [options.table + '.*'] - - for (var daoName in options.include) { - if (options.include.hasOwnProperty(daoName)) { - var dao = options.include[daoName] - , daoFactory = dao.daoFactoryManager.getDAO(tableName, { - attribute: 'tableName' - }) - , _tableName = addQuotes(dao.tableName) - , association = dao.getAssociation(daoFactory) - - if (association.connectorDAO) { - var foreignIdentifier = Utils._.keys(association.connectorDAO.rawAttributes).filter(function(attrName) { - return (!!attrName.match(/.+Id$/) || !!attrName.match(/.+_id$/)) && (attrName !== association.identifier) - })[0] - - query += ' LEFT OUTER JOIN ' + addQuotes(association.connectorDAO.tableName) + ' ON ' - query += addQuotes(association.connectorDAO.tableName) + '.' - query += addQuotes(foreignIdentifier) + '=' - query += addQuotes(table) + '.' + addQuotes('id') - - query += ' LEFT OUTER JOIN ' + addQuotes(dao.tableName) + ' ON ' - query += addQuotes(dao.tableName) + '.' - query += addQuotes('id') + '=' - query += addQuotes(association.connectorDAO.tableName) + '.' + addQuotes(association.identifier) - } else { - query += ' LEFT OUTER JOIN ' + addQuotes(dao.tableName) + ' ON ' - query += addQuotes(association.associationType === 'BelongsTo' ? dao.tableName : tableName) + '.' - query += addQuotes(association.identifier) + '=' - query += addQuotes(association.associationType === 'BelongsTo' ? tableName : dao.tableName) + '.' + addQuotes('id') - } + var optAttributes = options.attributes === '*' ? [options.table + '.*'] : [options.attributes] - var aliasAssoc = daoFactory.getAssociationByAlias(daoName) - , aliasName = !!aliasAssoc ? addQuotes(daoName) : _tableName - - optAttributes = optAttributes.concat( - Utils._.keys(dao.attributes).map(function(attr) { - return '' + - [_tableName, addQuotes(attr)].join('.') + - ' AS "' + - removeQuotes([aliasName, attr].join('.')) + '"' - }) - ) - } - } + options.include.forEach(function(include) { + var attributes = Object.keys(include.daoFactory.attributes).map(function(attr) { + var template = Utils._.template('"<%= as %>"."<%= attr %>" AS "<%= as %>.<%= attr %>"') + return template({ as: include.as, attr: attr }) + }) + + optAttributes = optAttributes.concat(attributes) + + var joinQuery = ' LEFT OUTER JOIN "<%= table %>" AS "<%= as %>" ON "<%= tableLeft %>"."<%= attrLeft %>" = "<%= tableRight %>"."<%= attrRight %>"' + + query += Utils._.template(joinQuery)({ + table: include.daoFactory.tableName, + as: include.as, + tableLeft: ((include.association.associationType === 'BelongsTo') ? include.as : tableName), + attrLeft: 'id', + tableRight: ((include.association.associationType === 'BelongsTo') ? tableName : include.as), + attrRight: include.association.identifier + }) + }) options.attributes = optAttributes.join(', ') } - if(options.where) { - options.where = QueryGenerator.getWhereConditions(options.where) + if(options.hasOwnProperty('where')) { + options.where = QueryGenerator.getWhereConditions(options.where, tableName) query += " WHERE <%= where %>" } - if(options.order) { - options.order = options.order.replace(/([^ ]+)(.*)/, function(m, g1, g2) { return addQuotes(g1)+g2 }) - query += " ORDER BY <%= order %>" - } - if(options.group) { - options.group = addQuotes(options.group) + if (Array.isArray(options.group)) { + options.group = options.group.map(function(grp){ + return QueryGenerator.addQuotes(grp) + }).join(', ') + } else { + options.group = QueryGenerator.addQuotes(options.group) + } + query += " GROUP BY <%= group %>" } + if(options.order) { + options.order = options.order.replace(/([^ ]+)(.*)/, function(m, g1, g2) { + return QueryGenerator.addQuotes(g1) + g2 + }) + query += " ORDER BY <%= order %>" + } + if (!(options.include && (options.limit === 1))) { if (options.limit) { query += " LIMIT <%= limit %>" @@ -284,28 +298,46 @@ module.exports = (function() { }, insertQuery: function(tableName, attrValueHash) { - attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) + attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull); + + // added by Scott Rutherford 01/29/13 + delete attrValueHash['id']; var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;" - , returning = [] + , returning = removeSerialsFromHash(tableName, attrValueHash) - Utils._.forEach(attrValueHash, function(value, key, hash) { - if (tables[tableName] && tables[tableName][key]) { - switch (tables[tableName][key]) { - case 'serial': - delete hash[key] - returning.push(key) - break - } - } - }); + var replacements = { + table: QueryGenerator.addQuotes(tableName) + , attributes: Object.keys(attrValueHash).map(function(attr){ + return QueryGenerator.addQuotes(attr) + }).join(",") + , values: Utils._.values(attrValueHash).map(function(value){ + return QueryGenerator.pgEscape(value) + }).join(",") + } + + return Utils._.template(query)(replacements) + }, + + bulkInsertQuery: function(tableName, attrValueHashes) { + var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %> RETURNING *;" + , tuples = [] + + Utils._.forEach(attrValueHashes, function(attrValueHash) { + removeSerialsFromHash(tableName, attrValueHash) + tuples.push("(" + + Utils._.values(attrValueHash).map(function(value){ + return QueryGenerator.pgEscape(value) + }).join(",") + + ")") + }) var replacements = { - table: addQuotes(tableName), - attributes: Utils._.keys(attrValueHash).map(function(attr){return addQuotes(attr)}).join(","), - values: Utils._.values(attrValueHash).map(function(value){ - return pgEscape(value) - }).join(",") + table: QueryGenerator.addQuotes(tableName) + , attributes: Object.keys(attrValueHashes[0]).map(function(attr){ + return QueryGenerator.addQuotes(attr) + }).join(",") + , tuples: tuples.join(",") } return Utils._.template(query)(replacements) @@ -319,13 +351,13 @@ module.exports = (function() { for (var key in attrValueHash) { var value = attrValueHash[key] - values.push(addQuotes(key) + "=" + pgEscape(value)) + values.push(QueryGenerator.addQuotes(key) + "=" + QueryGenerator.pgEscape(value)) } var replacements = { - table: addQuotes(tableName), + table: QueryGenerator.addQuotes(tableName), values: values.join(","), - where: QueryGenerator.getWhereConditions(where) + where: QueryGenerator.getWhereConditions(where) } return Utils._.template(query)(replacements) @@ -333,23 +365,28 @@ module.exports = (function() { deleteQuery: function(tableName, where, options) { options = options || {} - options.limit = options.limit || 1 + + if(Utils._.isUndefined(options.limit)) { + options.limit = 1; + } primaryKeys[tableName] = primaryKeys[tableName] || []; - var query = "DELETE FROM <%= table %> WHERE <%= primaryKeys %> IN (SELECT <%= primaryKeysSelection %> FROM <%= table %> WHERE <%= where %> LIMIT <%= limit %>)" + var query = "DELETE FROM <%= table %> WHERE <%= primaryKeys %> IN (SELECT <%= primaryKeysSelection %> FROM <%= table %> WHERE <%= where %><%= limit %>)" var pks; if (primaryKeys[tableName] && primaryKeys[tableName].length > 0) { - pks = primaryKeys[tableName].map(function(pk) { return addQuotes(pk) }).join(',') + pks = primaryKeys[tableName].map(function(pk) { + return QueryGenerator.addQuotes(pk) + }).join(',') } else { - pks = addQuotes('id') + pks = QueryGenerator.addQuotes('id') } var replacements = { - table: addQuotes(tableName), + table: QueryGenerator.addQuotes(tableName), where: QueryGenerator.getWhereConditions(where), - limit: pgEscape(options.limit), + limit: !!options.limit? " LIMIT " + QueryGenerator.pgEscape(options.limit) : "", primaryKeys: primaryKeys[tableName].length > 1 ? '(' + pks + ')' : pks, primaryKeysSelection: pks } @@ -357,43 +394,67 @@ module.exports = (function() { return Utils._.template(query)(replacements) }, + incrementQuery: function(tableName, attrValueHash, where) { + attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) + + var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %> RETURNING *" + , values = [] + + for (var key in attrValueHash) { + var value = attrValueHash[key] + values.push(QueryGenerator.addQuotes(key) + "=" + QueryGenerator.addQuotes(key) + " + " + QueryGenerator.pgEscape(value)) + } + + var replacements = { + table: QueryGenerator.addQuotes(tableName), + values: values.join(","), + where: QueryGenerator.getWhereConditions(where) + } + + return Utils._.template(query)(replacements) + }, + + addIndexQuery: function(tableName, attributes, options) { var transformedAttributes = attributes.map(function(attribute) { - if(typeof attribute == 'string') - return addQuotes(attribute) - else { + if (typeof attribute === 'string') { + return QueryGenerator.addQuotes(attribute) + } else { var result = "" - if(!attribute.attribute) + if (!attribute.attribute) { throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute)) + } - result += addQuotes(attribute.attribute) + result += QueryGenerator.addQuotes(attribute.attribute) - if(attribute.length) + if (attribute.length) { result += '(' + attribute.length + ')' + } - if(attribute.order) + if (attribute.order) { result += ' ' + attribute.order + } return result } }) var onlyAttributeNames = attributes.map(function(attribute) { - return (typeof attribute == 'string') ? attribute : attribute.attribute + return (typeof attribute === "string") ? attribute : attribute.attribute }) var indexTable = tableName.split('.') options = Utils._.extend({ indicesType: null, - indexName: Utils._.underscored(indexTable[indexTable.length-1] + '_' + onlyAttributeNames.join('_')), - parser: null + indexName: Utils._.underscored(indexTable[indexTable.length-1] + '_' + onlyAttributeNames.join('_')), + parser: null }, options || {}) return Utils._.compact([ - "CREATE", options.indicesType, "INDEX", addQuotes(options.indexName), + "CREATE", options.indicesType, "INDEX", QueryGenerator.addQuotes(options.indexName), (options.indexType ? ('USING ' + options.indexType) : undefined), - "ON", addQuotes(tableName), '(' + transformedAttributes.join(', ') + ')' + "ON", QueryGenerator.addQuotes(tableName), '(' + transformedAttributes.join(', ') + ')' ]).join(' ') }, @@ -406,23 +467,33 @@ module.exports = (function() { var sql = "DROP INDEX IF EXISTS <%= indexName %>" , indexName = indexNameOrAttributes - if(typeof indexName != 'string') + if (typeof indexName !== "string") { indexName = Utils._.underscored(tableName + '_' + indexNameOrAttributes.join('_')) + } - return Utils._.template(sql)({ tableName: addQuotes(tableName), indexName: addQuotes(indexName) }) + return Utils._.template(sql)({ + tableName: QueryGenerator.addQuotes(tableName), + indexName: QueryGenerator.addQuotes(indexName) + }) }, - getWhereConditions: function(smth) { + getWhereConditions: function(smth, tableName) { var result = null - if(Utils.isHash(smth)) + if (Utils.isHash(smth)) { + smth = Utils.prependTableNameToHash(tableName, smth) result = QueryGenerator.hashToWhereConditions(smth) - else if(typeof smth == 'number') - result = '\"id\"' + "=" + pgEscape(smth) - else if(typeof smth == "string") + } + else if (typeof smth === "number") { + smth = Utils.prependTableNameToHash(tableName, { id: smth }) + result = QueryGenerator.hashToWhereConditions(smth) + } + else if (typeof smth === "string") { result = smth - else if(Array.isArray(smth)) + } + else if (Array.isArray(smth)) { result = Utils.format(smth) + } return result }, @@ -434,23 +505,23 @@ module.exports = (function() { var value = hash[key] //handle qualified key names - var _key = key.split('.').map(function(col){return addQuotes(col)}).join(".") + var _key = key.split('.').map(function(col){return QueryGenerator.addQuotes(col)}).join(".") , _value = null - if(Array.isArray(value)) { - if(value.length == 0) { value = [null] } + if (Array.isArray(value)) { + if (value.length == 0) { value = [null] } _value = "(" + value.map(function(subValue) { - return pgEscape(subValue); + return QueryGenerator.pgEscape(subValue); }).join(',') + ")" result.push([_key, _value].join(" IN ")) } - else if ((value) && (typeof value == 'object')) { + else if ((value) && (typeof value === "object")) { //using as sentinel for join column => value - _value = value.join.split('.').map(function(col){return addQuotes(col)}).join(".") + _value = value.join.split('.').map(function(col){return QueryGenerator.addQuotes(col)}).join(".") result.push([_key, _value].join("=")) } else { - _value = pgEscape(value) + _value = QueryGenerator.pgEscape(value) result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) } } @@ -468,17 +539,66 @@ module.exports = (function() { var template = "<%= type %>" , replacements = { type: dataType.type } - if(dataType.type == 'TINYINT(1)') dataType.type = 'BOOLEAN' - if(dataType.type == 'DATETIME') dataType.type = 'TIMESTAMP' + if (dataType.type.toString() === DataTypes.ENUM.toString()) { + if (Array.isArray(dataType.values) && (dataType.values.length > 0)) { + replacements.type = "ENUM(" + Utils._.map(dataType.values, function(value) { + return Utils.escape(value) + }).join(", ") + ")" + } else { + throw new Error('Values for ENUM haven\'t been defined.') + } + } + + if (dataType.type === "TINYINT(1)") { + dataType.type = 'BOOLEAN' + } + + if (dataType.type === "DATETIME") { + dataType.type = 'TIMESTAMP WITH TIME ZONE' + } + + if (dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) { + template += " NOT NULL" + } + + if (dataType.autoIncrement) { + template +=" SERIAL" + } - if(dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) template += " NOT NULL" - if(dataType.autoIncrement) template +=" SERIAL" - if(dataType.defaultValue != undefined) { + if (dataType.defaultValue !== undefined) { template += " DEFAULT <%= defaultValue %>" - replacements.defaultValue = pgEscape(dataType.defaultValue) + replacements.defaultValue = QueryGenerator.pgEscape(dataType.defaultValue) + } + + if (dataType.unique) { + template += " UNIQUE" + } + + if (dataType.primaryKey) { + template += " PRIMARY KEY" + } + + if(dataType.references) { + template += " REFERENCES <%= referencesTable %> (<%= referencesKey %>)" + replacements.referencesTable = QueryGenerator.addQuotes(dataType.references) + + if(dataType.referencesKey) { + replacements.referencesKey = QueryGenerator.addQuotes(dataType.referencesKey) + } else { + replacements.referencesKey = QueryGenerator.addQuotes('id') + } + + if(dataType.onDelete) { + template += " ON DELETE <%= onDeleteAction %>" + replacements.onDeleteAction = dataType.onDelete.toUpperCase() + } + + if(dataType.onUpdate) { + template += " ON UPDATE <%= onUpdateAction %>" + replacements.onUpdateAction = dataType.onUpdate.toUpperCase() + } + } - if(dataType.unique) template += " UNIQUE" - if(dataType.primaryKey) template += " PRIMARY KEY" result[name] = Utils._.template(template)(replacements) } else { @@ -503,19 +623,147 @@ module.exports = (function() { return fields }, + enableForeignKeyConstraintsQuery: function() { + return false // not supported by dialect + }, + + disableForeignKeyConstraintsQuery: function() { + return false // not supported by dialect + }, + databaseConnectionUri: function(config) { - var template = '<%= protocol %>://<%= user %>:<%= password %>@<%= host %><% if(port) { %>:<%= port %><% } %>/<%= database %>'; + var template = '<%= protocol %>://<%= user %>:<%= password %>@<%= host %><% if(port) { %>:<%= port %><% } %>/<%= database %>' return Utils._.template(template)({ - user: config.username, - password: config.password, + user: encodeURIComponent(config.username), + password: encodeURIComponent(config.password), database: config.database, - host: config.host, - port: config.port, + host: config.host, + port: config.port, protocol: config.protocol }) + }, + + removeQuotes: function (s, quoteChar) { + quoteChar = quoteChar || '"' + return s.replace(new RegExp(quoteChar, 'g'), '') + }, + + addQuotes: function (s, quoteChar) { + quoteChar = quoteChar || '"' + return QueryGenerator.removeQuotes(s, quoteChar) + .split('.') + .map(function(e) { return quoteChar + String(e) + quoteChar }) + .join('.') + }, + + pgEscape: function (val) { + if (val === undefined || val === null) { + return 'NULL'; + } + + switch (typeof val) { + case 'boolean': return (val) ? 'true' : 'false'; + case 'number': return val+''; + case 'object': + if (Array.isArray(val)) { + return 'ARRAY['+ val.map(function(it) { return QueryGenerator.pgEscape(it) }).join(',') +']'; + } + } + + if (val instanceof Date) { + val = QueryGenerator.pgSqlDate(val); + } + + // http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS + val = val.replace(/'/g, "''"); + return "'"+val+"'"; + }, + + pgEscapeAndQuote: function (val) { + return QueryGenerator.addQuotes(QueryGenerator.removeQuotes(QueryGenerator.pgEscape(val), "'")) + }, + + pgEnum: function (tableName, attr, dataType) { + var enumName = QueryGenerator.pgEscapeAndQuote("enum_" + tableName + "_" + attr) + return "DROP TYPE IF EXISTS " + enumName + "; CREATE TYPE " + enumName + " AS " + dataType.match(/^ENUM\(.+\)/)[0] + "; " + }, + + toHstore: function(text) { + var obj = {} + , pattern = '("\\\\.|[^"\\\\]*"\s*=|[^=]*)\s*=\s*>\s*("(?:\\.|[^"\\\\])*"|[^,]*)(?:\s*,\s*|$)' + , rex = new RegExp(pattern,'g') + , r = null + + while ((r = rex.exec(text)) !== null) { + if (!!r[1] && !!r[2]) { + obj[r[1].replace(/^"/, '').replace(/"$/, '')] = r[2].replace(/^"/, '').replace(/"$/, '') + } + } + + return obj + }, + + padInt: function (i) { + return (i < 10) ? '0' + i.toString() : i.toString() + }, + + pgSqlDate: function (dt) { + var date = [ dt.getUTCFullYear(), QueryGenerator.padInt(dt.getUTCMonth()+1), QueryGenerator.padInt(dt.getUTCDate()) ].join('-') + var time = [ dt.getUTCHours(), QueryGenerator.padInt(dt.getUTCMinutes()), QueryGenerator.padInt(dt.getUTCSeconds())].join(':') + return date + ' ' + time + '.' + ((dt.getTime() % 1000) * 1000) + 'Z' + }, + + pgDataTypeMapping: function (tableName, attr, dataType) { + if (Utils._.includes(dataType, 'PRIMARY KEY')) { + primaryKeys[tableName].push(attr) + dataType = dataType.replace(/PRIMARY KEY/, '') + } + + if (Utils._.includes(dataType, 'TINYINT(1)')) { + dataType = dataType.replace(/TINYINT\(1\)/, 'BOOLEAN') + } + + if (Utils._.includes(dataType, 'DATETIME')) { + dataType = dataType.replace(/DATETIME/, 'TIMESTAMP WITH TIME ZONE') + } + + if (Utils._.includes(dataType, 'SERIAL')) { + if (Utils._.includes(dataType, 'BIGINT')) { + dataType = dataType.replace(/SERIAL/, 'BIGSERIAL') + dataType = dataType.replace(/BIGINT/, '') + tables[tableName][attr] = 'bigserial' + } else { + dataType = dataType.replace(/INTEGER/, '') + tables[tableName][attr] = 'serial' + } + dataType = dataType.replace(/NOT NULL/, '') + } + + if (dataType.match(/^ENUM\(/)) { + dataType = dataType.replace(/^ENUM\(.+\)/, QueryGenerator.pgEscapeAndQuote("enum_" + tableName + "_" + attr)) + } + + return dataType } } + // Private + + var removeSerialsFromHash = function(tableName, attrValueHash) { + var returning = []; + Utils._.forEach(attrValueHash, function(value, key, hash) { + if (tables[tableName] && tables[tableName][key]) { + switch (tables[tableName][key]) { + case 'serial': + delete hash[key] + returning.push(key) + break + } + } + }); + return returning; + } + return Utils._.extend(Utils._.clone(require("../query-generator")), QueryGenerator) })() diff --git a/lib/dialects/postgres/query.js b/lib/dialects/postgres/query.js index 279a78c32f46..6dff30fed944 100644 --- a/lib/dialects/postgres/query.js +++ b/lib/dialects/postgres/query.js @@ -1,5 +1,6 @@ var Utils = require("../../utils") , AbstractQuery = require('../abstract/query') + , DataTypes = require('../../data-types') module.exports = (function() { var Query = function(client, sequelize, callee, options) { @@ -19,7 +20,7 @@ module.exports = (function() { Query.prototype.run = function(sql) { this.sql = sql - if(this.options.logging !== false) { + if (this.options.logging !== false) { this.options.logging('Executing: ' + this.sql) } @@ -59,25 +60,77 @@ module.exports = (function() { , isRelNameQuery = (this.sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0) if (isTableNameQuery || isRelNameQuery) { - return this.emit('success', rows.map(function(row) { return Utils._.values(row) })) + if (isRelNameQuery) { + results = rows.map(function(row) { + return { + name: row.relname, + tableName: row.relname.split('_')[0] + } + }) + } else { + results = rows.map(function(row) { return Utils._.values(row) }) + } + return this.emit('success', results) } if (this.send('isSelectQuery')) { - this.emit('success', this.send('handleSelectQuery', rows)) + if (this.sql.toLowerCase().indexOf('select column_name') === 0) { + var result = {} + + rows.forEach(function(_result) { + result[_result.Field] = { + type: _result.Type.toUpperCase(), + allowNull: (_result.Null === 'YES'), + defaultValue: _result.Default + } + + if (result[_result.Field].type === 'BOOLEAN') { + result[_result.Field].defaultValue = { 'false': false, 'true': true }[result[_result.Field].defaultValue] + + if (result[_result.Field].defaultValue === undefined) { + result[_result.Field].defaultValue = null + } + } + + if (typeof result[_result.Field].defaultValue === 'string') { + result[_result.Field].defaultValue = result[_result.Field].defaultValue.replace(/'/g, "") + + if (result[_result.Field].defaultValue.indexOf('::') > -1) { + result[_result.Field].defaultValue = result[_result.Field].defaultValue.split('::')[0] + } + } + }) + + this.emit('success', result) + } else { + this.emit('success', this.send('handleSelectQuery', rows)) + } } else if (this.send('isShowOrDescribeQuery')) { this.emit('success', results) } else if (this.send('isInsertQuery')) { - for (var key in rows[0]) { - if (rows[0].hasOwnProperty(key)) { - this.callee[key] = rows[0][key] + if(this.callee !== null) { // may happen for bulk inserts + for (var key in rows[0]) { + if (rows[0].hasOwnProperty(key)) { + var record = rows[0][key] + if (!!this.callee.daoFactory && !!this.callee.daoFactory.rawAttributes && !!this.callee.daoFactory.rawAttributes[key] && !!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) { + record = this.callee.daoFactory.daoFactoryManager.sequelize.queryInterface.QueryGenerator.toHstore(record) + } + this.callee[key] = record + } } } this.emit('success', this.callee) } else if (this.send('isUpdateQuery')) { - for (var key in rows[0]) { - if (rows[0].hasOwnProperty(key)) { - this.callee[key] = rows[0][key] + if(this.callee !== null) { // may happen for bulk updates + for (var key in rows[0]) { + if (rows[0].hasOwnProperty(key)) { + var record = rows[0][key] + if (!!this.callee.daoFactory && !!this.callee.daoFactory.rawAttributes && !!this.callee.daoFactory.rawAttributes[key] && !!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) { + record = this.callee.daoFactory.daoFactoryManager.sequelize.queryInterface.QueryGenerator.toHstore(record) + } + this.callee[key] = record + } } } diff --git a/lib/dialects/query-generator.js b/lib/dialects/query-generator.js index 45c1e3e53f13..ea5b4a4cf8f4 100644 --- a/lib/dialects/query-generator.js +++ b/lib/dialects/query-generator.js @@ -1,5 +1,9 @@ module.exports = (function() { var QueryGenerator = { + addSchema: function(opts) { + throwMethodUndefined('addSchema') + }, + /* Returns a query for creating a table. Parameters: @@ -114,6 +118,14 @@ module.exports = (function() { throwMethodUndefined('insertQuery') }, + /* + Returns an insert into command for multiple values. + Parameters: table name + list of hashes of attribute-value-pairs. + */ + bulkInsertQuery: function(tableName, attrValueHashes) { + throwMethodUndefined('bulkInsertQuery') + }, + /* Returns an update query. Parameters: @@ -143,6 +155,33 @@ module.exports = (function() { throwMethodUndefined('deleteQuery') }, + /* + Returns a bulk deletion query. + Parameters: + - tableName -> Name of the table + - where -> A hash with conditions (e.g. {name: 'foo'}) + OR an ID as integer + OR a string with conditions (e.g. 'name="foo"'). + If you use a string, you have to escape it on your own. + */ + bulkDeleteQuery: function(tableName, where, options) { + throwMethodUndefined('bulkDeleteQuery') + }, + + /* + Returns an update query. + Parameters: + - tableName -> Name of the table + - values -> A hash with attribute-value-pairs + - where -> A hash with conditions (e.g. {name: 'foo'}) + OR an ID as integer + OR a string with conditions (e.g. 'name="foo"'). + If you use a string, you have to escape it on your own. + */ + incrementQuery: function(tableName, values, where) { + throwMethodUndefined('incrementQuery') + }, + /* Returns an add index query. Parameters: @@ -211,7 +250,22 @@ module.exports = (function() { */ findAutoIncrementField: function(factory) { throwMethodUndefined('findAutoIncrementField') + }, + + /* + Globally enable foreign key constraints + */ + enableForeignKeyConstraintsQuery: function() { + throwMethodUndefined('enableForeignKeyConstraintsQuery') + }, + + /* + Globally disable foreign key constraints + */ + disableForeignKeyConstraintsQuery: function() { + throwMethodUndefined('disableForeignKeyConstraintsQuery') } + } var throwMethodUndefined = function(methodName) { diff --git a/lib/dialects/sqlite/connector-manager.js b/lib/dialects/sqlite/connector-manager.js index 92ce4e67d33d..f3c6b9b238b2 100644 --- a/lib/dialects/sqlite/connector-manager.js +++ b/lib/dialects/sqlite/connector-manager.js @@ -5,7 +5,13 @@ var Utils = require("../../utils") module.exports = (function() { var ConnectorManager = function(sequelize) { this.sequelize = sequelize - this.database = new sqlite3.Database(sequelize.options.storage || ':memory:') + this.database = db = new sqlite3.Database(sequelize.options.storage || ':memory:', function(err) { + if(!err && sequelize.options.foreignKeys !== false) { + // Make it possible to define and use foreign key constraints unless + // explicitly disallowed. It's still opt-in per relation + db.run('PRAGMA FOREIGN_KEYS=ON') + } + }) } Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype) @@ -15,8 +21,3 @@ module.exports = (function() { return ConnectorManager })() - - - - - diff --git a/lib/dialects/sqlite/query-generator.js b/lib/dialects/sqlite/query-generator.js index e84f3187fbfd..7fbcbbe0e4f4 100644 --- a/lib/dialects/sqlite/query-generator.js +++ b/lib/dialects/sqlite/query-generator.js @@ -1,4 +1,6 @@ var Utils = require("../../utils") + , DataTypes = require("../../data-types") + var MySqlQueryGenerator = Utils._.extend( Utils._.clone(require("../query-generator")), Utils._.clone(require("../mysql/query-generator")) @@ -22,6 +24,52 @@ module.exports = (function() { var QueryGenerator = { options: {}, + removeQuotes: function (s, quoteChar) { + quoteChar = quoteChar || '`' + return s.replace(new RegExp(quoteChar, 'g'), '') + }, + + addQuotes: function (s, quoteChar) { + quoteChar = quoteChar || '`' + return QueryGenerator.removeQuotes(s, quoteChar) + .split('.') + .map(function(e) { return quoteChar + String(e) + quoteChar }) + .join('.') + }, + + addSchema: function(opts) { + var tableName = undefined + var schema = (!!opts && !!opts.options && !!opts.options.schema ? opts.options.schema : undefined) + var schemaPrefix = (!!opts && !!opts.options && !!opts.options.schemaPrefix ? opts.options.schemaPrefix : undefined) + + if (!!opts && !!opts.tableName) { + tableName = opts.tableName + } + else if (typeof opts === "string") { + tableName = opts + } + + if (!schema || schema.toString().trim() === "") { + return tableName + } + + return QueryGenerator.addQuotes(schema + (!schemaPrefix ? '.' : schemaPrefix) + tableName) + }, + + createSchema: function() { + var query = "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';" + return Utils._.template(query)({}) + }, + + dropSchema: function() { + var query = "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';" + return Utils._.template(query)({}) + }, + + showSchemasQuery: function() { + return "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';" + }, + createTableQuery: function(tableName, attributes, options) { options = options || {} @@ -37,6 +85,10 @@ module.exports = (function() { if (attributes.hasOwnProperty(attr)) { var dataType = attributes[attr] + if (Utils._.includes(dataType, 'AUTOINCREMENT')) { + dataType = dataType.replace(/BIGINT/, 'INTEGER') + } + if (Utils._.includes(dataType, 'PRIMARY KEY') && needsMultiplePrimaryKeys) { primaryKeys.push(attr) attrStr.push(Utils.addTicks(attr) + " " + dataType.replace(/PRIMARY KEY/, 'NOT NULL')) @@ -57,7 +109,13 @@ module.exports = (function() { values.attributes += ", PRIMARY KEY (" + pkString + ")" } - return Utils._.template(query)(values).trim() + ";" + var sql = Utils._.template(query, values).trim() + ";" + return QueryGenerator.replaceBooleanDefaults(sql) + }, + + addColumnQuery: function() { + var sql = MySqlQueryGenerator.addColumnQuery.apply(null, arguments) + return QueryGenerator.replaceBooleanDefaults(sql) }, showTablesQuery: function() { @@ -71,7 +129,7 @@ module.exports = (function() { var replacements = { table: Utils.addTicks(tableName), - attributes: Utils._.keys(attrValueHash).map(function(attr){return Utils.addTicks(attr)}).join(","), + attributes: Object.keys(attrValueHash).map(function(attr){return Utils.addTicks(attr)}).join(","), values: Utils._.values(attrValueHash).map(function(value){ return escape((value instanceof Date) ? Utils.toSqlDate(value) : value) }).join(",") @@ -80,6 +138,95 @@ module.exports = (function() { return Utils._.template(query)(replacements) }, + bulkInsertQuery: function(tableName, attrValueHashes) { + var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;" + , tuples = [] + + Utils._.forEach(attrValueHashes, function(attrValueHash) { + tuples.push("(" + + Utils._.values(attrValueHash).map(function(value){ + return escape((value instanceof Date) ? Utils.toSqlDate(value) : value) + }).join(",") + + ")") + }) + + var replacements = { + table: Utils.addTicks(tableName), + attributes: Object.keys(attrValueHashes[0]).map(function(attr){return Utils.addTicks(attr)}).join(","), + tuples: tuples + } + + return Utils._.template(query)(replacements) + }, + selectQuery: function(tableName, options) { + var table = null, + joinQuery = "" + + options = options || {} + options.table = table = Array.isArray(tableName) ? tableName.map(function(tbl){ return QueryGenerator.addQuotes(tbl) }).join(", ") : QueryGenerator.addQuotes(tableName) + options.attributes = options.attributes && options.attributes.map(function(attr){ + if(Array.isArray(attr) && attr.length == 2) { + return [attr[0], QueryGenerator.addQuotes(attr[1])].join(' as ') + } else { + return attr.indexOf(Utils.TICK_CHAR) < 0 ? QueryGenerator.addQuotes(attr) : attr + } + }).join(", ") + options.attributes = options.attributes || '*' + + if (options.include) { + var optAttributes = options.attributes === '*' ? [options.table + '.*'] : [options.attributes] + + options.include.forEach(function(include) { + var attributes = Object.keys(include.daoFactory.attributes).map(function(attr) { + return "`" + include.as + "`.`" + attr + "` AS `" + include.as + "." + attr + "`" + }) + + optAttributes = optAttributes.concat(attributes) + + var table = include.daoFactory.tableName + var as = include.as + var tableLeft = ((include.association.associationType === 'BelongsTo') ? include.as : tableName) + var attrLeft = 'id' + var tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as) + var attrRight = include.association.identifier + joinQuery += " LEFT OUTER JOIN `" + table + "` AS `" + as + "` ON `" + tableLeft + "`.`" + attrLeft + "` = `" + tableRight + "`.`" + attrRight + "`" + + }) + + options.attributes = optAttributes.join(', ') + } + + var query = "SELECT " + options.attributes + " FROM " + options.table + query += joinQuery + + if (options.hasOwnProperty('where')) { + options.where = this.getWhereConditions(options.where, tableName) + query += " WHERE " + options.where + } + + if (options.group) { + options.group = Array.isArray(options.group) ? options.group.map(function(grp){return QueryGenerator.addQuotes(grp)}).join(', ') : QueryGenerator.addQuotes(options.group) + query += " GROUP BY " + options.group + } + + if (options.order) { + query += " ORDER BY " + options.order + } + + + if (options.limit && !(options.include && (options.limit === 1))) { + if (options.offset) { + query += " LIMIT " + options.offset + ", " + options.limit + } else { + query += " LIMIT " + options.limit + } + } + + query += ";" + + return query + }, + updateQuery: function(tableName, attrValueHash, where) { attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) @@ -106,8 +253,27 @@ module.exports = (function() { var query = "DELETE FROM <%= table %> WHERE <%= where %>" var replacements = { table: Utils.addTicks(tableName), - where: this.getWhereConditions(where), - limit: Utils.escape(options.limit) + where: MySqlQueryGenerator.getWhereConditions(where) + } + + return Utils._.template(query)(replacements) + }, + + incrementQuery: function(tableName, attrValueHash, where) { + attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) + + var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>" + , values = [] + + for (var key in attrValueHash) { + var value = attrValueHash[key] + values.push(Utils.addTicks(key) + "=" + Utils.addTicks(key) + "+ " + escape((value instanceof Date) ? Utils.toSqlDate(value) : value)) + } + + var replacements = { + table: Utils.addTicks(tableName), + values: values.join(","), + where: MySqlQueryGenerator.getWhereConditions(where) } return Utils._.template(query)(replacements) @@ -119,15 +285,23 @@ module.exports = (function() { for (var name in attributes) { var dataType = attributes[name] - if(Utils.isHash(dataType)) { + if (Utils.isHash(dataType)) { var template = "<%= type %>" , replacements = { type: dataType.type } + if (dataType.type.toString() === DataTypes.ENUM.toString()) { + replacements.type = "TEXT" + + if (!(Array.isArray(dataType.values) && (dataType.values.length > 0))) { + throw new Error('Values for ENUM haven\'t been defined.') + } + } + if (dataType.hasOwnProperty('allowNull') && !dataType.allowNull && !dataType.primaryKey) { template += " NOT NULL" } - if(dataType.defaultValue != undefined) { + if (dataType.defaultValue !== undefined) { template += " DEFAULT <%= defaultValue %>" replacements.defaultValue = Utils.escape(dataType.defaultValue) } @@ -144,6 +318,28 @@ module.exports = (function() { } } + if(dataType.references) { + template += " REFERENCES <%= referencesTable %> (<%= referencesKey %>)" + replacements.referencesTable = Utils.addTicks(dataType.references) + + if(dataType.referencesKey) { + replacements.referencesKey = Utils.addTicks(dataType.referencesKey) + } else { + replacements.referencesKey = Utils.addTicks('id') + } + + if(dataType.onDelete) { + template += " ON DELETE <%= onDeleteAction %>" + replacements.onDeleteAction = dataType.onDelete.toUpperCase() + } + + if(dataType.onUpdate) { + template += " ON UPDATE <%= onUpdateAction %>" + replacements.onUpdateAction = dataType.onUpdate.toUpperCase() + } + + } + result[name] = Utils._.template(template)(replacements) } else { result[name] = dataType @@ -169,6 +365,16 @@ module.exports = (function() { return fields }, + enableForeignKeyConstraintsQuery: function() { + var sql = "PRAGMA foreign_keys = ON;" + return Utils._.template(sql, {}) + }, + + disableForeignKeyConstraintsQuery: function() { + var sql = "PRAGMA foreign_keys = OFF;" + return Utils._.template(sql, {}) + }, + hashToWhereConditions: function(hash) { for (var key in hash) { if (hash.hasOwnProperty(key)) { @@ -183,8 +389,81 @@ module.exports = (function() { } return hashToWhereConditions(hash).replace(/\\'/g, "''"); + }, + + showIndexQuery: function(tableName) { + var sql = "PRAGMA INDEX_LIST('<%= tableName %>')" + return Utils._.template(sql, { tableName: tableName }) + }, + + removeIndexQuery: function(tableName, indexNameOrAttributes) { + var sql = "DROP INDEX <%= indexName %>" + , indexName = indexNameOrAttributes + + if (typeof indexName !== 'string') { + indexName = Utils._.underscored(tableName + '_' + indexNameOrAttributes.join('_')) + } + + return Utils._.template(sql, { tableName: tableName, indexName: indexName }) + }, + + describeTableQuery: function(tableName) { + var sql = "PRAGMA TABLE_INFO('<%= tableName %>');" + return Utils._.template(sql, { tableName: tableName }) + }, + + renameTableQuery: function(before, after) { + var query = "ALTER TABLE `<%= before %>` RENAME TO `<%= after %>`;" + return Utils._.template(query, { before: before, after: after }) + }, + + removeColumnQuery: function(tableName, attributes) { + attributes = QueryGenerator.attributesToSQL(attributes) + + var backupTableName = tableName + "_backup" + var query = [ + QueryGenerator.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'), + "INSERT INTO <%= tableName %>_backup SELECT <%= attributeNames %> FROM <%= tableName %>;", + "DROP TABLE <%= tableName %>;", + QueryGenerator.createTableQuery(tableName, attributes), + "INSERT INTO <%= tableName %> SELECT <%= attributeNames %> FROM <%= tableName %>_backup;", + "DROP TABLE <%= tableName %>_backup;" + ].join("") + + return Utils._.template(query, { + tableName: tableName, + attributeNames: Utils._.keys(attributes).join(', ') + }) + }, + + renameColumnQuery: function(tableName, attrNameBefore, attrNameAfter, attributes) { + attributes = QueryGenerator.attributesToSQL(attributes) + + var backupTableName = tableName + "_backup" + var query = [ + QueryGenerator.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'), + "INSERT INTO <%= tableName %>_backup SELECT <%= attributeNamesImport %> FROM <%= tableName %>;", + "DROP TABLE <%= tableName %>;", + QueryGenerator.createTableQuery(tableName, attributes), + "INSERT INTO <%= tableName %> SELECT <%= attributeNamesExport %> FROM <%= tableName %>_backup;", + "DROP TABLE <%= tableName %>_backup;" + ].join("") + + return Utils._.template(query, { + tableName: tableName, + attributeNamesImport: Utils._.keys(attributes).map(function(attr) { + return (attrNameAfter === attr) ? attrNameBefore + ' AS ' + attr : attr + }).join(', '), + attributeNamesExport: Utils._.keys(attributes).map(function(attr) { + return attr + }).join(', ') + }) + }, + + replaceBooleanDefaults: function(sql) { + return sql.replace(/DEFAULT '?false'?/g, "DEFAULT 0").replace(/DEFAULT '?true'?/g, "DEFAULT 1") } } - return Utils._.extend(MySqlQueryGenerator, QueryGenerator) + return Utils._.extend({}, MySqlQueryGenerator, QueryGenerator) })() diff --git a/lib/dialects/sqlite/query-interface.js b/lib/dialects/sqlite/query-interface.js new file mode 100644 index 000000000000..5b185046c49a --- /dev/null +++ b/lib/dialects/sqlite/query-interface.js @@ -0,0 +1,122 @@ +var Utils = require("../../utils") + +/** + Returns an object that treats SQLite's inabilities to do certain queries. + + @class QueryInterface + @static + */ +var QueryInterface = module.exports = { + /** + 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 + new table with the same name but without the obsolete column. + + @method removeColumn + @for QueryInterface + + @param {String} tableName The name of the table. + @param {String} attributeName The name of the attribute that we want to remove. + @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 + */ + removeColumn: function(tableName, attributeName, emitter, queryAndEmit) { + this.describeTable(tableName).complete(function(err, fields) { + if (err) { + emitter.emit('error', err) + } else { + delete fields[attributeName] + + var sql = this.QueryGenerator.removeColumnQuery(tableName, fields) + , subQueries = sql.split(';').filter(function(q) { return q !== '' }) + + QueryInterface.execMultiQuery.call(this, subQueries, 'removeColumn', emitter, queryAndEmit) + } + }.bind(this)) + }, + + /** + 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 + new table with the same name but with a modified version of the respective column. + + @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 {CustomEventEmitter} emitter The EventEmitter from outside. + @param {Function} queryAndEmit The function from outside that triggers some events to get triggered. + + @since 1.6.0 + */ + changeColumn: function(tableName, attributes, emitter, queryAndEmit) { + var attributeName = Utils._.keys(attributes)[0] + + this.describeTable(tableName).complete(function(err, fields) { + if (err) { + emitter.emit('error', err) + } else { + fields[attributeName] = attributes[attributeName] + + var sql = this.QueryGenerator.removeColumnQuery(tableName, fields) + , subQueries = sql.split(';').filter(function(q) { return q !== '' }) + + QueryInterface.execMultiQuery.call(this, subQueries, 'changeColumn', emitter, queryAndEmit) + } + }.bind(this)) + }, + + /** + 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 + new table with the same name but with a renamed version of the respective column. + + @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} attrNameAfter The name of the attribute after it was renamed. + @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 + */ + renameColumn: function(tableName, attrNameBefore, attrNameAfter, emitter, queryAndEmit) { + this.describeTable(tableName).complete(function(err, fields) { + if (err) { + emitter.emit('error', err) + } else { + fields[attrNameAfter] = Utils._.clone(fields[attrNameBefore]) + delete fields[attrNameBefore] + + var sql = this.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields) + , subQueries = sql.split(';').filter(function(q) { return q !== '' }) + + QueryInterface.execMultiQuery.call(this, subQueries, 'renameColumn', emitter, queryAndEmit) + } + }.bind(this)) + }, + + execMultiQuery: function(queries, methodName, emitter, queryAndEmit) { + var chainer = new Utils.QueryChainer() + + queries.splice(0, queries.length - 1).forEach(function(query) { + chainer.add(this.sequelize, 'query', [query + ";", null, { raw: true }]) + }.bind(this)) + + chainer + .runSerially() + .complete(function(err) { + if (err) { + emitter.emit(methodName, err) + emitter.emit('error', err) + } else { + queryAndEmit.call(this, queries.splice(queries.length - 1)[0], methodName, {}, emitter) + } + }.bind(this)) + } +} diff --git a/lib/dialects/sqlite/query.js b/lib/dialects/sqlite/query.js index 5d608998dad7..f545011e7047 100644 --- a/lib/dialects/sqlite/query.js +++ b/lib/dialects/sqlite/query.js @@ -25,7 +25,7 @@ module.exports = (function() { this.sql = sql - if(this.options.logging !== false) { + if (this.options.logging !== false) { this.options.logging('Executing: ' + this.sql) } @@ -67,7 +67,7 @@ module.exports = (function() { //private var getDatabaseMethod = function() { - if (this.send('isInsertQuery') || this.send('isUpdateQuery')) { + if (this.send('isInsertQuery') || this.send('isUpdateQuery') || (this.sql.toLowerCase().indexOf('CREATE TEMPORARY TABLE'.toLowerCase()) !== -1)) { return 'run' } else { return 'all' @@ -76,14 +76,13 @@ module.exports = (function() { var onSuccess = function(results, metaData) { var result = this.callee - , self = this // add the inserted row id to the instance if (this.send('isInsertQuery', results, metaData)) { this.send('handleInsertQuery', results, metaData) } - if (this.sql.indexOf('sqlite_master') != -1) { + if (this.sql.indexOf('sqlite_master') !== -1) { result = results.map(function(resultSet) { return resultSet.name }) } else if (this.send('isSelectQuery')) { // we need to convert the timestamps into actual date objects @@ -92,7 +91,10 @@ module.exports = (function() { results = results.map(function(result) { for (var name in result) { if (result.hasOwnProperty(name) && (metaData.columnTypes[name] === 'DATETIME')) { - result[name] = new Date(result[name]); + var val = result[name]; + if(val !== null) { + result[name] = new Date(val+'Z'); // Z means UTC + } } } return result @@ -102,6 +104,38 @@ module.exports = (function() { result = this.send('handleSelectQuery', results) } else if (this.send('isShowOrDescribeQuery')) { result = results + } else if (this.sql.indexOf('PRAGMA INDEX_LIST') !== -1) { + // this is the sqlite way of getting the indexes of a table + result = results.map(function(result) { + return { + name: result.name, + tableName: result.name.split('_')[0], + unique: (result.unique === 0) + } + }) + } else if (this.sql.indexOf('PRAGMA TABLE_INFO') !== -1) { + // this is the sqlite way of getting the metadata of a table + result = {} + + results.forEach(function(_result) { + result[_result.name] = { + type: _result.type, + allowNull: (_result.notnull === 0), + defaultValue: _result.dflt_value + } + + if (result[_result.name].type === 'TINYINT(1)') { + result[_result.name].defaultValue = { '0': false, '1': true }[result[_result.name].defaultValue] + } + + if (result[_result.name].defaultValue === undefined) { + result[_result.name].defaultValue = null + } + + if (typeof result[_result.name].defaultValue === 'string') { + result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, "") + } + }) } this.emit('success', result) diff --git a/lib/emitters/custom-event-emitter.js b/lib/emitters/custom-event-emitter.js index dc22722fc83d..4f84646bc1f0 100644 --- a/lib/emitters/custom-event-emitter.js +++ b/lib/emitters/custom-event-emitter.js @@ -1,19 +1,31 @@ -var util = require("util") - , EventEmitter = require("events").EventEmitter +var util = require("util") + , EventEmitter = require("events").EventEmitter + , Promise = require("promise") + , proxyEventKeys = ['success', 'error', 'sql'] + + +var bindToProcess = function(fct) { + if (fct) { + if(process.domain) { + return process.domain.bind(fct); + } + } + + return fct; +}; module.exports = (function() { var CustomEventEmitter = function(fct) { - this.fct = fct + this.fct = bindToProcess(fct); } util.inherits(CustomEventEmitter, EventEmitter) CustomEventEmitter.prototype.run = function() { - var self = this - - // delay the function call and return the emitter - setTimeout(function(){ - self.fct.call(self, self) - }, 1) + process.nextTick(function() { + if (this.fct) { + this.fct.call(this, this) + } + }.bind(this)) return this } @@ -21,7 +33,7 @@ module.exports = (function() { CustomEventEmitter.prototype.success = CustomEventEmitter.prototype.ok = function(fct) { - this.on('success', fct) + this.on('success', bindToProcess(fct)) return this } @@ -29,18 +41,39 @@ module.exports = (function() { CustomEventEmitter.prototype.fail = CustomEventEmitter.prototype.error = function(fct) { - this.on('error', fct) - return this + this.on('error', bindToProcess(fct)) + return this; } CustomEventEmitter.prototype.done = CustomEventEmitter.prototype.complete = function(fct) { + fct = bindToProcess(fct); this.on('error', function(err) { fct(err, null) }) .on('success', function(result) { fct(null, result) }) return this } + CustomEventEmitter.prototype.proxy = function(emitter) { + proxyEventKeys.forEach(function (eventKey) { + this.on(eventKey, function (result) { + emitter.emit(eventKey, result) + }) + }.bind(this)) + } + + CustomEventEmitter.prototype.then = + function (onFulfilled, onRejected) { + var self = this + onFulfilled = bindToProcess(onFulfilled) + onRejected = bindToProcess(onRejected) + return new Promise(function (resolve, reject) { + self.on('error', reject) + .on('success', resolve); + }).then(onFulfilled, onRejected) + } + - return CustomEventEmitter + return CustomEventEmitter; })() + diff --git a/lib/migration.js b/lib/migration.js index 31902690e6c9..8bf7a52f06ff 100644 --- a/lib/migration.js +++ b/lib/migration.js @@ -5,8 +5,6 @@ var moment = require("moment") module.exports = (function() { var Migration = function(migrator, path) { - var split = path.split('/') - this.migrator = migrator this.path = path this.filename = Utils._.last(this.path.split('/')) @@ -16,7 +14,16 @@ module.exports = (function() { this.migrationId = parsed.id this.date = parsed.date; this.queryInterface = this.migrator.queryInterface - this.undoneMethods = 0 + } + + for (var methodName in QueryInterface.prototype) { + if (QueryInterface.prototype.hasOwnProperty(methodName) && (typeof QueryInterface.prototype[methodName]) === 'function') { + (function(methodName) { + Migration.prototype[methodName] = function() { + return this.queryInterface[methodName].apply(this.queryInterface, arguments) + } + })(methodName) + } } /////////////// @@ -26,28 +33,16 @@ module.exports = (function() { Migration.parseFilename = function(s) { var matches = s.match(/^((\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}))[-_].+/) - if(matches === null) { + if (matches === null) { throw new Error(s + ' is not a valid migration name! Use YYYYMMDDHHmmss-migration-name format.') } return { - id: parseInt(matches[1]), + id: parseInt(matches[1], 10), date: moment(matches.slice(2, 8).join('-'), 'YYYYMMDDHHmmss') } } - Migration.migrationHasInterfaceCalls = function(func) { - var functionString = Utils.removeCommentsFromFunctionString(func.toString()) - , hasCalls = false - - for(var method in QueryInterface.prototype) { - var regex = new RegExp('[\\s\\n\\r]*\\.[\\s\\n\\r]*' + method) - hasCalls = hasCalls || regex.test(functionString) - } - - return hasCalls - } - /////////////// // member ///// /////////////// @@ -59,22 +54,19 @@ module.exports = (function() { }) Migration.prototype.execute = function(options) { - var self = this - return new Utils.CustomEventEmitter(function(emitter) { options = Utils._.extend({ method: 'up' }, options || {}) - var onSuccess = function() { emitter.emit('success', null) } - , func = self.migration[options.method] - - extendMigrationWithQueryInterfaceMethods.call(self, onSuccess) - func.call(null, self, DataTypes) - - if(!Migration.migrationHasInterfaceCalls(func)) - onSuccess() - }).run() + this.migration[options.method].call(null, this, DataTypes, function(err) { + if (err) { + emitter.emit('error', err) + } else { + emitter.emit('success', null) + } + }) + }.bind(this)).run() } Migration.prototype.isBefore = function(dateString, options) { @@ -97,35 +89,5 @@ module.exports = (function() { return options.withoutEqual ? (date < this.date) : (date <= this.date) } - // extends the Migration prototype with all methods of QueryInterface.prototype - // with additional tracking of start and finish. this is done in order to minimize - // asynchronous handling in migrations - var extendMigrationWithQueryInterfaceMethods = function(callback) { - var self = this - - for(var method in QueryInterface.prototype) { - (function(_method) { - self[_method] = function() { - var emitter = self.QueryInterface - , args = Utils._.map(arguments, function(arg, _) { return arg }) - - self.undoneMethods++ - - // bind listeners to the query interface - // the event will have the same name like the method - self.queryInterface.on(_method, function(err) { - self.undoneMethods-- - if(err) - throw new Error(err) - else - (self.undoneMethods == 0) && callback && callback() - }) - - return self.queryInterface[_method].apply(self.queryInterface, args) - } - })(method) - } - } - return Migration })() diff --git a/lib/migrator.js b/lib/migrator.js index a454f3c9fe7e..5d0c7a69fc4e 100644 --- a/lib/migrator.js +++ b/lib/migrator.js @@ -1,5 +1,4 @@ const fs = require("fs") - , path = require("path") , moment = require("moment") var Utils = require("./utils") @@ -13,15 +12,16 @@ module.exports = (function() { path: __dirname + '/../migrations', from: null, to: null, - logging: console.log + logging: console.log, + filesFilter: /\.js$/ }, options || {}) - if(this.options.logging === true) { + if (this.options.logging === true) { console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log') this.options.logging = console.log } - if(this.options.logging == console.log) { + if (this.options.logging == console.log) { // using just console.log will break in node < 0.6 this.options.logging = function(s) { console.log(s) } } @@ -42,32 +42,45 @@ module.exports = (function() { return new Utils.CustomEventEmitter(function(emitter) { self.getUndoneMigrations(function(err, migrations) { - if(err) { + if (err) { emitter.emit('error', err) } else { var chainer = new Utils.QueryChainer , from = migrations[0] - if(options.method == 'down') { - migrations.reverse() + if (options.method === 'down') { from = migrations[0] + migrations.reverse() } + if (migrations.length === 0) { + self.options.logging("There are no pending migrations.") + } else { + self.options.logging("Running migrations...") + } migrations.forEach(function(migration) { + var migrationTime chainer.add(migration, 'execute', [options], { before: function(migration) { - if(self.options.logging !== false) - self.options.logging('Executing migration: ' + migration.filename) + if (self.options.logging !== false) { + self.options.logging(migration.filename) + } + migrationTime = process.hrtime() }, after: function(migration) { - if(self.options.logging !== false) - self.options.logging('Executed migration: ' + migration.filename) + migrationTime = process.hrtime(migrationTime) + migrationTime = Math.round( (migrationTime[0] * 1000) + (migrationTime[1] / 1000000)); + + if (self.options.logging !== false) { + self.options.logging('Completed in ' + migrationTime + 'ms') + } }, success: function(migration, callback) { - if(options.method == 'down') + if (options.method === 'down') { deleteUndoneMigration.call(self, from, migration, callback) - else + } else { saveSuccessfulMigration.call(self, from, migration, callback) + } } }) }) @@ -93,7 +106,9 @@ module.exports = (function() { callback && callback(null, result) } - var migrationFiles = fs.readdirSync(this.options.path) + var migrationFiles = fs.readdirSync(this.options.path).filter(function(file) { + return self.options.filesFilter.test(file) + }) var migrations = migrationFiles.map(function(file) { return new Migration(self, self.options.path + '/' + file) @@ -103,27 +118,30 @@ module.exports = (function() { return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0]) }) - if(this.options.from) { + if (this.options.from) { filterFrom(migrations, this.options.from, function(err, migrations) { - if(self.options.to) + if (self.options.to) { filterTo(migrations, self.options.to, callback) - else + } else { callback && callback(null, migrations) + } }) } else { getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) { - if(lastMigrationId) { + if (lastMigrationId) { filterFrom(migrations, lastMigrationId, function(err, migrations) { - if(self.options.to) + if (self.options.to) { filterTo(migrations, self.options.to, callback) - else + } else { callback && callback(null, migrations) + } }, { withoutEqual: true }) } else { - if(self.options.to) + if (self.options.to) { filterTo(migrations, self.options.to, callback) - else + } else { callback && callback(null, migrations) + } } }).error(function(err) { callback && callback(err, null) @@ -138,7 +156,7 @@ module.exports = (function() { var storedDAO = self.sequelize.daoFactoryManager.getDAO('SequelizeMeta') , SequelizeMeta = storedDAO - if(!storedDAO) { + if (!storedDAO) { SequelizeMeta = self.sequelize.define('SequelizeMeta', { from: DataTypes.STRING, to: DataTypes.STRING @@ -148,7 +166,7 @@ module.exports = (function() { } // force sync when model has newly created or if syncOptions are passed - if(!storedDAO || syncOptions) { + if (!storedDAO || syncOptions) { SequelizeMeta .sync(syncOptions || {}) .success(function() { emitter.emit('success', SequelizeMeta) }) @@ -218,7 +236,7 @@ module.exports = (function() { self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) { SequelizeMeta - .find({ from: from.migrationId, to: to.migrationId }) + .find({ where: { from: from.migrationId.toString(), to: to.migrationId.toString() } }) .success(function(meta) { meta.destroy().success(callback) }) diff --git a/lib/query-chainer.js b/lib/query-chainer.js index 58f288ba275a..6dabbfca69ad 100644 --- a/lib/query-chainer.js +++ b/lib/query-chainer.js @@ -16,7 +16,7 @@ module.exports = (function() { emitters = emitters || [] emitters.forEach(function(emitter) { - if(Array.isArray(emitter)) { + if (Array.isArray(emitter)) { self.add.apply(self, emitter) } else { self.add(emitter) @@ -25,7 +25,7 @@ module.exports = (function() { } QueryChainer.prototype.add = function(emitterOrKlass, method, params, options) { - if(!!method) { + if (!!method) { this.serials.push({ klass: emitterOrKlass, method: method, params: params, options: options }) } else { observeEmitter.call(this, emitterOrKlass) @@ -55,7 +55,7 @@ module.exports = (function() { var exec = function() { var serial = self.serials.pop() - if(serial) { + if (serial) { serial.options = serial.options || {} serial.options.before && serial.options.before(serial.klass) @@ -72,7 +72,7 @@ module.exports = (function() { exec() } - if(options.skipOnError && (self.fails.length > 0)) { + if (options.skipOnError && (self.fails.length > 0)) { onError('Skipped due to earlier error!') } else { var emitter = serial.klass[serial.method].apply(serial.klass, serial.params) @@ -80,7 +80,7 @@ module.exports = (function() { emitter.success(function(result) { self.serialResults[serialCopy.indexOf(serial)] = result - if(serial.options.success) { + if (serial.options.success) { serial.options.success(serial.klass, onSuccess) } else { onSuccess() @@ -115,7 +115,7 @@ module.exports = (function() { finish.call(self, 'emitterResults') }) .on('sql', function(sql) { - if(self.eventEmitter) { + if (self.eventEmitter) { self.eventEmitter.emit('sql', sql) } }) @@ -124,13 +124,14 @@ module.exports = (function() { var finish = function(resultsName) { this.finished = true - if(this.emitters.length > 0) { + if (this.emitters.length > 0) { this.finished = (this.finishedEmits == this.emitters.length) - } else if(this.serials.length > 0) { + } + else if (this.serials.length > 0) { this.finished = (this.finishedEmits == this.serials.length) } - if(this.finished && this.wasRunning) { + if (this.finished && this.wasRunning) { var status = (this.fails.length == 0 ? 'success' : 'error') , result = (this.fails.length == 0 ? this[resultsName] : this.fails) diff --git a/lib/query-interface.js b/lib/query-interface.js index deaca01ad6b4..8a8287c5efeb 100644 --- a/lib/query-interface.js +++ b/lib/query-interface.js @@ -1,22 +1,76 @@ -var Utils = require('./utils') - , DataTypes = require('./data-types') +var Utils = require('./utils') + , DataTypes = require('./data-types') + , SQLiteQueryInterface = require('./dialects/sqlite/query-interface') module.exports = (function() { var QueryInterface = function(sequelize) { - this.sequelize = sequelize - this.QueryGenerator = require('./dialects/' + this.sequelize.options.dialect + '/query-generator') - this.QueryGenerator.options = this.sequelize.options; + this.sequelize = sequelize + this.QueryGenerator = require('./dialects/' + this.sequelize.options.dialect + '/query-generator') + this.QueryGenerator.options = this.sequelize.options } Utils.addEventEmitter(QueryInterface) + QueryInterface.prototype.createSchema = function(schema) { + var sql = this.QueryGenerator.createSchema(schema) + return queryAndEmit.call(this, sql, 'createSchema') + } + + QueryInterface.prototype.dropSchema = function(schema) { + var sql = this.QueryGenerator.dropSchema(schema) + return queryAndEmit.call(this, sql, 'dropSchema') + } + + QueryInterface.prototype.dropAllSchemas = function() { + var self = this + + return new Utils.CustomEventEmitter(function(emitter) { + var chainer = new Utils.QueryChainer() + + self.showAllSchemas().success(function(schemaNames) { + schemaNames.forEach(function(schemaName) { + chainer.add(self.dropSchema(schemaName)) + }) + chainer + .run() + .success(function() { + self.emit('dropAllSchemas', null) + emitter.emit('success', null) + }) + .error(function(err) { + self.emit('dropAllSchemas', err) + emitter.emit('error', err) + }) + }).error(function(err) { + self.emit('dropAllSchemas', err) + emitter.emit('error', err) + }) + }).run() + } + + QueryInterface.prototype.showAllSchemas = function() { + var self = this + + return new Utils.CustomEventEmitter(function(emitter) { + var showSchemasSql = self.QueryGenerator.showSchemasQuery() + self.sequelize.query(showSchemasSql, null, { raw: true }).success(function(schemaNames) { + self.emit('showAllSchemas', null) + emitter.emit('success', Utils._.flatten(Utils._.map(schemaNames, function(value){ return value.schema_name }))) + }).error(function(err) { + self.emit('showAllSchemas', err) + emitter.emit('error', err) + }) + }).run() + } + QueryInterface.prototype.createTable = function(tableName, attributes, options) { var attributeHashes = {} Utils._.each(attributes, function(dataTypeOrOptions, attributeName) { - if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) - attributeHashes[attributeName] = { type: dataTypeOrOptions } - else + if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) { + attributeHashes[attributeName] = { type: dataTypeOrOptions, allowNull: true } + } else { attributeHashes[attributeName] = dataTypeOrOptions + } }) attributes = this.QueryGenerator.attributesToSQL(attributeHashes) @@ -25,8 +79,8 @@ module.exports = (function() { return queryAndEmit.call(this, sql, 'createTable') } - QueryInterface.prototype.dropTable = function(tableName) { - var sql = this.QueryGenerator.dropTableQuery(tableName) + QueryInterface.prototype.dropTable = function(tableName, options) { + var sql = this.QueryGenerator.dropTableQuery(tableName, options) return queryAndEmit.call(this, sql, 'dropTable') } @@ -37,11 +91,17 @@ module.exports = (function() { var chainer = new Utils.QueryChainer() self.showAllTables().success(function(tableNames) { + + chainer.add(self, 'disableForeignKeyConstraints', []) + tableNames.forEach(function(tableName) { - chainer.add(self.dropTable(tableName)) + chainer.add(self, 'dropTable', [tableName, {cascade: true}]) }) + + chainer.add(self, 'enableForeignKeyConstraints', []) + chainer - .run() + .runSerially() .success(function() { self.emit('dropAllTables', null) emitter.emit('success', null) @@ -82,11 +142,13 @@ module.exports = (function() { return new Utils.CustomEventEmitter(function(emitter) { var sql; + if (self.QueryGenerator.describeTableQuery) { sql = self.QueryGenerator.describeTableQuery(tableName) } else { sql = 'DESCRIBE `' + tableName + '`;' } + self.sequelize.query(sql, null, { raw: true }).success(function(data) { emitter.emit('success', data) }).error(function(err) { @@ -98,10 +160,11 @@ module.exports = (function() { QueryInterface.prototype.addColumn = function(tableName, attributeName, dataTypeOrOptions) { var attributes = {} - if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) - attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false } - else + if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) { + attributes[attributeName] = { type: dataTypeOrOptions, allowNull: true } + } else { attributes[attributeName] = dataTypeOrOptions + } var options = this.QueryGenerator.attributesToSQL(attributes) , sql = this.QueryGenerator.addColumnQuery(tableName, options) @@ -110,56 +173,69 @@ module.exports = (function() { } QueryInterface.prototype.removeColumn = function(tableName, attributeName) { - var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName) - return queryAndEmit.call(this, sql, 'removeColumn') + if (this.sequelize.options.dialect === 'sqlite') { + // sqlite needs some special treatment as it cannot drop a column + return new Utils.CustomEventEmitter(function(emitter) { + SQLiteQueryInterface.removeColumn.call(this, tableName, attributeName, emitter, queryAndEmit) + }.bind(this)).run() + } else { + var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName) + return queryAndEmit.call(this, sql, 'removeColumn') + } } QueryInterface.prototype.changeColumn = function(tableName, attributeName, dataTypeOrOptions) { var attributes = {} - if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) - attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false } - else + if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) { + attributes[attributeName] = { type: dataTypeOrOptions, allowNull: true } + } else { attributes[attributeName] = dataTypeOrOptions + } - var options = this.QueryGenerator.attributesToSQL(attributes) - , sql = this.QueryGenerator.changeColumnQuery(tableName, options) + if (this.sequelize.options.dialect === 'sqlite') { + // sqlite needs some special treatment as it cannot change a column + return new Utils.CustomEventEmitter(function(emitter) { + SQLiteQueryInterface.changeColumn.call(this, tableName, attributes, emitter, queryAndEmit) + }.bind(this)).run() + } else { + var options = this.QueryGenerator.attributesToSQL(attributes) + , sql = this.QueryGenerator.changeColumnQuery(tableName, options) - return queryAndEmit.call(this, sql, 'changeColumn') + return queryAndEmit.call(this, sql, 'changeColumn') + } } QueryInterface.prototype.renameColumn = function(tableName, attrNameBefore, attrNameAfter) { - var self = this - return new Utils.CustomEventEmitter(function(emitter) { - self.describeTable(tableName).success(function(data) { - data = data.filter(function(h) { return h.Field == attrNameBefore })[0] + this.describeTable(tableName).success(function(data) { + data = data[attrNameBefore] || {} var options = {} options[attrNameAfter] = { - type: data.Type, - allowNull: data.Null == 'YES', - defaultValue: data.Default + attribute: attrNameAfter, + type: data.type, + allowNull: data.allowNull, + defaultValue: data.defaultValue } - var sql = self.QueryGenerator.renameColumnQuery(tableName, - attrNameBefore, - self.QueryGenerator.attributesToSQL(options) - ) - - self.sequelize.query(sql, null, {}).success(function() { - self.emit('renameColumn', null) - emitter.emit('success', null) - }).error(function(err) { - self.emit('renameColumn', err) - emitter.emit('error', err) - }) - }).error(function(err) { - self.emit('renameColumn', err) + if (this.sequelize.options.dialect === 'sqlite') { + // sqlite needs some special treatment as it cannot rename a column + SQLiteQueryInterface.renameColumn.call(this, tableName, attrNameBefore, attrNameAfter, emitter, queryAndEmit) + } else { + var sql = this.QueryGenerator.renameColumnQuery(tableName, + attrNameBefore, + this.QueryGenerator.attributesToSQL(options) + ) + queryAndEmit.call(this, sql, 'renameColumn', {}, emitter) + } + }.bind(this)) + .error(function(err) { + this.emit('renameColumn', err) emitter.emit('error', err) - }) - }).run() + }.bind(this)) + }.bind(this)).run() } QueryInterface.prototype.addIndex = function(tableName, attributes, options) { @@ -184,25 +260,48 @@ module.exports = (function() { }) } + QueryInterface.prototype.bulkInsert = function(tableName, records) { + var sql = this.QueryGenerator.bulkInsertQuery(tableName, records) + return queryAndEmit.call(this, sql, 'bulkInsert') + } + QueryInterface.prototype.update = function(dao, tableName, values, identifier) { var sql = this.QueryGenerator.updateQuery(tableName, values, identifier) return queryAndEmit.call(this, [sql, dao], 'update') } + QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier) { + var sql = this.QueryGenerator.updateQuery(tableName, values, identifier) + return queryAndEmit.call(this, sql, 'bulkUpdate') + } + QueryInterface.prototype.delete = function(dao, tableName, identifier) { var sql = this.QueryGenerator.deleteQuery(tableName, identifier) return queryAndEmit.call(this, [sql, dao], 'delete') } + QueryInterface.prototype.bulkDelete = function(tableName, identifier) { + var sql = this.QueryGenerator.deleteQuery(tableName, identifier, {limit: null}) + return queryAndEmit.call(this, sql, 'bulkDelete') + } + QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) { + options = options || {} + var sql = this.QueryGenerator.selectQuery(tableName, options) + queryOptions = Utils._.extend({}, queryOptions, { include: options.include }) return queryAndEmit.call(this, [sql, factory, queryOptions], 'select') } + QueryInterface.prototype.increment = function(dao, tableName, values, identifier) { + var sql = this.QueryGenerator.incrementQuery(tableName, values, identifier); + return queryAndEmit.call(this, [sql, dao], 'increment'); + } + QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelector) { var self = this - if(attributeSelector == undefined) { + if (attributeSelector === undefined) { throw new Error('Please pass an attribute selector!') } @@ -218,6 +317,10 @@ module.exports = (function() { result = parseInt(result) } + if (options && options.parseFloat) { + result = parseFloat(result) + } + self.emit('rawSelect', null) emitter.emit('success', result) }) @@ -231,20 +334,42 @@ module.exports = (function() { }).run() } - // private + QueryInterface.prototype.enableForeignKeyConstraints = function() { + var sql = this.QueryGenerator.enableForeignKeyConstraintsQuery() + if(sql) { + return queryAndEmit.call(this, sql, 'enableForeignKeyConstraints') + } else { + return new Utils.CustomEventEmitter(function(emitter) { + this.emit('enableForeignKeyConstraints', null) + emitter.emit('success') + }).run() + } + } - var queryAndEmit = function(sqlOrQueryParams, methodName, options) { - var self = this + QueryInterface.prototype.disableForeignKeyConstraints = function() { + var sql = this.QueryGenerator.disableForeignKeyConstraintsQuery() + if(sql){ + return queryAndEmit.call(this, sql, 'disableForeignKeyConstraints') + } else { + return new Utils.CustomEventEmitter(function(emitter) { + this.emit('disableForeignKeyConstraints', null) + emitter.emit('success') + }).run() + } + } + // private + + var queryAndEmit = function(sqlOrQueryParams, methodName, options, emitter) { options = Utils._.extend({ success: function(){}, error: function(){} }, options || {}) - return new Utils.CustomEventEmitter(function(emitter) { + var execQuery = function(emitter) { var query = null - if(Array.isArray(sqlOrQueryParams)) { + if (Array.isArray(sqlOrQueryParams)) { if (sqlOrQueryParams.length === 1) { sqlOrQueryParams.push(null) } @@ -253,9 +378,9 @@ module.exports = (function() { sqlOrQueryParams.push({}) } - query = self.sequelize.query.apply(self.sequelize, sqlOrQueryParams) + query = this.sequelize.query.apply(this.sequelize, sqlOrQueryParams) } else { - query = self.sequelize.query(sqlOrQueryParams, null, {}) + query = this.sequelize.query(sqlOrQueryParams, null, {}) } // append the query for better testing @@ -263,17 +388,24 @@ module.exports = (function() { query.success(function(obj) { options.success && options.success(obj) - self.emit(methodName, null) + this.emit(methodName, null) emitter.emit('success', obj) - }).error(function(err) { + }.bind(this)).error(function(err) { options.error && options.error(err) - self.emit(methodName, err) + this.emit(methodName, err) emitter.emit('error', err) - }) + }.bind(this)) + query.on('sql', function(sql) { emitter.emit('sql', sql) - }); - }).run() + }) + }.bind(this) + + if (!!emitter) { + execQuery(emitter) + } else { + return new Utils.CustomEventEmitter(execQuery).run() + } } return QueryInterface diff --git a/lib/sequelize.js b/lib/sequelize.js index 5fa1d4e7d0cb..f21d80bda6f2 100644 --- a/lib/sequelize.js +++ b/lib/sequelize.js @@ -1,32 +1,70 @@ -var Utils = require("./utils") +var url = require("url") + , Utils = require("./utils") , DAOFactory = require("./dao-factory") , DataTypes = require('./data-types') , DAOFactoryManager = require("./dao-factory-manager") - , Migrator = require("./migrator") , QueryInterface = require("./query-interface") -if(parseFloat(process.version.replace('v', '')) < 0.6) { - console.log("DEPRECATION WARNING: Support for Node.JS < v0.6 will be canceled in the next minor release.") -} - module.exports = (function() { /** - Main constructor of the project. - - Params: - - - `database` - - `username` - - `password`, optional, default: null - - `options`, optional, default: {} - - Examples: - - mymodule.write('foo') - mymodule.write('foo', { stream: process.stderr }) - + Main class of the project. + + @param {String} database The name of the database. + @param {String} username The username which is used to authenticate against the database. + @param {String} [password=null] The password which is used to authenticate against the database. + @param {Object} [options={}] An object with options. + @param {String} [options.dialect='mysql'] The dialect of the relational database. + @param {String} [options.host='localhost'] The host of the relational database. + @param {Integer} [options.port=3306] The port of the relational database. + @param {String} [options.protocol='tcp'] The protocol of the relational database. + @param {Object} [options.define={}] Options, which shall be default for every model definition. + @param {Object} [options.query={}] I have absolutely no idea. + @param {Object} [options.sync={}] Options, which shall be default for every `sync` call. + @param {Function} [options.logging=console.log] A function that gets executed everytime Sequelize would log something. + @param {Boolean} [options.omitNull=false] A flag that defines if null values should be passed to SQL queries or not. + @param {Boolean} [options.queue=true] I have absolutely no idea. + @param {Boolean} [options.native=false] A flag that defines if native library shall be used or not. + @param {Boolean} [options.replication=false] I have absolutely no idea. + @param {Object} [options.pool={}] Something. + + @example + // without password and options + var sequelize = new Sequelize('database', 'username') + + // without options + var sequelize = new Sequelize('database', 'username', 'password') + + // without password / with blank password + var sequelize = new Sequelize('database', 'username', null, {}) + + // with password and options + var sequelize = new Sequelize('my_database', 'john', 'doe', {}) + + @class Sequelize + @constructor */ var Sequelize = function(database, username, password, options) { + var urlParts + options = options || {} + + if (arguments.length === 1 || (arguments.length === 2 && typeof username === 'object')) { + options = username || {} + urlParts = url.parse(arguments[0]) + database = urlParts.path.replace(/^\//, '') + dialect = urlParts.protocol + options.dialect = urlParts.protocol.replace(/:$/, '') + options.host = urlParts.hostname + + if (urlParts.port) { + options.port = urlParts.port + } + + if (urlParts.auth) { + username = urlParts.auth.split(':')[0] + password = urlParts.auth.split(':')[1] + } + } + this.options = Utils._.extend({ dialect: 'mysql', host: 'localhost', @@ -43,7 +81,7 @@ module.exports = (function() { pool: {} }, options || {}) - if(this.options.logging === true) { + if (this.options.logging === true) { console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log') this.options.logging = console.log } @@ -79,13 +117,29 @@ module.exports = (function() { Sequelize[dataType] = DataTypes[dataType] } + /** + Returns an instance of QueryInterface. + + @method getQueryInterface + @return {QueryInterface} An instance (singleton) of QueryInterface. + */ Sequelize.prototype.getQueryInterface = function() { this.queryInterface = this.queryInterface || new QueryInterface(this) return this.queryInterface } + /** + Returns an instance (singleton) of Migrator. + + @method getMigrator + @param {Object} [options={}] Some options + @param {Boolean} [force=false] A flag that defines if the migrator should get instantiated or not. + @return {Migrator} An instance of Migrator. + */ Sequelize.prototype.getMigrator = function(options, force) { - if(force) { + var Migrator = require("./migrator") + + if (force) { this.migrator = new Migrator(this, options) } else { this.migrator = this.migrator || new Migrator(this, options) @@ -98,10 +152,20 @@ module.exports = (function() { options = options || {} var globalOptions = this.options - if(globalOptions.define) { + // If you don't specify a valid data type lets help you debug it + Utils._.each(attributes, function(dataType, name){ + if (Utils.isHash(dataType)) { + dataType = dataType.type + } + if (dataType === undefined) { + throw new Error('Unrecognized data type for field '+ name) + } + }) + + if (globalOptions.define) { options = Utils._.extend({}, globalOptions.define, options) Utils._(['classMethods', 'instanceMethods']).each(function(key) { - if(globalOptions.define[key]) { + if (globalOptions.define[key]) { options[key] = options[key] || {} Utils._.extend(options[key], globalOptions.define[key]) } @@ -109,6 +173,11 @@ module.exports = (function() { } options.omitNull = globalOptions.omitNull + // if you call "define" multiple times for the same daoName, do not clutter the factory + if(this.isDefined(daoName)) { + this.daoFactoryManager.removeDAO(this.daoFactoryManager.getDAO(daoName)) + } + var factory = new DAOFactory(daoName, attributes, options) this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager)) return factory @@ -132,8 +201,10 @@ module.exports = (function() { this.getMigrator().migrate(options) } - Sequelize.prototype.query = function(sql, callee, options) { - if (arguments.length === 3) { + Sequelize.prototype.query = function(sql, callee, options, replacements) { + if (arguments.length === 4) { + sql = Utils.format([sql].concat(replacements), this.options.dialect) + } else if (arguments.length === 3) { options = options } else if (arguments.length === 2) { options = {} @@ -150,20 +221,55 @@ module.exports = (function() { return this.connectorManager.query(sql, callee, options) } + Sequelize.prototype.createSchema = function(schema) { + var chainer = new Utils.QueryChainer() + + chainer.add(this.getQueryInterface().createSchema(schema)) + + return chainer.run() + } + + Sequelize.prototype.showAllSchemas = function() { + var chainer = new Utils.QueryChainer() + + chainer.add(this.getQueryInterface().showAllSchemas()) + + return chainer.run() + } + + Sequelize.prototype.dropSchema = function(schema) { + var chainer = new Utils.QueryChainer() + + chainer.add(this.getQueryInterface().dropSchema(schema)) + + return chainer.run() + } + + Sequelize.prototype.dropAllSchemas = function() { + var self = this + + var chainer = new Utils.QueryChainer() + chainer.add(self.getQueryInterface().dropAllSchemas()) + return chainer.run() + } + Sequelize.prototype.sync = function(options) { options = options || {} - if(this.options.sync) { + if (this.options.sync) { options = Utils._.extend({}, this.options.sync, options) } var chainer = new Utils.QueryChainer() - this.daoFactoryManager.daos.forEach(function(dao) { - chainer.add(dao.sync(options)) + // Topologically sort by foreign key constraints to give us an appropriate + // creation order + + this.daoFactoryManager.forEachDAO(function(dao) { + chainer.add(dao, 'sync', [options]) }) - return chainer.run() + return chainer.runSerially() } Sequelize.prototype.drop = function() { diff --git a/lib/sql-string.js b/lib/sql-string.js new file mode 100644 index 000000000000..96be2c7e6ae4 --- /dev/null +++ b/lib/sql-string.js @@ -0,0 +1,141 @@ +var SqlString = exports; + +SqlString.escapeId = function (val, forbidQualified) { + if (forbidQualified) { + return '`' + val.replace(/`/g, '``') + '`'; + } + return '`' + val.replace(/`/g, '``').replace(/\./g, '`.`') + '`'; +}; + +SqlString.escape = function(val, stringifyObjects, timeZone, dialect) { + if (val === undefined || val === null) { + return 'NULL'; + } + + switch (typeof val) { + case 'boolean': return (val) ? 'true' : 'false'; + case 'number': return val+''; + } + + if (val instanceof Date) { + val = SqlString.dateToString(val, timeZone || "Z"); + } + + if (Buffer.isBuffer(val)) { + return SqlString.bufferToString(val); + } + + if (Array.isArray(val)) { + return SqlString.arrayToList(val, timeZone); + } + + if (typeof val === 'object') { + if (stringifyObjects) { + val = val.toString(); + } else { + return SqlString.objectToValues(val, timeZone); + } + } + + if (dialect == "postgres") { + // http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS + val = val.replace(/'/g, "''"); + } else { + val = val.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) { + switch(s) { + case "\0": return "\\0"; + case "\n": return "\\n"; + case "\r": return "\\r"; + case "\b": return "\\b"; + case "\t": return "\\t"; + case "\x1a": return "\\Z"; + default: return "\\"+s; + } + }); + } + return "'"+val+"'"; +}; + +SqlString.arrayToList = function(array, timeZone) { + return array.map(function(v) { + if (Array.isArray(v)) return '(' + SqlString.arrayToList(v) + ')'; + return SqlString.escape(v, true, timeZone); + }).join(', '); +}; + +SqlString.format = function(sql, values, timeZone, dialect) { + values = [].concat(values); + + return sql.replace(/\?/g, function(match) { + if (!values.length) { + return match; + } + + return SqlString.escape(values.shift(), false, timeZone, dialect); + }); +}; + +SqlString.dateToString = function(date, timeZone) { + var dt = new Date(date); + + if (timeZone != 'local') { + var tz = convertTimezone(timeZone); + + dt.setTime(dt.getTime() + (dt.getTimezoneOffset() * 60000)); + if (tz !== false) { + dt.setTime(dt.getTime() + (tz * 60000)); + } + } + + var year = dt.getFullYear(); + var month = zeroPad(dt.getMonth() + 1); + var day = zeroPad(dt.getDate()); + var hour = zeroPad(dt.getHours()); + var minute = zeroPad(dt.getMinutes()); + var second = zeroPad(dt.getSeconds()); + + return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second; +}; + +SqlString.bufferToString = function(buffer) { + var hex = ''; + try { + hex = buffer.toString('hex'); + } catch (err) { + // node v0.4.x does not support hex / throws unknown encoding error + for (var i = 0; i < buffer.length; i++) { + var byte = buffer[i]; + hex += zeroPad(byte.toString(16)); + } + } + + return "X'" + hex+ "'"; +}; + +SqlString.objectToValues = function(object, timeZone) { + var values = []; + for (var key in object) { + var value = object[key]; + if(typeof value === 'function') { + continue; + } + + values.push(this.escapeId(key) + ' = ' + SqlString.escape(value, true, timeZone)); + } + + return values.join(', '); +}; + +function zeroPad(number) { + return (number < 10) ? '0' + number : number; +} + +function convertTimezone(tz) { + if (tz == "Z") return 0; + + var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/); + if (m) { + return (m[1] == '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60; + } + return false; +} diff --git a/lib/utils.js b/lib/utils.js index 96c32519b194..73adbd260233 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,11 +1,10 @@ -var mysql = require("mysql") - , connection = mysql.createConnection({}) - , util = require("util") +var util = require("util") , DataTypes = require("./data-types") + , SqlString = require("./sql-string") var Utils = module.exports = { _: (function() { - var _ = require("underscore") + var _ = require("lodash") , _s = require('underscore.string') _.mixin(_s.exports()) @@ -14,7 +13,7 @@ var Utils = module.exports = { camelizeIf: function(string, condition) { var result = string - if(condition) { + if (condition) { result = _.camelize(string) } @@ -23,7 +22,7 @@ var Utils = module.exports = { underscoredIf: function(string, condition) { var result = string - if(condition) { + if (condition) { result = _.underscored(string) } @@ -37,44 +36,45 @@ var Utils = module.exports = { util.inherits(_class, require('events').EventEmitter) }, TICK_CHAR: '`', - addTicks: function(s) { - return Utils.TICK_CHAR + Utils.removeTicks(s) + Utils.TICK_CHAR + addTicks: function(s, tickChar) { + tickChar = tickChar || Utils.TICK_CHAR + return tickChar + Utils.removeTicks(s, tickChar) + tickChar }, - removeTicks: function(s) { - return s.replace(new RegExp(Utils.TICK_CHAR, 'g'), "") + removeTicks: function(s, tickChar) { + tickChar = tickChar || Utils.TICK_CHAR + return s.replace(new RegExp(tickChar, 'g'), "") }, escape: function(s) { - return connection.escape(s).replace(/\\"/g, '"') + return SqlString.escape(s, true, "local").replace(/\\"/g, '"') }, - format: function(arr) { - var query = arr[0] - , replacements = Utils._.compact(arr.map(function(obj) { return obj != query ? obj : null})) - - return connection.format.apply(connection, [query, replacements]) + format: function(arr, dialect) { + var timeZone = null; + return SqlString.format(arr.shift(), arr, timeZone, dialect) }, isHash: function(obj) { - return Utils._.isObject(obj) && !Utils._.isArray(obj); + return Utils._.isObject(obj) && !Array.isArray(obj); + }, + pad: function (s) { + return s < 10 ? '0' + s : s }, toSqlDate: function(date) { - return [ - [ - date.getFullYear(), - ((date.getMonth() < 9 ? '0' : '') + (date.getMonth()+1)), - ((date.getDate() < 10 ? '0' : '') + date.getDate()) - ].join("-"), - date.toLocaleTimeString() - ].join(" ") + return date.getUTCFullYear() + '-' + + this.pad(date.getUTCMonth()+1) + '-' + + this.pad(date.getUTCDate()) + ' ' + + this.pad(date.getUTCHours()) + ':' + + this.pad(date.getUTCMinutes()) + ':' + + this.pad(date.getUTCSeconds()) }, argsArePrimaryKeys: function(args, primaryKeys) { - var result = (args.length == Utils._.keys(primaryKeys).length) + var result = (args.length == Object.keys(primaryKeys).length) if (result) { Utils._.each(args, function(arg) { - if(result) { - if(['number', 'string'].indexOf(typeof arg) !== -1) + if (result) { + if (['number', 'string'].indexOf(typeof arg) !== -1) { result = true - else + } else { result = (arg instanceof Date) - + } } }) } @@ -100,7 +100,7 @@ var Utils = module.exports = { }, toDefaultValue: function(value) { - return (value == DataTypes.NOW) ? new Date() : value + return (value === DataTypes.NOW) ? Utils.now() : value }, setAttributes: function(hash, identifier, instance, prefix) { @@ -119,7 +119,7 @@ var Utils = module.exports = { removeNullValuesFromHash: function(hash, omitNull) { var result = hash - if(omitNull) { + if (omitNull) { var _hash = {} Utils._.each(hash, function(val, key) { @@ -152,6 +152,14 @@ var Utils = module.exports = { } }, + firstValueOfHash: function(obj) { + for (var key in obj) { + if (obj.hasOwnProperty(key)) + return obj[key] + } + return null + }, + inherit: function(subClass, superClass) { if (superClass.constructor == Function) { // Normal Inheritance @@ -166,6 +174,12 @@ var Utils = module.exports = { } return subClass; + }, + + now: function() { + var now = new Date() + now.setMilliseconds(0) + return now } } diff --git a/package.json b/package.json index 5f74b16e2b02..3ef34bc77cd0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sequelize", "description": "Multi dialect ORM for Node.JS", - "version": "1.6.0-beta4", + "version": "1.7.0-alpha1", "author": "Sascha Depold ", "contributors": [ { @@ -21,22 +21,33 @@ "email": "jam@innofluence.com" } ], + "repository": { + "type": "git", + "url": "https://github.com/sequelize/sequelize.git" + }, + "bugs": { + "url": "https://github.com/sequelize/sequelize/issues" + }, "dependencies": { - "mysql": "~2.0.0-alpha3", - "underscore": "~1.4.0", + "lodash": "~1.2.1", "underscore.string": "~2.3.0", "lingo": "~0.0.5", - "validator": "0.3.x", + "validator": "1.1.1", "moment": "~1.7.0", "commander": "~0.6.0", - "generic-pool": "1.0.9" + "dottie": "0.0.6-1", + "toposort-class": "0.1.4", + "generic-pool": "2.0.3", + "promise": "~3.0.0" }, "devDependencies": { - "jasmine-node": "1.0.17", + "jasmine-node": "1.5.0", "sqlite3": "~2.1.5", - "pg": "~0.8.7", - "buster": "~0.6.0", - "dox-foundation": "~0.3.0" + "mysql": "~2.0.0-alpha7", + "pg": "~0.10.2", + "buster": "~0.6.3", + "watchr": "~2.2.0", + "yuidocjs": "~0.3.36" }, "keywords": [ "mysql", @@ -47,14 +58,14 @@ "main": "index", "scripts": { "test": "npm run test-jasmine && npm run test-buster", - "test-jasmine": "./node_modules/.bin/jasmine-node spec-jasmine/", + "test-jasmine": "jasmine-node spec-jasmine/", "test-buster": "npm run test-buster-mysql && npm run test-buster-postgres && npm run test-buster-postgres-native && npm run test-buster-sqlite", - "test-buster-travis": "./node_modules/.bin/buster-test", - "test-buster-mysql": "DIALECT=mysql ./node_modules/.bin/buster-test", - "test-buster-postgres": "DIALECT=postgres ./node_modules/.bin/buster-test", - "test-buster-postgres-native": "DIALECT=postgres-native ./node_modules/.bin/buster-test", - "test-buster-sqlite": "DIALECT=sqlite ./node_modules/.bin/buster-test", - "generate-docs": "node_modules/.bin/dox-foundation --source ./lib --title Sequelize" + "test-buster-travis": "buster-test", + "test-buster-mysql": "DIALECT=mysql buster-test", + "test-buster-postgres": "DIALECT=postgres buster-test", + "test-buster-postgres-native": "DIALECT=postgres-native buster-test", + "test-buster-sqlite": "DIALECT=sqlite buster-test", + "docs": "node_modules/.bin/yuidoc . -o docs" }, "bin": { "sequelize": "bin/sequelize" diff --git a/spec-jasmine/associations/belongs-to.spec.js b/spec-jasmine/associations/belongs-to.spec.js index 39fa2ed27b53..80522a09d544 100644 --- a/spec-jasmine/associations/belongs-to.spec.js +++ b/spec-jasmine/associations/belongs-to.spec.js @@ -1,6 +1,6 @@ var config = require("../config/config") , Sequelize = require("../../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { logging: false }) + , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) , Helpers = new (require("../config/helpers"))(sequelize) describe('BelongsTo', function() { diff --git a/spec-jasmine/associations/has-many.spec.js b/spec-jasmine/associations/has-many.spec.js index 673b03325019..86a2cbd3361d 100644 --- a/spec-jasmine/associations/has-many.spec.js +++ b/spec-jasmine/associations/has-many.spec.js @@ -1,6 +1,6 @@ var config = require("../config/config") , Sequelize = require("../../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { logging: false }) + , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) , Helpers = new (require("../config/helpers"))(sequelize) describe('HasMany', function() { @@ -10,7 +10,7 @@ describe('HasMany', function() { , Helpers = null var setup = function() { - sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { logging: false }) + sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) Helpers = new (require("../config/helpers"))(sequelize) Helpers.dropAllTables() diff --git a/spec-jasmine/associations/has-one.spec.js b/spec-jasmine/associations/has-one.spec.js index f02507048a7d..7d0eca2cdc4c 100644 --- a/spec-jasmine/associations/has-one.spec.js +++ b/spec-jasmine/associations/has-one.spec.js @@ -1,6 +1,6 @@ var config = require("../config/config") , Sequelize = require("../../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { logging: false }) + , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) , Helpers = new (require("../config/helpers"))(sequelize) describe('HasOne', function() { diff --git a/spec-jasmine/dao-factory.spec.js b/spec-jasmine/dao-factory.spec.js index 71ec088cb4c2..27342e9e1ee5 100644 --- a/spec-jasmine/dao-factory.spec.js +++ b/spec-jasmine/dao-factory.spec.js @@ -90,25 +90,6 @@ describe('DAOFactory', function() { }) }) - it('marks the database entry as deleted if dao is paranoid', function() { - Helpers.async(function(done) { - User = sequelize.define('User', { - name: Sequelize.STRING, bio: Sequelize.TEXT - }, { paranoid:true }) - User.sync({ force: true }).success(done) - }) - - Helpers.async(function(done) { - User.create({ name: 'asd', bio: 'asd' }).success(function(u) { - expect(u.deletedAt).toBeNull() - u.destroy().success(function(u) { - expect(u.deletedAt).toBeTruthy() - done() - }) - }) - }) - }) - it('allows sql logging of update statements', function() { Helpers.async(function(done) { User = sequelize.define('User', { diff --git a/spec-jasmine/dao.spec.js b/spec-jasmine/dao.spec.js index 704165accbc8..418a93682e08 100644 --- a/spec-jasmine/dao.spec.js +++ b/spec-jasmine/dao.spec.js @@ -13,7 +13,8 @@ describe('DAO', function() { { logging: false, dialect: dialect, - port: config[dialect].port + port: config[dialect].port, + host: config[dialect].host } ) , Helpers = new (require("./config/helpers"))(sequelize) @@ -181,7 +182,7 @@ describe('DAO', function() { expect(users.length).toEqual(1) expect(users[0].username).toEqual(username) expect(users[0].birthDate instanceof Date).toBe(true) - expect(users[0].birthDate.getTime()).toEqual(new Date(1984, 8, 23).getTime()) + expect(users[0].birthDate).toEqual(new Date(1984, 8, 23)) done() }) }) @@ -199,7 +200,7 @@ describe('DAO', function() { updatedAt = user.updatedAt expect(updatedAt.getTime()).toBeGreaterThan(now) done() - }, 10) + }, 1000) }) Helpers.async(function(done) { @@ -208,7 +209,7 @@ describe('DAO', function() { expect(updatedAt.getTime()).toBeLessThan(user.updatedAt.getTime()) done() }) - }, 10) + }, 1000) }) }) @@ -334,7 +335,6 @@ describe('DAO', function() { }) }) }) - }) }) }) diff --git a/spec-jasmine/migration.spec.js b/spec-jasmine/migration.spec.js deleted file mode 100644 index dfe659070b0e..000000000000 --- a/spec-jasmine/migration.spec.js +++ /dev/null @@ -1,68 +0,0 @@ -var config = require("./config/config") - , Sequelize = require("../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { logging: false }) - , Helpers = new (require("./config/helpers"))(sequelize) - , Migrator = require("../lib/migrator") - , Migration = require("../lib/migration") - , _ = Sequelize.Utils._ - -describe('Migration', function() { - describe('migrationHasInterfaceCalls', function() { - // the syntax in the following tests are correct - // don't touch them! the functions will get stringified below - var tests = [ - { - topic: function(migration, DataTypes) { - migration.createTable() - }, - expectation: true - }, - { - topic: function(migration, DataTypes) { - // migration.createTable() - }, - expectation: false - }, - { - topic: function(migration, DataTypes) { - migration - .createTable() - }, - expectation: true - }, - { - topic: function(migration, DataTypes) { - migration. - createTable() - }, - expectation: true - }, - { - topic: function(migration, DataTypes) { - migration . createTable () - }, - expectation: true - }, - { - topic: function(migration, DataTypes) { - /* - migration . createTable() - */ - }, - expectation: false - }, - { - topic: function(migration, DataTypes) { - migration/* noot noot */.createTable() - }, - expectation: true - } - ] - - tests.forEach(function(test) { - it('correctly result in ' + test.expectation + ' for ' + test.topic.toString(), function() { - expect(Migration.migrationHasInterfaceCalls(test.topic)).toEqual(test.expectation) - }) - }) - }) -}) diff --git a/spec-jasmine/migrator.spec.js b/spec-jasmine/migrator.spec.js deleted file mode 100644 index 62a2a16aa9c3..000000000000 --- a/spec-jasmine/migrator.spec.js +++ /dev/null @@ -1,313 +0,0 @@ -var config = require("./config/config") - , Sequelize = require("../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { logging: false }) - , Helpers = new (require("./config/helpers"))(sequelize) - , Migrator = require("../lib/migrator") - , _ = Sequelize.Utils._ - -describe('Migrator', function() { - var migrator = null - , SequelizeMeta = null - - var setup = function(_options) { - Helpers.async(function(done) { - var options = Sequelize.Utils._.extend({ - path: __dirname + '/assets/migrations', - logging: false - }, _options || {}) - - migrator = new Migrator(sequelize, options) - migrator - .findOrCreateSequelizeMetaDAO({ force: true }) - .success(function(_SequelizeMeta) { - SequelizeMeta = _SequelizeMeta - done() - }) - .error(function(err) { console.log(err) }) - }) - } - - var reset = function() { - migrator = null - Helpers.dropAllTables() - } - - beforeEach(reset) - afterEach(reset) - - describe('getUndoneMigrations', function() { - it("returns no files if timestamps are after the files timestamp", function() { - setup({ from: 20120101010101 }) - - Helpers.async(function(done) { - migrator.getUndoneMigrations(function(err, migrations) { - expect(err).toBeNull() - expect(migrations.length).toEqual(0) - done() - }) - }) - }) - - it("returns only files between from and to", function() { - setup({ from: 19700101000000, to: 20111117063700 }) - - Helpers.async(function(done) { - migrator.getUndoneMigrations(function(err, migrations) { - expect(err).toBeNull() - expect(migrations.length).toEqual(1) - expect(_.last(migrations).filename).toEqual('20111117063700-createPerson.js') - done() - }) - }) - }) - - it("returns also the file which is exactly options.from or options.to", function() { - setup({ from: 20111117063700, to: 20111130161100 }) - - Helpers.async(function(done) { - migrator.getUndoneMigrations(function(err, migrations) { - expect(err).toBeNull() - expect(migrations.length).toEqual(2) - expect(migrations[0].filename).toEqual('20111117063700-createPerson.js') - expect(migrations[1].filename).toEqual('20111130161100-emptyMigration.js') - done() - }) - }) - }) - - it("returns all files to options.to if no options.from is defined", function() { - setup({ to: 20111130161100 }) - - Helpers.async(function(done) { - migrator.getUndoneMigrations(function(err, migrations) { - expect(err).toBeNull() - expect(migrations.length).toEqual(2) - done() - }) - }) - }) - - it("returns all files from last migration id stored in database", function() { - setup() - - Helpers.async(function(done) { - SequelizeMeta.create({ from: null, to: 20111117063700 }).success(function() { - migrator.getUndoneMigrations(function(err, migrations) { - expect(err).toBeNull() - expect(migrations.length).toEqual(6) - expect(migrations[0].filename).toEqual('20111130161100-emptyMigration.js') - done() - }) - }) - }) - }) - }) - - describe('migrations', function() { - beforeEach(function() { - setup({ from: 20111117063700, to: 20111117063700 }) - - Helpers.async(function(done) { - migrator.migrate().success(done).error(function(err) { console.log(err) }) - }) - }) - - describe('executions', function() { - it("executes migration #20111117063700 and correctly creates the table", function() { - Helpers.async(function(done) { - sequelize.getQueryInterface().showAllTables().success(function(tableNames) { - tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) - expect(tableNames.length).toEqual(1) - expect(tableNames[0]).toEqual('Person') - done() - }) - }) - }) - - it("executes migration #20111117063700 and correctly adds isBetaMember", function() { - Helpers.async(function(done) { - sequelize.getQueryInterface().describeTable('Person').success(function(data) { - var beta = data.filter(function(d) { return d.Field == 'isBetaMember'}) - expect(beta).toBeDefined() - done() - }) - }) - }) - - it("executes migration #20111117063700 correctly up (createTable) and downwards (dropTable)", function() { - Helpers.async(function(done) { - sequelize.getQueryInterface().showAllTables().success(function(tableNames) { - tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) - expect(tableNames.length).toEqual(1) - done() - }) - }) - - Helpers.async(function(done) { - migrator.migrate({ method: 'down' }).success(function() { - sequelize.getQueryInterface().showAllTables().success(function(tableNames) { - tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) - expect(tableNames.length).toEqual(0) - done() - }).error(function(err){ console.log(err); done() }) - }).error(function(err){ console.log(err); done() }) - }) - }) - - it("executes the empty migration #20111130161100", function() { - Helpers.async(function(done) { - setup({ from: 20111130161100, to: 20111130161100}) - done() - }) - - Helpers.async(function(done) { - migrator.migrate().success(done).error(function(err) { console.log(err) }) - // this migration isn't actually testing anything but - // should not timeout - }) - }) - }) - - describe('renameTable', function() { - it("executes migration #20111205064000 and renames a table", function() { - Helpers.async(function(done) { - sequelize.getQueryInterface().showAllTables().success(function(tableNames) { - tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) - expect(tableNames.length).toEqual(1) - expect(tableNames[0]).toEqual('Person') - done() - }) - }) - - setup({from: 20111205064000, to: 20111205064000}) - - Helpers.async(function(done) { - migrator.migrate().success(done).error(function(err) { console.log(err) }) - }) - - Helpers.async(function(done) { - sequelize.getQueryInterface().showAllTables().success(function(tableNames) { - tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) - expect(tableNames.length).toEqual(1) - expect(tableNames[0]).toEqual('User') - done() - }) - }) - }) - }) - - describe('addColumn', function() { - it('adds a column to the user table', function() { - setup({from: 20111205064000, to: 20111205162700}) - - Helpers.async(function(done) { - migrator.migrate().success(done).error(function(err) { console.log(err) }) - }) - - Helpers.async(function(done) { - sequelize.getQueryInterface().describeTable('User').success(function(data) { - var signature = data.filter(function(hash){ return hash.Field == 'signature' })[0] - , isAdmin = data.filter(function(hash){ return hash.Field == 'isAdmin' })[0] - , shopId = data.filter(function(hash){ return hash.Field == 'shopId' })[0] - - expect(signature.Field).toEqual('signature') - expect(signature.Null).toEqual('NO') - - expect(isAdmin.Field).toEqual('isAdmin') - expect(isAdmin.Null).toEqual('NO') - expect(isAdmin.Default).toEqual('0') - - expect(shopId.Field).toEqual('shopId') - expect(shopId.Null).toEqual('YES') - - done() - }).error(function(err) { - console.log(err) - }) - }) - }) - }) - - describe('removeColumn', function() { - it('removes the shopId column from user', function() { - setup({from: 20111205064000, to: 20111206061400}) - - Helpers.async(function(done) { - migrator.migrate().success(done).error(function(err) { console.log(err) }) - }) - - Helpers.async(function(done) { - sequelize.getQueryInterface().describeTable('User').success(function(data) { - var signature = data.filter(function(hash){ return hash.Field == 'signature' })[0] - , isAdmin = data.filter(function(hash){ return hash.Field == 'isAdmin' })[0] - , shopId = data.filter(function(hash){ return hash.Field == 'shopId' })[0] - - expect(signature.Field).toEqual('signature') - expect(signature.Null).toEqual('NO') - - expect(isAdmin.Field).toEqual('isAdmin') - expect(isAdmin.Null).toEqual('NO') - expect(isAdmin.Default).toEqual('0') - - expect(shopId).toBeFalsy() - - done() - }).error(function(err) { - console.log(err) - }) - }) - - }) - }) - - describe('changeColumn', function() { - it('changes the signature column from user to default "signature" + notNull', function() { - setup({from: 20111205064000, to: 20111206063000}) - - Helpers.async(function(done) { - migrator.migrate().success(done).error(function(err) { console.log(err) }) - }) - - Helpers.async(function(done) { - sequelize.getQueryInterface().describeTable('User').success(function(data) { - var signature = data.filter(function(hash){ return hash.Field == 'signature' })[0] - - expect(signature.Field).toEqual('signature') - expect(signature.Type).toEqual('varchar(255)') - expect(signature.Null).toEqual('NO') - expect(signature.Default).toEqual('Signature') - - done() - }).error(function(err) { - console.log(err) - }) - }) - }) - }) - }) - - describe('renameColumn', function() { - it("renames the signature column from user to sig", function() { - setup({from: 20111117063700, to: 20111206163300}) - - Helpers.async(function(done) { - migrator.migrate().success(done).error(function(err) { console.log(err) }) - }) - - Helpers.async(function(done) { - sequelize.getQueryInterface().describeTable('User').success(function(data) { - var signature = data.filter(function(hash){ return hash.Field == 'signature' })[0] - , sig = data.filter(function(hash){ return hash.Field == 'sig' })[0] - - expect(signature).toBeFalsy() - expect(sig).toBeTruthy() - - done() - }).error(function(err) { - console.log(err) - }) - }) - }) - }) -}) - diff --git a/spec-jasmine/mysql/associations.has-many.spec.js b/spec-jasmine/mysql/associations.has-many.spec.js index f5c229df2f11..d8fda75bcea1 100644 --- a/spec-jasmine/mysql/associations.has-many.spec.js +++ b/spec-jasmine/mysql/associations.has-many.spec.js @@ -1,6 +1,6 @@ var config = require("../config/config") , Sequelize = require("../../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false }) + , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) , Helpers = new (require("../config/helpers"))(sequelize) describe('HasMany', function() { diff --git a/spec-jasmine/mysql/associations.spec.js b/spec-jasmine/mysql/associations.spec.js index cd76eb342395..21bad8354731 100644 --- a/spec-jasmine/mysql/associations.spec.js +++ b/spec-jasmine/mysql/associations.spec.js @@ -1,6 +1,6 @@ var config = require("../config/config") , Sequelize = require("../../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false }) + , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) , Helpers = new (require("../config/helpers"))(sequelize) describe('Associations', function() { diff --git a/spec-jasmine/mysql/connector-manager.spec.js b/spec-jasmine/mysql/connector-manager.spec.js index bb52e96065d0..fd6c97e15f36 100644 --- a/spec-jasmine/mysql/connector-manager.spec.js +++ b/spec-jasmine/mysql/connector-manager.spec.js @@ -1,6 +1,6 @@ var config = require("../config/config") , Sequelize = require("../../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false }) + , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) , Helpers = new (require("../config/helpers"))(sequelize) describe('ConnectorManager', function() { diff --git a/spec-jasmine/mysql/dao-factory.spec.js b/spec-jasmine/mysql/dao-factory.spec.js index c49721505c13..d171d160829f 100644 --- a/spec-jasmine/mysql/dao-factory.spec.js +++ b/spec-jasmine/mysql/dao-factory.spec.js @@ -1,6 +1,6 @@ var config = require("../config/config") , Sequelize = require("../../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false }) + , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) , Helpers = new (require("../config/helpers"))(sequelize) describe('DAOFactory', function() { diff --git a/spec-jasmine/mysql/query-generator.spec.js b/spec-jasmine/mysql/query-generator.spec.js index 32164dfadbb8..c6d6b462e129 100644 --- a/spec-jasmine/mysql/query-generator.spec.js +++ b/spec-jasmine/mysql/query-generator.spec.js @@ -1,6 +1,6 @@ var config = require("../config/config") , Sequelize = require("../../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false }) + , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) , Helpers = new (require("../config/helpers"))(sequelize) , QueryGenerator = require("../../lib/dialects/mysql/query-generator") , util = require("util") @@ -10,6 +10,62 @@ describe('QueryGenerator', function() { afterEach(function() { Helpers.drop() }) var suites = { + + attributesToSQL: [ + { + arguments: [{id: 'INTEGER'}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: 'INTEGER', foo: 'VARCHAR(255)'}], + expectation: {id: 'INTEGER', foo: 'VARCHAR(255)'} + }, + { + arguments: [{id: {type: 'INTEGER'}}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: false}}], + expectation: {id: 'INTEGER NOT NULL'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: true}}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: {type: 'INTEGER', primaryKey: true, autoIncrement: true}}], + expectation: {id: 'INTEGER auto_increment PRIMARY KEY'} + }, + { + arguments: [{id: {type: 'INTEGER', defaultValue: 0}}], + expectation: {id: 'INTEGER DEFAULT 0'} + }, + { + arguments: [{id: {type: 'INTEGER', unique: true}}], + expectation: {id: 'INTEGER UNIQUE'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar'}}], + expectation: {id: 'INTEGER REFERENCES `Bar` (`id`)'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', referencesKey: 'pk'}}], + expectation: {id: 'INTEGER REFERENCES `Bar` (`pk`)'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', onDelete: 'CASCADE'}}], + expectation: {id: 'INTEGER REFERENCES `Bar` (`id`) ON DELETE CASCADE'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', onUpdate: 'RESTRICT'}}], + expectation: {id: 'INTEGER REFERENCES `Bar` (`id`) ON UPDATE RESTRICT'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: false, autoIncrement: true, defaultValue: 1, references: 'Bar', onDelete: 'CASCADE', onUpdate: 'RESTRICT'}}], + expectation: {id: 'INTEGER NOT NULL auto_increment DEFAULT 1 REFERENCES `Bar` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT'} + }, + ], + createTableQuery: [ { arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}], @@ -22,6 +78,18 @@ describe('QueryGenerator', function() { { arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {charset: 'latin1'}], expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255)) ENGINE=InnoDB DEFAULT CHARSET=latin1;" + }, + { + arguments: ['myTable', {title: 'ENUM("A", "B", "C")', name: 'VARCHAR(255)'}, {charset: 'latin1'}], + expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` ENUM(\"A\", \"B\", \"C\"), `name` VARCHAR(255)) ENGINE=InnoDB DEFAULT CHARSET=latin1;" + }, + { + arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)', id: 'INTEGER PRIMARY KEY'}], + expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255), `id` INTEGER , PRIMARY KEY (`id`)) ENGINE=InnoDB;" + }, + { + arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)', otherId: 'INTEGER REFERENCES `otherTable` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION'}], + expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255), `otherId` INTEGER, FOREIGN KEY (`otherId`) REFERENCES `otherTable` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE=InnoDB;" } ], @@ -73,6 +141,14 @@ describe('QueryGenerator', function() { arguments: ['myTable', {group: "name"}], expectation: "SELECT * FROM `myTable` GROUP BY `name`;", context: QueryGenerator + }, { + arguments: ['myTable', {group: ["name"]}], + expectation: "SELECT * FROM `myTable` GROUP BY `name`;", + context: QueryGenerator + }, { + arguments: ['myTable', {group: ["name", "title"]}], + expectation: "SELECT * FROM `myTable` GROUP BY `name`, `title`;", + context: QueryGenerator }, { arguments: ['myTable', {group: "name", order: "id DESC"}], expectation: "SELECT * FROM `myTable` GROUP BY `name` ORDER BY id DESC;", @@ -90,6 +166,26 @@ describe('QueryGenerator', function() { arguments: ['myTable', {offset: 2}], expectation: "SELECT * FROM `myTable`;", context: QueryGenerator + }, { + title: 'multiple where arguments', + arguments: ['myTable', {where: {boat: 'canoe', weather: 'cold'}}], + expectation: "SELECT * FROM `myTable` WHERE `myTable`.`boat`='canoe' AND `myTable`.`weather`='cold';", + context: QueryGenerator + }, { + title: 'no where arguments (object)', + arguments: ['myTable', {where: {}}], + expectation: "SELECT * FROM `myTable` WHERE 1=1;", + context: QueryGenerator + }, { + title: 'no where arguments (string)', + arguments: ['myTable', {where: ''}], + expectation: "SELECT * FROM `myTable` WHERE 1=1;", + context: QueryGenerator + }, { + title: 'no where arguments (null)', + arguments: ['myTable', {where: null}], + expectation: "SELECT * FROM `myTable` WHERE 1=1;", + context: QueryGenerator } ], @@ -101,7 +197,7 @@ describe('QueryGenerator', function() { arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}], expectation: "INSERT INTO `myTable` (`name`) VALUES ('foo\\';DROP TABLE myTable;');" }, { - arguments: ['myTable', {name: 'foo', birthday: new Date(2011, 2, 27, 10, 1, 55)}], + arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}], expectation: "INSERT INTO `myTable` (`name`,`birthday`) VALUES ('foo','2011-03-27 10:01:55');" }, { arguments: ['myTable', {name: 'foo', foo: 1}], @@ -121,15 +217,55 @@ describe('QueryGenerator', function() { arguments: ['myTable', {name: 'foo', foo: 1, nullValue: undefined}], expectation: "INSERT INTO `myTable` (`name`,`foo`) VALUES ('foo',1);", context: {options: {omitNull: true}} + }, { + arguments: ['myTable', {foo: false}], + expectation: "INSERT INTO `myTable` (`foo`) VALUES (0);" + }, { + arguments: ['myTable', {foo: true}], + expectation: "INSERT INTO `myTable` (`foo`) VALUES (1);" + } + ], + + bulkInsertQuery: [ + { + arguments: ['myTable', [{name: 'foo'}, {name: 'bar'}]], + expectation: "INSERT INTO `myTable` (`name`) VALUES ('foo'),('bar');" + }, { + arguments: ['myTable', [{name: "foo';DROP TABLE myTable;"}, {name: 'bar'}]], + expectation: "INSERT INTO `myTable` (`name`) VALUES ('foo\\';DROP TABLE myTable;'),('bar');" + }, { + arguments: ['myTable', [{name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {name: 'bar', birthday: new Date(Date.UTC(2012, 2, 27, 10, 1, 55))}]], + expectation: "INSERT INTO `myTable` (`name`,`birthday`) VALUES ('foo','2011-03-27 10:01:55'),('bar','2012-03-27 10:01:55');" + }, { + arguments: ['myTable', [{name: 'foo', foo: 1}, {name: 'bar', foo: 2}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`) VALUES ('foo',1),('bar',2);" + }, { + arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', nullValue: null}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',NULL);" + }, { + arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', foo: 2, nullValue: null}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);", + context: {options: {omitNull: false}} + }, { + arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', foo: 2, nullValue: null}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);", + context: {options: {omitNull: true}} // Note: We don't honour this because it makes little sense when some rows may have nulls and others not + }, { + arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: undefined}, {name: 'bar', foo: 2, undefinedValue: undefined}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);", + context: {options: {omitNull: true}} // Note: As above + }, { + arguments: ['myTable', [{name: "foo", value: true}, {name: 'bar', value: false}]], + expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('foo',1),('bar',0);" } ], updateQuery: [ { - arguments: ['myTable', {name: 'foo', birthday: new Date(2011, 2, 27, 10, 1, 55)}, {id: 2}], + arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], expectation: "UPDATE `myTable` SET `name`='foo',`birthday`='2011-03-27 10:01:55' WHERE `id`=2" }, { - arguments: ['myTable', {name: 'foo', birthday: new Date(2011, 2, 27, 10, 1, 55)}, 2], + arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, 2], expectation: "UPDATE `myTable` SET `name`='foo',`birthday`='2011-03-27 10:01:55' WHERE `id`=2" }, { arguments: ['myTable', {bar: 2}, {name: 'foo'}], @@ -148,6 +284,12 @@ describe('QueryGenerator', function() { arguments: ['myTable', {bar: 2, nullValue: null}, {name: 'foo'}], expectation: "UPDATE `myTable` SET `bar`=2 WHERE `name`='foo'", context: {options: {omitNull: true}} + }, { + arguments: ['myTable', {bar: false}, {name: 'foo'}], + expectation: "UPDATE `myTable` SET `bar`=0 WHERE `name`='foo'" + }, { + arguments: ['myTable', {bar: true}, {name: 'foo'}], + expectation: "UPDATE `myTable` SET `bar`=1 WHERE `name`='foo'" } ], @@ -164,6 +306,9 @@ describe('QueryGenerator', function() { }, { arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}, {limit: 10}], expectation: "DELETE FROM `myTable` WHERE `name`='foo\\';DROP TABLE myTable;' LIMIT 10" + }, { + arguments: ['myTable', {name: 'foo'}, {limit: null}], + expectation: "DELETE FROM `myTable` WHERE `name`='foo'" } ], @@ -215,6 +360,27 @@ describe('QueryGenerator', function() { { arguments: [{ id: [] }], expectation: "`id` IN (NULL)" + }, + { + arguments: [{ maple: false, bacon: true }], + expectation: "`maple`=0 AND `bacon`=1" + }, + { + arguments: [{ beaver: [false, true] }], + expectation: "`beaver` IN (0,1)" + }, + { + arguments: [{birthday: new Date(Date.UTC(2011, 6, 1, 10, 1, 55))}], + expectation: "`birthday`='2011-07-01 10:01:55'" + }, + { + arguments: [{ birthday: new Date(Date.UTC(2011, 6, 1, 10, 1, 55)), + otherday: new Date(Date.UTC(2013, 6, 2, 10, 1, 22)) }], + expectation: "`birthday`='2011-07-01 10:01:55' AND `otherday`='2013-07-02 10:01:22'" + }, + { + arguments: [{ birthday: [new Date(Date.UTC(2011, 6, 1, 10, 1, 55)), new Date(Date.UTC(2013, 6, 2, 10, 1, 22))] }], + expectation: "`birthday` IN ('2011-07-01 10:01:55','2013-07-02 10:01:22')" } ] } @@ -222,7 +388,7 @@ describe('QueryGenerator', function() { Sequelize.Utils._.each(suites, function(tests, suiteTitle) { describe(suiteTitle, function() { tests.forEach(function(test) { - var title = test.title || 'correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) + var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) it(title, function() { // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly var context = test.context || {options: {}}; diff --git a/spec-jasmine/postgres/query-generator.spec.js b/spec-jasmine/postgres/query-generator.spec.js index 80b5217d059f..f16844ee3bb7 100644 --- a/spec-jasmine/postgres/query-generator.spec.js +++ b/spec-jasmine/postgres/query-generator.spec.js @@ -14,6 +14,62 @@ describe('QueryGenerator', function() { afterEach(function() { Helpers.drop() }) var suites = { + + attributesToSQL: [ + { + arguments: [{id: 'INTEGER'}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: 'INTEGER', foo: 'VARCHAR(255)'}], + expectation: {id: 'INTEGER', foo: 'VARCHAR(255)'} + }, + { + arguments: [{id: {type: 'INTEGER'}}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: false}}], + expectation: {id: 'INTEGER NOT NULL'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: true}}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: {type: 'INTEGER', primaryKey: true, autoIncrement: true}}], + expectation: {id: 'INTEGER SERIAL PRIMARY KEY'} + }, + { + arguments: [{id: {type: 'INTEGER', defaultValue: 0}}], + expectation: {id: 'INTEGER DEFAULT 0'} + }, + { + arguments: [{id: {type: 'INTEGER', unique: true}}], + expectation: {id: 'INTEGER UNIQUE'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar'}}], + expectation: {id: 'INTEGER REFERENCES "Bar" ("id")'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', referencesKey: 'pk'}}], + expectation: {id: 'INTEGER REFERENCES "Bar" ("pk")'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', onDelete: 'CASCADE'}}], + expectation: {id: 'INTEGER REFERENCES "Bar" ("id") ON DELETE CASCADE'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', onUpdate: 'RESTRICT'}}], + expectation: {id: 'INTEGER REFERENCES "Bar" ("id") ON UPDATE RESTRICT'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: false, defaultValue: 1, references: 'Bar', onDelete: 'CASCADE', onUpdate: 'RESTRICT'}}], + expectation: {id: 'INTEGER NOT NULL DEFAULT 1 REFERENCES "Bar" ("id") ON DELETE CASCADE ON UPDATE RESTRICT'} + }, + ], + createTableQuery: [ { arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}], @@ -23,7 +79,18 @@ describe('QueryGenerator', function() { arguments: ['mySchema.myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}], expectation: "CREATE TABLE IF NOT EXISTS \"mySchema\".\"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255));" }, - + { + arguments: ['myTable', {title: 'ENUM("A", "B", "C")', name: 'VARCHAR(255)'}], + expectation: "DROP TYPE IF EXISTS \"enum_myTable_title\"; CREATE TYPE \"enum_myTable_title\" AS ENUM(\"A\", \"B\", \"C\"); CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" \"enum_myTable_title\", \"name\" VARCHAR(255));" + }, + { + arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)', id: 'INTEGER PRIMARY KEY'}], + expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255), \"id\" INTEGER , PRIMARY KEY (\"id\"));" + }, + { + arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)', otherId: 'INTEGER REFERENCES "otherTable" ("id") ON DELETE CASCADE ON UPDATE NO ACTION'}], + expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255), \"otherId\" INTEGER REFERENCES \"otherTable\" (\"id\") ON DELETE CASCADE ON UPDATE NO ACTION);" + } ], dropTableQuery: [ @@ -34,6 +101,14 @@ describe('QueryGenerator', function() { { arguments: ['mySchema.myTable'], expectation: "DROP TABLE IF EXISTS \"mySchema\".\"myTable\";" + }, + { + arguments: ['myTable', {cascade: true}], + expectation: "DROP TABLE IF EXISTS \"myTable\" CASCADE;" + }, + { + arguments: ['mySchema.myTable', {cascade: true}], + expectation: "DROP TABLE IF EXISTS \"mySchema\".\"myTable\" CASCADE;" } ], @@ -46,16 +121,16 @@ describe('QueryGenerator', function() { expectation: "SELECT \"id\", \"name\" FROM \"myTable\";" }, { arguments: ['myTable', {where: {id: 2}}], - expectation: "SELECT * FROM \"myTable\" WHERE \"id\"=2;" + expectation: "SELECT * FROM \"myTable\" WHERE \"myTable\".\"id\"=2;" }, { arguments: ['myTable', {where: {name: 'foo'}}], - expectation: "SELECT * FROM \"myTable\" WHERE \"name\"='foo';" + expectation: "SELECT * FROM \"myTable\" WHERE \"myTable\".\"name\"='foo';" }, { arguments: ['myTable', {where: {name: "foo';DROP TABLE myTable;"}}], - expectation: "SELECT * FROM \"myTable\" WHERE \"name\"='foo'';DROP TABLE myTable;';" + expectation: "SELECT * FROM \"myTable\" WHERE \"myTable\".\"name\"='foo'';DROP TABLE myTable;';" }, { arguments: ['myTable', {where: 2}], - expectation: "SELECT * FROM \"myTable\" WHERE \"id\"=2;" + expectation: "SELECT * FROM \"myTable\" WHERE \"myTable\".\"id\"=2;" }, { arguments: ['foo', { attributes: [['count(*)', 'count']] }], expectation: 'SELECT count(*) as \"count\" FROM \"foo\";' @@ -68,6 +143,12 @@ describe('QueryGenerator', function() { }, { arguments: ['myTable', {group: "name"}], expectation: "SELECT * FROM \"myTable\" GROUP BY \"name\";" + }, { + arguments: ['myTable', {group: ["name"]}], + expectation: "SELECT * FROM \"myTable\" GROUP BY \"name\";" + }, { + arguments: ['myTable', {group: ["name","title"]}], + expectation: "SELECT * FROM \"myTable\" GROUP BY \"name\", \"title\";" }, { arguments: ['myTable', {limit: 10}], expectation: "SELECT * FROM \"myTable\" LIMIT 10;" @@ -83,7 +164,7 @@ describe('QueryGenerator', function() { expectation: "SELECT * FROM \"mySchema\".\"myTable\";" }, { arguments: ['mySchema.myTable', {where: {name: "foo';DROP TABLE mySchema.myTable;"}}], - expectation: "SELECT * FROM \"mySchema\".\"myTable\" WHERE \"name\"='foo'';DROP TABLE mySchema.myTable;';" + expectation: "SELECT * FROM \"mySchema\".\"myTable\" WHERE \"mySchema\".\"myTable\".\"name\"='foo'';DROP TABLE mySchema.myTable;';" } ], @@ -96,7 +177,7 @@ describe('QueryGenerator', function() { expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo'';DROP TABLE myTable;') RETURNING *;" }, { arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}], - expectation: "INSERT INTO \"myTable\" (\"name\",\"birthday\") VALUES ('foo','2011-03-27 10:01:55.0') RETURNING *;" + expectation: "INSERT INTO \"myTable\" (\"name\",\"birthday\") VALUES ('foo','2011-03-27 10:01:55.0Z') RETURNING *;" }, { arguments: ['myTable', {name: 'foo', foo: 1}], expectation: "INSERT INTO \"myTable\" (\"name\",\"foo\") VALUES ('foo',1) RETURNING *;" @@ -127,13 +208,53 @@ describe('QueryGenerator', function() { } ], + bulkInsertQuery: [ + { + arguments: ['myTable', [{name: 'foo'}, {name: 'bar'}]], + expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo'),('bar') RETURNING *;" + }, { + arguments: ['myTable', [{name: "foo';DROP TABLE myTable;"}, {name: 'bar'}]], + expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo'';DROP TABLE myTable;'),('bar') RETURNING *;" + }, { + arguments: ['myTable', [{name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {name: 'bar', birthday: new Date(Date.UTC(2012, 2, 27, 10, 1, 55))}]], + expectation: "INSERT INTO \"myTable\" (\"name\",\"birthday\") VALUES ('foo','2011-03-27 10:01:55.0Z'),('bar','2012-03-27 10:01:55.0Z') RETURNING *;" + }, { + arguments: ['myTable', [{name: 'foo', foo: 1}, {name: 'bar', foo: 2}]], + expectation: "INSERT INTO \"myTable\" (\"name\",\"foo\") VALUES ('foo',1),('bar',2) RETURNING *;" + }, { + arguments: ['myTable', [{name: 'foo', nullValue: null}, {name: 'bar', nullValue: null}]], + expectation: "INSERT INTO \"myTable\" (\"name\",\"nullValue\") VALUES ('foo',NULL),('bar',NULL) RETURNING *;" + }, { + arguments: ['myTable', [{name: 'foo', nullValue: null}, {name: 'bar', nullValue: null}]], + expectation: "INSERT INTO \"myTable\" (\"name\",\"nullValue\") VALUES ('foo',NULL),('bar',NULL) RETURNING *;", + context: {options: {omitNull: false}} + }, { + arguments: ['myTable', [{name: 'foo', nullValue: null}, {name: 'bar', nullValue: null}]], + expectation: "INSERT INTO \"myTable\" (\"name\",\"nullValue\") VALUES ('foo',NULL),('bar',NULL) RETURNING *;", + context: {options: {omitNull: true}} // Note: We don't honour this because it makes little sense when some rows may have nulls and others not + }, { + arguments: ['myTable', [{name: 'foo', nullValue: undefined}, {name: 'bar', nullValue: undefined}]], + expectation: "INSERT INTO \"myTable\" (\"name\",\"nullValue\") VALUES ('foo',NULL),('bar',NULL) RETURNING *;", + context: {options: {omitNull: true}} // Note: As above + }, { + arguments: ['mySchema.myTable', [{name: 'foo'}, {name: 'bar'}]], + expectation: "INSERT INTO \"mySchema\".\"myTable\" (\"name\") VALUES ('foo'),('bar') RETURNING *;" + }, { + arguments: ['mySchema.myTable', [{name: JSON.stringify({info: 'Look ma a " quote'})}, {name: JSON.stringify({info: 'Look ma another " quote'})}]], + expectation: "INSERT INTO \"mySchema\".\"myTable\" (\"name\") VALUES ('{\"info\":\"Look ma a \\\" quote\"}'),('{\"info\":\"Look ma another \\\" quote\"}') RETURNING *;" + }, { + arguments: ['mySchema.myTable', [{name: "foo';DROP TABLE mySchema.myTable;"}, {name: 'bar'}]], + expectation: "INSERT INTO \"mySchema\".\"myTable\" (\"name\") VALUES ('foo'';DROP TABLE mySchema.myTable;'),('bar') RETURNING *;" + } + ], + updateQuery: [ { arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], - expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0' WHERE \"id\"=2 RETURNING *" + expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0Z' WHERE \"id\"=2 RETURNING *" }, { arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, 2], - expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0' WHERE \"id\"=2 RETURNING *" + expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0Z' WHERE \"id\"=2 RETURNING *" }, { arguments: ['myTable', {bar: 2}, {name: 'foo'}], expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo' RETURNING *" @@ -157,7 +278,7 @@ describe('QueryGenerator', function() { context: {options: {omitNull: true}} }, { arguments: ['mySchema.myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], - expectation: "UPDATE \"mySchema\".\"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0' WHERE \"id\"=2 RETURNING *" + expectation: "UPDATE \"mySchema\".\"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0Z' WHERE \"id\"=2 RETURNING *" }, { arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {name: 'foo'}], expectation: "UPDATE \"mySchema\".\"myTable\" SET \"name\"='foo'';DROP TABLE mySchema.myTable;' WHERE \"name\"='foo' RETURNING *" @@ -183,6 +304,9 @@ describe('QueryGenerator', function() { }, { arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {limit: 10}], expectation: "DELETE FROM \"mySchema\".\"myTable\" WHERE \"id\" IN (SELECT \"id\" FROM \"mySchema\".\"myTable\" WHERE \"name\"='foo'';DROP TABLE mySchema.myTable;' LIMIT 10)" + }, { + arguments: ['myTable', {name: 'foo'}, {limit: null}], + expectation: "DELETE FROM \"myTable\" WHERE \"id\" IN (SELECT \"id\" FROM \"myTable\" WHERE \"name\"='foo')" } ], @@ -248,7 +372,7 @@ describe('QueryGenerator', function() { Sequelize.Utils._.each(suites, function(tests, suiteTitle) { describe(suiteTitle, function() { tests.forEach(function(test) { - var title = test.title || 'correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) + var title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) it(title, function() { // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly var context = test.context || {options: {}}; diff --git a/spec-jasmine/query-interface.spec.js b/spec-jasmine/query-interface.spec.js deleted file mode 100644 index 4039cc0f4026..000000000000 --- a/spec-jasmine/query-interface.spec.js +++ /dev/null @@ -1,99 +0,0 @@ -var config = require("./config/config") - , Sequelize = require("../index") - , sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { logging: false }) - , Helpers = new (require("./config/helpers"))(sequelize) - , QueryInterface = require("../lib/query-interface") - -describe('QueryInterface', function() { - var interface = null - - beforeEach(function() { - interface = sequelize.getQueryInterface() - Helpers.dropAllTables() - }) - - afterEach(function() { - interface = null - Helpers.dropAllTables() - }) - - describe('dropAllTables', function() { - it("should drop all tables", function() { - Helpers.async(function(done) { - interface.dropAllTables().success(done).error(function(err) { console.log(err) }) - }) - - Helpers.async(function(done) { - interface.showAllTables().success(function(tableNames) { - expect(tableNames.length).toEqual(0) - done() - }) - }) - - Helpers.async(function(done) { - interface.createTable('table', { name: Sequelize.STRING }) - .success(done) - .error(function(err){ console.log(err)}) - }) - - Helpers.async(function(done) { - interface.showAllTables().success(function(tableNames) { - expect(tableNames.length).toEqual(1) - done() - }) - }) - - Helpers.async(function(done) { - interface.dropAllTables().success(done).error(function(err) { console.log(err) }) - }) - - Helpers.async(function(done) { - interface.showAllTables().success(function(tableNames) { - expect(tableNames.length).toEqual(0) - done() - }) - }) - }) - }) - - describe('indexes', function() { - beforeEach(function(){ - Helpers.async(function(done) { - interface.createTable('User', { - username: Sequelize.STRING, - isAdmin: Sequelize.BOOLEAN - }).success(done) - }) - }) - - it('adds, reads and removes an index to the table', function() { - Helpers.async(function(done) { - interface.addIndex('User', ['username', 'isAdmin']).success(done).error(function(err) { - console.log(err) - }) - }) - - Helpers.async(function(done) { - interface.showIndex('User').success(function(indexes) { - var indexColumns = indexes.map(function(index) { return index.Column_name }).sort() - expect(indexColumns).toEqual(['isAdmin', 'username']) - done() - }).error(function(err) { console.log(err) }) - }) - - Helpers.async(function(done) { - interface.removeIndex('User', ['username', 'isAdmin']).success(done).error(function(err) { - console.log(err) - }) - }) - - Helpers.async(function(done) { - interface.showIndex('User').success(function(indexes) { - var indexColumns = indexes.map(function(index) { return index.Column_name }).sort() - expect(indexColumns).toEqual([]) - done() - }).error(function(err) { console.log(err) }) - }) - }) - }) -}) diff --git a/spec-jasmine/sequelize.spec.js b/spec-jasmine/sequelize.spec.js index 841d8a9209d1..3ea0d5f404f0 100644 --- a/spec-jasmine/sequelize.spec.js +++ b/spec-jasmine/sequelize.spec.js @@ -8,7 +8,17 @@ describe('Sequelize', function() { var setup = function(options) { - options = options || {logging: false} + options = options || {} + + if (!options.hasOwnProperty('pool')) + options.pool = config.mysql.pool + if (!options.hasOwnProperty('logging')) + options.logging = false + if (!options.hasOwnProperty('host')) + options.host = config.mysql.host + if (!options.hasOwnProperty('port')) + options.port = config.mysql.port + sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, options) Helpers = new (require("./config/helpers"))(sequelize) @@ -69,6 +79,17 @@ describe('Sequelize', function() { expect(typeof DAO.options.classMethods.localClassMethod).toEqual('function') expect(typeof DAO.options.instanceMethods.globalInstanceMethod).toEqual('function') }) + + it("uses the passed tableName", function(done) { + var Photo = sequelize.define('Foto', { name: Sequelize.STRING }, { tableName: 'photos' }) + + Photo.sync({ force: true }).success(function() { + sequelize.getQueryInterface().showAllTables().success(function(tableNames) { + expect(tableNames).toContain('photos') + done() + }) + }) + }) }) describe('sync', function() { diff --git a/spec-jasmine/sqlite/query-generator.spec.js b/spec-jasmine/sqlite/query-generator.spec.js index dea921eff8f6..8dd60a4081d9 100644 --- a/spec-jasmine/sqlite/query-generator.spec.js +++ b/spec-jasmine/sqlite/query-generator.spec.js @@ -9,6 +9,81 @@ describe('QueryGenerator', function() { afterEach(function() { Helpers.drop() }) var suites = { + + attributesToSQL: [ + { + arguments: [{id: 'INTEGER'}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: 'INTEGER', foo: 'VARCHAR(255)'}], + expectation: {id: 'INTEGER', foo: 'VARCHAR(255)'} + }, + { + arguments: [{id: {type: 'INTEGER'}}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: false}}], + expectation: {id: 'INTEGER NOT NULL'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: true}}], + expectation: {id: 'INTEGER'} + }, + { + arguments: [{id: {type: 'INTEGER', primaryKey: true, autoIncrement: true}}], + expectation: {id: 'INTEGER PRIMARY KEY AUTOINCREMENT'} + }, + { + arguments: [{id: {type: 'INTEGER', defaultValue: 0}}], + expectation: {id: 'INTEGER DEFAULT 0'} + }, + { + arguments: [{id: {type: 'INTEGER', unique: true}}], + expectation: {id: 'INTEGER UNIQUE'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar'}}], + expectation: {id: 'INTEGER REFERENCES `Bar` (`id`)'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', referencesKey: 'pk'}}], + expectation: {id: 'INTEGER REFERENCES `Bar` (`pk`)'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', onDelete: 'CASCADE'}}], + expectation: {id: 'INTEGER REFERENCES `Bar` (`id`) ON DELETE CASCADE'} + }, + { + arguments: [{id: {type: 'INTEGER', references: 'Bar', onUpdate: 'RESTRICT'}}], + expectation: {id: 'INTEGER REFERENCES `Bar` (`id`) ON UPDATE RESTRICT'} + }, + { + arguments: [{id: {type: 'INTEGER', allowNull: false, defaultValue: 1, references: 'Bar', onDelete: 'CASCADE', onUpdate: 'RESTRICT'}}], + expectation: {id: 'INTEGER NOT NULL DEFAULT 1 REFERENCES `Bar` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT'} + }, + ], + + createTableQuery: [ + { + arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}], + expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255));" + }, + { + arguments: ['myTable', {title: 'ENUM("A", "B", "C")', name: 'VARCHAR(255)'}], + expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` ENUM(\"A\", \"B\", \"C\"), `name` VARCHAR(255));" + }, + { + arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)', id: 'INTEGER PRIMARY KEY'}], + expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255), `id` INTEGER PRIMARY KEY);" + }, + { + arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)', otherId: 'INTEGER REFERENCES `otherTable` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION'}], + expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255), `otherId` INTEGER REFERENCES `otherTable` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION);" + } + ], + insertQuery: [ { arguments: ['myTable', { name: 'foo' }], @@ -46,6 +121,43 @@ describe('QueryGenerator', function() { } ], + bulkInsertQuery: [ + { + arguments: ['myTable', [{name: 'foo'}, {name: 'bar'}]], + expectation: "INSERT INTO `myTable` (`name`) VALUES ('foo'),('bar');" + }, { + arguments: ['myTable', [{name: "'bar'"}, {name: 'foo'}]], + expectation: "INSERT INTO `myTable` (`name`) VALUES ('''bar'''),('foo');" + }, { + arguments: ['myTable', [{name: "bar", value: null}, {name: 'foo', value: 1}]], + expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('bar',NULL),('foo',1);" + }, { + arguments: ['myTable', [{name: "bar", value: undefined}, {name: 'bar', value: 2}]], + expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('bar',NULL),('bar',2);" + }, { + arguments: ['myTable', [{name: "foo", value: true}, {name: 'bar', value: false}]], + expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('foo',1),('bar',0);" + }, { + arguments: ['myTable', [{name: "foo", value: false}, {name: 'bar', value: false}]], + expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('foo',0),('bar',0);" + }, { + arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', foo: 2, nullValue: null}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);" + }, { + arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', foo: 2, nullValue: null}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);", + context: {options: {omitNull: false}} + }, { + arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', foo: 2, nullValue: null}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);", + context: {options: {omitNull: true}} // Note: We don't honour this because it makes little sense when some rows may have nulls and others not + }, { + arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', foo: 2, nullValue: null}]], + expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);", + context: {options: {omitNull: true}} // Note: As above + } + ], + updateQuery: [ { arguments: ['myTable', { name: 'foo' }, { id: 2 }], @@ -77,13 +189,32 @@ describe('QueryGenerator', function() { expectation: "UPDATE `myTable` SET `bar`=2 WHERE `name`='foo'", context: {options: {omitNull: true}} } + ], + + deleteQuery: [ + { + arguments: ['myTable', {name: 'foo'}], + expectation: "DELETE FROM `myTable` WHERE `name`='foo'" + }, { + arguments: ['myTable', 1], + expectation: "DELETE FROM `myTable` WHERE `id`=1" + }, { + arguments: ['myTable', 1, {limit: 10}], + expectation: "DELETE FROM `myTable` WHERE `id`=1" + }, { + arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}, {limit: 10}], + expectation: "DELETE FROM `myTable` WHERE `name`='foo\\';DROP TABLE myTable;'" + }, { + arguments: ['myTable', {name: 'foo'}, {limit: null}], + expectation: "DELETE FROM `myTable` WHERE `name`='foo'" + } ] }; Sequelize.Utils._.each(suites, function(tests, suiteTitle) { describe(suiteTitle, function() { tests.forEach(function(test) { - var title = test.title || 'correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) + var title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) it(title, function() { // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly var context = test.context || {options: {}}; diff --git a/spec-jasmine/utils.spec.js b/spec-jasmine/utils.spec.js index e0abe0445770..49bbbbed935a 100644 --- a/spec-jasmine/utils.spec.js +++ b/spec-jasmine/utils.spec.js @@ -112,4 +112,16 @@ describe('Utils', function() { expect(Utils.isHash(values)).toBeTruthy(); }); }); + + describe('format', function() { + it('should format where clause correctly when the value is truthy', function() { + var where = ['foo = ?', 1]; + expect(Utils.format(where)).toEqual('foo = 1'); + }); + + it('should format where clause correctly when the value is falsy', function() { + var where = ['foo = ?', 0]; + expect(Utils.format(where)).toEqual('foo = 0'); + }); + }); }) diff --git a/spec/assets/migrations/20111117063700-createPerson.js b/spec/assets/migrations/20111117063700-createPerson.js new file mode 100644 index 000000000000..27e3d775c998 --- /dev/null +++ b/spec/assets/migrations/20111117063700-createPerson.js @@ -0,0 +1,17 @@ +module.exports = { + up: function(migration, DataTypes, done) { + migration + .createTable('Person', { + name: DataTypes.STRING, + isBetaMember: { + type: DataTypes.BOOLEAN, + defaultValue: false, + allowNull: false + } + }) + .complete(done) + }, + down: function(migration, DataTypes, done) { + migration.dropTable('Person').complete(done) + } +} diff --git a/spec/assets/migrations/20111130161100-emptyMigration.js b/spec/assets/migrations/20111130161100-emptyMigration.js new file mode 100644 index 000000000000..06a589c73233 --- /dev/null +++ b/spec/assets/migrations/20111130161100-emptyMigration.js @@ -0,0 +1,4 @@ +module.exports = { + up: function(migration, DataTypes, done) { done() }, + down: function(migration, DataTypes, done) { done() } +} diff --git a/spec/assets/migrations/20111205064000-renamePersonToUser.js b/spec/assets/migrations/20111205064000-renamePersonToUser.js new file mode 100644 index 000000000000..931eea18546a --- /dev/null +++ b/spec/assets/migrations/20111205064000-renamePersonToUser.js @@ -0,0 +1,9 @@ +module.exports = { + up: function(migration, DataTypes, done) { + migration.renameTable('Person', 'User').complete(done) + }, + + down: function(migration, DataTypes, done) { + migration.renameTable('User', 'Person').complete(done) + } +} diff --git a/spec/assets/migrations/20111205162700-addSignatureColumnToUser.js b/spec/assets/migrations/20111205162700-addSignatureColumnToUser.js new file mode 100644 index 000000000000..064e75bb3304 --- /dev/null +++ b/spec/assets/migrations/20111205162700-addSignatureColumnToUser.js @@ -0,0 +1,39 @@ +module.exports = { + up: function(migration, DataTypes, done) { + migration + .addColumn('User', 'isAdmin', { type: DataTypes.BOOLEAN, defaultValue: false, allowNull: false }) + .complete(function(err) { + if (err) { + done(err) + } else { + migration + .addColumn('User', 'signature', DataTypes.TEXT) + .complete(function(err) { + if (err) { + done(err) + } else { + migration.addColumn('User', 'shopId', { type: DataTypes.INTEGER, allowNull: true }).complete(done) + } + }) + } + }) + + + }, + + down: function(migration, DataTypes, done) { + migration.removeColumn('User', 'signature').complete(function(err) { + if (err) { + done(err) + } else { + migration.removeColumn('User', 'shopId').complete(function(err) { + if (err) { + done(err) + } else { + migration.removeColumn('User', 'isAdmin').complete(done) + } + }) + } + }) + } +} diff --git a/spec/assets/migrations/20111206061400-removeShopIdColumnFromUser.js b/spec/assets/migrations/20111206061400-removeShopIdColumnFromUser.js new file mode 100644 index 000000000000..c97f2c9ab328 --- /dev/null +++ b/spec/assets/migrations/20111206061400-removeShopIdColumnFromUser.js @@ -0,0 +1,9 @@ +module.exports = { + up: function(migration, DataTypes, done) { + migration.removeColumn('User', 'shopId').complete(done) + }, + + down: function(migration, DataTypes, done) { + migration.addColumn('User', 'shopId', { type: DataTypes.INTEGER, allowNull: true }).complete(done) + } +} diff --git a/spec/assets/migrations/20111206063000-changeSignatureColumnOfUserToMendatory.js b/spec/assets/migrations/20111206063000-changeSignatureColumnOfUserToMendatory.js new file mode 100644 index 000000000000..3c0cf78b62d8 --- /dev/null +++ b/spec/assets/migrations/20111206063000-changeSignatureColumnOfUserToMendatory.js @@ -0,0 +1,11 @@ +module.exports = { + up: function(migration, DataTypes, done) { + migration.changeColumn('User', 'signature', { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'Signature' + }).complete(done) + }, + + down: function(migration, DataTypes, done) { done() } +} diff --git a/spec/assets/migrations/20111206163300-renameSignatureColumnOfUserToSig.js b/spec/assets/migrations/20111206163300-renameSignatureColumnOfUserToSig.js new file mode 100644 index 000000000000..67fe1b80634f --- /dev/null +++ b/spec/assets/migrations/20111206163300-renameSignatureColumnOfUserToSig.js @@ -0,0 +1,9 @@ +module.exports = { + up: function(migration, DataTypes, done) { + migration.renameColumn('User', 'signature', 'sig').complete(done) + }, + + down: function(migration, DataTypes, done) { + migration.renameColumn('User', 'sig', 'signature').complete(done) + } +} diff --git a/spec/assets/project.js b/spec/assets/project.js new file mode 100644 index 000000000000..f2eeced8a1f8 --- /dev/null +++ b/spec/assets/project.js @@ -0,0 +1,5 @@ +module.exports = function(sequelize, DataTypes) { + return sequelize.define('Project' + parseInt(Math.random() * 9999999999999999), { + name: DataTypes.STRING + }) +} \ No newline at end of file diff --git a/spec/associations/belongs-to.spec.js b/spec/associations/belongs-to.spec.js index 2891cb033027..40c8c11f017b 100644 --- a/spec/associations/belongs-to.spec.js +++ b/spec/associations/belongs-to.spec.js @@ -8,9 +8,10 @@ if (typeof require === 'function') { buster.spec.expose() buster.testRunner.timeout = 500 -describe("[" + Helpers.getTestDialectTeaser() + "] BelongsTo", function() { +describe(Helpers.getTestDialectTeaser("BelongsTo"), function() { before(function(done) { Helpers.initTests({ + dialect: dialect, beforeComplete: function(sequelize) { this.sequelize = sequelize }.bind(this), @@ -46,4 +47,136 @@ describe("[" + Helpers.getTestDialectTeaser() + "] BelongsTo", function() { }) }) }) + + describe("Foreign key constraints", function() { + + it("are not enabled by default", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + Task.belongsTo(User) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + task.setUser(user).success(function() { + user.destroy().success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can cascade deletes", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + Task.belongsTo(User, {onDelete: 'cascade'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + task.setUser(user).success(function() { + user.destroy().success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(0) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can restrict deletes", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + Task.belongsTo(User, {onDelete: 'restrict'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + task.setUser(user).success(function() { + user.destroy().error(function() { + // Should fail due to FK restriction + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can cascade updates", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + Task.belongsTo(User, {onUpdate: 'cascade'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + task.setUser(user).success(function() { + + // Changing the id of a DAO requires a little dance since + // the `UPDATE` query generated by `save()` uses `id` in the + // `WHERE` clause + + var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory) + user.QueryInterface.update(user, tableName, {id: 999}, user.id) + .success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + expect(tasks[0].UserId).toEqual(999) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can restrict updates", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + Task.belongsTo(User, {onUpdate: 'restrict'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + task.setUser(user).success(function() { + + // Changing the id of a DAO requires a little dance since + // the `UPDATE` query generated by `save()` uses `id` in the + // `WHERE` clause + + var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory) + user.QueryInterface.update(user, tableName, {id: 999}, user.id) + .error(function() { + // Should fail due to FK restriction + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + }) + }) diff --git a/spec/associations/has-many.spec.js b/spec/associations/has-many.spec.js index a70c4fa84a8d..d8fc58575711 100644 --- a/spec/associations/has-many.spec.js +++ b/spec/associations/has-many.spec.js @@ -8,7 +8,7 @@ if (typeof require === 'function') { buster.spec.expose() buster.testRunner.timeout = 500 -describe("[" + Helpers.getTestDialectTeaser() + "] HasMany", function() { +describe(Helpers.getTestDialectTeaser("HasMany"), function() { before(function(done) { var self = this @@ -288,7 +288,7 @@ describe("[" + Helpers.getTestDialectTeaser() + "] HasMany", function() { var add = this.spy() - this.stub(Sequelize.Utils, 'QueryChainer').returns({ add: add, run: function(){} }) + this.stub(Sequelize.Utils, 'QueryChainer').returns({ add: add, runSerially: function(){} }) this.sequelize.sync({ force: true }) expect(add).toHaveBeenCalledThrice() @@ -323,4 +323,135 @@ describe("[" + Helpers.getTestDialectTeaser() + "] HasMany", function() { }) }) }) + + describe("Foreign key constraints", function() { + + it("are not enabled by default", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasMany(Task) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTasks([task]).success(function() { + user.destroy().success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can cascade deletes", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasMany(Task, {onDelete: 'cascade'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTasks([task]).success(function() { + user.destroy().success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(0) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can restrict deletes", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasMany(Task, {onDelete: 'restrict'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTasks([task]).success(function() { + user.destroy().error(function() { + // Should fail due to FK restriction + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can cascade updates", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasMany(Task, {onUpdate: 'cascade'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTasks([task]).success(function() { + + // Changing the id of a DAO requires a little dance since + // the `UPDATE` query generated by `save()` uses `id` in the + // `WHERE` clause + + var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory) + user.QueryInterface.update(user, tableName, {id: 999}, user.id) + .success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + expect(tasks[0].UserId).toEqual(999) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can restrict updates", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasMany(Task, {onUpdate: 'restrict'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTasks([task]).success(function() { + + // Changing the id of a DAO requires a little dance since + // the `UPDATE` query generated by `save()` uses `id` in the + // `WHERE` clause + + var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory) + user.QueryInterface.update(user, tableName, {id: 999}, user.id) + .error(function() { + // Should fail due to FK restriction + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + }) }) diff --git a/spec/associations/has-one.spec.js b/spec/associations/has-one.spec.js index 4ffa165466b0..e00a7432d153 100644 --- a/spec/associations/has-one.spec.js +++ b/spec/associations/has-one.spec.js @@ -8,7 +8,7 @@ if (typeof require === 'function') { buster.spec.expose() buster.testRunner.timeout = 1500 -describe("[" + Helpers.getTestDialectTeaser() + "] HasOne", function() { +describe(Helpers.getTestDialectTeaser("HasOne"), function() { before(function(done) { var self = this @@ -47,4 +47,136 @@ describe("[" + Helpers.getTestDialectTeaser() + "] HasOne", function() { }) }) }) + + describe("Foreign key constraints", function() { + + it("are not enabled by default", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasOne(Task) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTask(task).success(function() { + user.destroy().success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can cascade deletes", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasOne(Task, {onDelete: 'cascade'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTask(task).success(function() { + user.destroy().success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(0) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can restrict deletes", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasOne(Task, {onDelete: 'restrict'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTask(task).success(function() { + user.destroy().error(function() { + // Should fail due to FK restriction + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can cascade updates", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasOne(Task, {onUpdate: 'cascade'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTask(task).success(function() { + + // Changing the id of a DAO requires a little dance since + // the `UPDATE` query generated by `save()` uses `id` in the + // `WHERE` clause + + var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory) + user.QueryInterface.update(user, tableName, {id: 999}, user.id) + .success(function() { + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + expect(tasks[0].UserId).toEqual(999) + done() + }) + }) + }) + }) + }) + }) + }) + + it("can restrict updates", function(done) { + var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + , User = this.sequelize.define('User', { username: Sequelize.STRING }) + + User.hasOne(Task, {onUpdate: 'restrict'}) + + this.sequelize.sync({ force: true }).success(function() { + User.create({ username: 'foo' }).success(function(user) { + Task.create({ title: 'task' }).success(function(task) { + user.setTask(task).success(function() { + + // Changing the id of a DAO requires a little dance since + // the `UPDATE` query generated by `save()` uses `id` in the + // `WHERE` clause + + var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory) + user.QueryInterface.update(user, tableName, {id: 999}, user.id) + .error(function() { + // Should fail due to FK restriction + Task.findAll().success(function(tasks) { + expect(tasks.length).toEqual(1) + done() + }) + }) + }) + }) + }) + }) + }) + + }) + }) diff --git a/spec/associations/mixin.spec.js b/spec/associations/mixin.spec.js index d8367d7c2313..b20b1171c910 100644 --- a/spec/associations/mixin.spec.js +++ b/spec/associations/mixin.spec.js @@ -7,7 +7,7 @@ if (typeof require === 'function') { buster.spec.expose() -describe("[" + Helpers.getTestDialectTeaser() + "] Mixin", function() { +describe(Helpers.getTestDialectTeaser("Mixin"), function() { before(function(done) { Helpers.initTests({ dialect: dialect, diff --git a/spec/buster-helpers.js b/spec/buster-helpers.js index 1d1a76f10b6a..209f4535829a 100644 --- a/spec/buster-helpers.js +++ b/spec/buster-helpers.js @@ -2,6 +2,7 @@ const Sequelize = require(__dirname + "/../index") , DataTypes = require(__dirname + "/../lib/data-types") , config = require(__dirname + "/config/config") , fs = require('fs') + , buster = require("buster") var BusterHelpers = module.exports = { Sequelize: Sequelize, @@ -10,8 +11,17 @@ var BusterHelpers = module.exports = { var sequelize = this.createSequelizeInstance(options) this.clearDatabase(sequelize, function() { - options.beforeComplete && options.beforeComplete(sequelize, DataTypes) - options.onComplete && options.onComplete(sequelize, DataTypes) + if (options.context) { + options.context.sequelize = sequelize + } + + if (options.beforeComplete) { + options.beforeComplete(sequelize, DataTypes) + } + + if (options.onComplete) { + options.onComplete(sequelize, DataTypes) + } }) }, @@ -70,14 +80,14 @@ var BusterHelpers = module.exports = { return envDialect }, - getTestDialectTeaser: function() { + getTestDialectTeaser: function(moduleName) { var dialect = this.getTestDialect() if (process.env.DIALECT === 'postgres-native') { dialect = 'postgres-native' } - return dialect.toUpperCase() + return "[" + dialect.toUpperCase() + "] " + moduleName }, checkMatchForDialects: function(dialect, value, expectations) { @@ -86,5 +96,14 @@ var BusterHelpers = module.exports = { } else { throw new Error('Undefined expectation for "' + dialect + '"!') } + }, + + assertException: function(block, msg) { + try { + block() + throw new Error('Passed function did not throw an error') + } catch(e) { + buster.assert.equals(e.message, msg) + } } } diff --git a/spec/configuration.spec.js b/spec/configuration.spec.js new file mode 100644 index 000000000000..7e676d694669 --- /dev/null +++ b/spec/configuration.spec.js @@ -0,0 +1,74 @@ +if(typeof require === 'function') { + const buster = require("buster") + , CustomEventEmitter = require("../lib/emitters/custom-event-emitter") + , Helpers = require('./buster-helpers') + , dialect = Helpers.getTestDialect() +} + +buster.spec.expose() +buster.testRunner.timeout = 1000 + +var Sequelize = require(__dirname + '/../index') + +describe(Helpers.getTestDialectTeaser("Configuration"), function() { + describe('Instantiation with a URL string', function() { + it('should accept username, password, host, port, and database', function() { + var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname') + var config = sequelize.config + var options = sequelize.options + + expect(options.dialect).toEqual('mysql') + + expect(config.database).toEqual('dbname') + expect(config.host).toEqual('example.com') + expect(config.username).toEqual('user') + expect(config.password).toEqual('pass') + expect(config.port).toEqual(9821) + }) + + it('should work with no authentication options', function() { + var sequelize = new Sequelize('mysql://example.com:9821/dbname') + var config = sequelize.config + + expect(config.username).toEqual(undefined) + expect(config.password).toEqual(null) + }) + + it('should use the default port when no other is specified', function() { + var sequelize = new Sequelize('mysql://example.com/dbname') + var config = sequelize.config + + // The default port should be set + expect(config.port).toEqual(3306) + }) + }) + + describe('Intantiation with arguments', function() { + it('should accept two parameters (database, username)', function() { + var sequelize = new Sequelize('dbname', 'root') + var config = sequelize.config + + expect(config.database).toEqual('dbname') + expect(config.username).toEqual('root') + }) + + it('should accept three parameters (database, username, password)', function() { + var sequelize = new Sequelize('dbname', 'root', 'pass') + var config = sequelize.config + + expect(config.database).toEqual('dbname') + expect(config.username).toEqual('root') + expect(config.password).toEqual('pass') + }) + + it('should accept four parameters (database, username, password, options)', function() { + var sequelize = new Sequelize('dbname', 'root', 'pass', { port: 999 }) + var config = sequelize.config + + expect(config.database).toEqual('dbname') + expect(config.username).toEqual('root') + expect(config.password).toEqual('pass') + expect(config.port).toEqual(999) + }) + }) +}) diff --git a/spec/dao-factory.spec.js b/spec/dao-factory.spec.js index 3e42c7634072..b1b990bac3f1 100644 --- a/spec/dao-factory.spec.js +++ b/spec/dao-factory.spec.js @@ -2,12 +2,13 @@ if(typeof require === 'function') { const buster = require("buster") , Sequelize = require("../index") , Helpers = require('./buster-helpers') + , _ = require('lodash') , dialect = Helpers.getTestDialect() } buster.spec.expose() -describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { +describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { before(function(done) { Helpers.initTests({ dialect: dialect, @@ -36,6 +37,16 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { expect(User.tableName).toEqual('SuperUsers') }) + it("uses checks to make sure dao factory isnt leaking on multiple define", function() { + var User = this.sequelize.define('SuperUser', {}, { freezeTableName: false }) + var factorySize = this.sequelize.daoFactoryManager.all.length + + var User2 = this.sequelize.define('SuperUser', {}, { freezeTableName: false }) + var factorySize2 = this.sequelize.daoFactoryManager.all.length + + expect(factorySize).toEqual(factorySize2) + }) + it("attaches class and instance methods", function() { var User = this.sequelize.define('UserWithClassAndInstanceMethods', {}, { classMethods: { doSmth: function(){ return 1 } }, @@ -52,18 +63,12 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { }) it("throws an error if 2 autoIncrements are passed", function() { - try { - var User = this.sequelize.define('UserWithTwoAutoIncrements', { + Helpers.assertException(function() { + this.sequelize.define('UserWithTwoAutoIncrements', { userid: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, userscore: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true } }) - // the parse shouldn't execute the following line - // this tests needs to be refactored... - // we need to use expect.toThrow when a later version than 0.6 was released - expect(1).toEqual(2) - } catch(e) { - expect(e.message).toEqual('Invalid DAO definition. Only one autoincrement field allowed.') - } + }.bind(this), 'Invalid DAO definition. Only one autoincrement field allowed.') }) }) @@ -95,6 +100,82 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { var user = this.User.build({ username: 'John Wayne' }) expect(user.selectedValues).toEqual({ username: 'John Wayne' }) }) + + it("attaches getter and setter methods from attribute definition", function() { + var Product = this.sequelize.define('ProductWithSettersAndGetters1', { + price: { + type: Sequelize.INTEGER, + get : function() { + return 'answer = ' + this.getDataValue('price'); + }, + set : function(v) { + return this.setDataValue('price', v + 42); + } + } + },{ + }); + + expect(Product.build({price: 42}).price).toEqual('answer = 84'); + + var p = Product.build({price: 1}); + + expect(p.price).toEqual('answer = 43'); + + p.price = 0; + + expect(p.price).toEqual('answer = 42'); // ah finally the right answer :-) + }) + + it("attaches getter and setter methods from options", function() { + var Product = this.sequelize.define('ProductWithSettersAndGetters2', { + priceInCents: { + type: Sequelize.INTEGER + } + },{ + setterMethods: { + price: function(value) { + this.dataValues.priceInCents = value * 100; + } + }, + getterMethods: { + price: function() { + return '$' + (this.getDataValue('priceInCents') / 100); + }, + + priceInCents: function() { + return this.dataValues.priceInCents; + } + } + }); + + expect(Product.build({price: 20}).priceInCents).toEqual(20 * 100); + expect(Product.build({priceInCents: 30 * 100}).price).toEqual('$' + 30); + }) + + it("attaches getter and setter methods from options only if not defined in attribute", function() { + var Product = this.sequelize.define('ProductWithSettersAndGetters3', { + price1: { + type: Sequelize.INTEGER, + set : function(v) { this.setDataValue('price1', v * 10); } + }, + price2: { + type: Sequelize.INTEGER, + get : function(v) { return this.getDataValue('price2') * 10; } + } + },{ + setterMethods: { + price1: function(v) { this.setDataValue('price1', v * 100); } + }, + getterMethods: { + price2: function() { return '$' + this.getDataValue('price2'); } + } + }); + + var p = Product.build({ price1: 1, price2: 2 }); + + expect(p.price1).toEqual(10); + expect(p.price2).toEqual(20); + }) }) describe('findOrCreate', function () { @@ -205,6 +286,33 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { }) }) + it('raises an error if you mess up the datatype', function() { + Helpers.assertException(function() { + this.sequelize.define('UserBadDataType', { + activity_date: Sequelize.DATe + }) + }.bind(this), 'Unrecognized data type for field activity_date') + + Helpers.assertException(function() { + this.sequelize.define('UserBadDataType', { + activity_date: {type: Sequelize.DATe} + }) + }.bind(this), 'Unrecognized data type for field activity_date') + }) + + it('sets a 64 bit int in bigint', function(done) { + var User = this.sequelize.define('UserWithBigIntFields', { + big: Sequelize.BIGINT + }) + + User.sync({ force: true }).success(function() { + User.create({ big: '9223372036854775807' }).on('success', function(user) { + expect(user.big).toEqual( '9223372036854775807' ) + done() + }) + }) + }) + it('sets auto increment fields', function(done) { var User = this.sequelize.define('UserWithAutoIncrementField', { userid: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, allowNull: false } @@ -331,8 +439,300 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { done() }) }) + + describe('enums', function() { + before(function(done) { + this.Item = this.sequelize.define('Item', { + state: { type: Helpers.Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] } + }) + + this.sequelize.sync({ force: true }).success(function() { + this.Item.create({ state: 'available' }).success(function(item) { + this.item = item + done() + }.bind(this)) + }.bind(this)) + }) + + it('correctly restores enum values', function(done) { + this.Item.find({ where: { state: 'available' }}).success(function(item) { + expect(item.id).toEqual(this.item.id) + done() + }.bind(this)) + }) + }) }) + describe('bulkCreate', function() { + + it('inserts multiple values respecting the white list', function(done) { + var self = this + , data = [{ username: 'Peter', secretValue: '42' }, + { username: 'Paul', secretValue: '23'}] + + this.User.bulkCreate(data, ['username']).success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(2) + + expect(users[0].username).toEqual("Peter") + expect(users[0].secretValue).toBeNull(); + + expect(users[1].username).toEqual("Paul") + expect(users[1].secretValue).toBeNull(); + + done() + }) + }) + }) + + it('should store all values if no whitelist is specified', function(done) { + var self = this + , data = [{ username: 'Peter', secretValue: '42' }, + { username: 'Paul', secretValue: '23'}] + + this.User.bulkCreate(data).success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(2) + + expect(users[0].username).toEqual("Peter") + expect(users[0].secretValue).toEqual('42') + + expect(users[1].username).toEqual("Paul") + expect(users[1].secretValue).toEqual('23') + + done() + }) + }) + }) + + it('saves data with single quote', function(done) { + var self = this + , quote = "Single'Quote" + , data = [{ username: 'Peter', data: quote}, + { username: 'Paul', data: quote}] + + this.User.bulkCreate(data).success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(2) + + expect(users[0].username).toEqual("Peter") + expect(users[0].data).toEqual(quote) + + expect(users[1].username).toEqual("Paul") + expect(users[1].data).toEqual(quote) + + done() + }) + }) + }) + + it('saves data with double quote', function(done) { + var self = this + , quote = 'Double"Quote' + , data = [{ username: 'Peter', data: quote}, + { username: 'Paul', data: quote}] + + this.User.bulkCreate(data).success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(2) + + expect(users[0].username).toEqual("Peter") + expect(users[0].data).toEqual(quote) + + expect(users[1].username).toEqual("Paul") + expect(users[1].data).toEqual(quote) + + done() + }) + }) + }) + + it('saves stringified JSON data', function(done) { + var self = this + , json = JSON.stringify({ key: 'value' }) + , data = [{ username: 'Peter', data: json}, + { username: 'Paul', data: json}] + + this.User.bulkCreate(data).success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(2) + + expect(users[0].username).toEqual("Peter") + expect(users[0].data).toEqual(json) + + expect(users[1].username).toEqual("Paul") + expect(users[1].data).toEqual(json) + + done() + }) + }) + }) + + it('stores the current date in createdAt', function(done) { + var self = this + , data = [{ username: 'Peter'}, + { username: 'Paul'}] + + this.User.bulkCreate(data).success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(2) + + expect(users[0].username).toEqual("Peter") + expect(parseInt(+users[0].createdAt/5000)).toEqual(parseInt(+new Date()/5000)) + + expect(users[1].username).toEqual("Paul") + expect(parseInt(+users[1].createdAt/5000)).toEqual(parseInt(+new Date()/5000)) + + done() + }) + }) + }) + + describe('enums', function() { + before(function(done) { + this.Item = this.sequelize.define('Item', { + state: { type: Helpers.Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] }, + name: Sequelize.STRING + }) + + this.sequelize.sync({ force: true }).success(function() { + this.Item.bulkCreate([{state: 'in_cart', name: 'A'}, { state: 'available', name: 'B'}]).success(function() { + done() + }.bind(this)) + }.bind(this)) + }) + + it('correctly restores enum values', function(done) { + this.Item.find({ where: { state: 'available' }}).success(function(item) { + expect(item.name).toEqual('B') + done() + }.bind(this)) + }) + }) + + }) // - bulkCreate + + describe('update', function() { + + it('updates only values that match filter', function(done) { + var self = this + , data = [{ username: 'Peter', secretValue: '42' }, + { username: 'Paul', secretValue: '42' }, + { username: 'Bob', secretValue: '43' }] + + this.User.bulkCreate(data).success(function() { + + self.User.update({username: 'Bill'}, {secretValue: '42'}) + .success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(3) + + users.forEach(function (user) { + if (user.secretValue == '42') { + expect(user.username).toEqual("Bill") + } else { + expect(user.username).toEqual("Bob") + } + }) + + done() + }) + }) + }) + }) + + it('sets updatedAt to the current timestamp', function(done) { + var self = this + , data = [{ username: 'Peter', secretValue: '42' }, + { username: 'Paul', secretValue: '42' }, + { username: 'Bob', secretValue: '43' }] + + this.User.bulkCreate(data).success(function() { + + self.User.update({username: 'Bill'}, {secretValue: '42'}) + .success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(3) + + expect(users[0].username).toEqual("Bill") + expect(users[1].username).toEqual("Bill") + expect(users[2].username).toEqual("Bob") + + expect(parseInt(+users[0].updatedAt/5000)).toEqual(parseInt(+new Date()/5000)) + expect(parseInt(+users[1].updatedAt/5000)).toEqual(parseInt(+new Date()/5000)) + + done() + }) + }) + }) + }) + + }) // - update + + describe('destroy', function() { + + it('deletes values that match filter', function(done) { + var self = this + , data = [{ username: 'Peter', secretValue: '42' }, + { username: 'Paul', secretValue: '42' }, + { username: 'Bob', secretValue: '43' }] + + this.User.bulkCreate(data).success(function() { + + self.User.destroy({secretValue: '42'}) + .success(function() { + self.User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(1) + + expect(users[0].username).toEqual("Bob") + + done() + }) + }) + }) + }) + + it('sets deletedAt to the current timestamp if paranoid is true', function(done) { + + var self = this + , User = this.sequelize.define('ParanoidUser', { + username: Sequelize.STRING, + secretValue: Sequelize.STRING, + data: Sequelize.STRING + }, { + paranoid: true + }) + , data = [{ username: 'Peter', secretValue: '42' }, + { username: 'Paul', secretValue: '42' }, + { username: 'Bob', secretValue: '43' }] + + User.sync({ force: true }).success(function() { + + User.bulkCreate(data).success(function() { + + User.destroy({secretValue: '42'}) + .success(function() { + User.findAll({order: 'id'}).success(function(users) { + expect(users.length).toEqual(3) + + expect(users[0].username).toEqual("Peter") + expect(users[1].username).toEqual("Paul") + expect(users[2].username).toEqual("Bob") + + expect(parseInt(+users[0].deletedAt/5000)).toEqual(parseInt(+new Date()/5000)) + expect(parseInt(+users[1].deletedAt/5000)).toEqual(parseInt(+new Date()/5000)) + + done() + }) + }) + }) + + }) + + }) + + }) // - destroy + describe('find', function find() { before(function(done) { this.User.create({ @@ -439,690 +839,645 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { }.bind(this)) }) - describe('association fetching', function() { + it('returns the selected fields and all fields of the included table as instance.selectedValues', function(done) { + this.Mission = this.sequelize.define('Mission', { + title: {type: Sequelize.STRING, defaultValue: 'a mission!!'}, + foo: {type: Sequelize.INTEGER, defaultValue: 2}, + }) + + this.Mission.belongsTo(this.User) + this.User.hasMany(this.Mission) + + this.sequelize.sync({ force: true }).complete(function() { + this.Mission.create() + .success(function(mission) { + this.User.create({ + username: 'John DOE' + }).success(function(user) { + mission.setUser(user) + .success(function() { + this.User.find({ + where: { username: 'John DOE' }, + attributes: ['username'], + include: [this.Mission] + }).success(function(user) { + expect(user.selectedValues).toEqual({ username: 'John DOE' }) + done() + }) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + + it('always honors ZERO as primary key', function(_done) { + var permutations = [ + 0, + '0', + {where: {id: 0}}, + {where: {id: '0'}} + ] + , done = _.after(2 * permutations.length, _done); + + this.User.create({name: 'jack'}).success(function (jack) { + this.User.create({name: 'jill'}).success(function (jill) { + permutations.forEach(function(perm) { + this.User.find(perm).done(function(err, user) { + expect(err).toBeNull(); + expect(user).toBeNull(); + done(); + }).on('sql', function(s) { + expect(s.indexOf(0)).not.toEqual(-1); + done(); + }) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + + describe('eager loading', function() { before(function() { - this.Task = this.sequelize.define('Task', { - title: Sequelize.STRING + this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + this.Worker = this.sequelize.define('Worker', { name: Sequelize.STRING }) + + this.init = function(callback) { + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task + + callback() + }.bind(this)) + }.bind(this)) + }.bind(this)) + }.bind(this) + }) + + describe('belongsTo', function() { + before(function(done) { + this.Task.belongsTo(this.Worker) + this.init(function() { + this.task.setWorker(this.worker).success(done) + }.bind(this)) }) - this.User = this.sequelize.define('UserWithName', { - name: Sequelize.STRING + it('throws an error about unexpected input if include contains a non-object', function() { + Helpers.assertException(function() { + this.Worker.find({ include: [ 1 ] }) + }.bind(this), 'Include unexpected. Element has to be either an instance of DAOFactory or an object.') }) - }) - describe('1:1 associations', function() { - it('fetches associated objects (1st direction)', function(done) { - this.User.hasOne(this.Task) - this.Task.belongsTo(this.User) + it('throws an error about missing attributes if include contains an object with daoFactory', function() { + Helpers.assertException(function() { + this.Worker.find({ include: [ { daoFactory: this.Worker } ] }) + }.bind(this), 'Include malformed. Expected attributes: daoFactory, as!') + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task' }).success(function(task) { - user.setTask(task).success(function() { - this.User.find({ - where: { 'UserWithNames.id': 1 }, - include: [ 'Task' ] - }).success(function(user) { - expect(user.task).toBeDefined() - expect(user.task.id).toEqual(task.id) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) - - it('fetches associated objects via "as" param (1st direction)', function(done) { - this.User.hasOne(this.Task, { as: 'Homework' }) - this.Task.belongsTo(this.User) - - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task' }).success(function(task) { - user.setHomework(task).success(function() { - this.User.find({ - where: { 'UserWithNames.id': 1 }, - include: [ 'Homework' ] - }).success(function(user) { - expect(user.homework).toBeDefined() - expect(user.homework.id).toEqual(task.id) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) - - it('fetches associated object (2nd direction)', function(done) { - this.User.hasOne(this.Task) - this.Task.belongsTo(this.User) - - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.User.create({ name: 'another user' }).success(function(another_user) { - this.Task.create({ title: 'task' }).success(function(task) { - user.setTask(task).success(function() { - this.Task.find({ - where: { 'Tasks.id': 1 }, - include: [ 'UserWithName' ] - }).success(function(task) { - expect(task.userWithName).toBeDefined() - expect(task.userWithName.id).toEqual(user.id) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) - - it('fetches associated object via "as" param (2nd direction)', function(done) { - this.User.hasOne(this.Task) - this.Task.belongsTo(this.User, { as: 'Owner' }) - - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.User.create({ name: 'another user' }).success(function(another_user) { - this.Task.create({ title: 'task' }).success(function(task) { - user.setTask(task).success(function() { - this.Task.find({ - where: { 'Tasks.id': 1 }, - include: [ 'Owner' ] - }).success(function(task) { - expect(task.owner).toBeDefined() - expect(task.owner.id).toEqual(user.id) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('throws an error if included DaoFactory is not associated', function() { + Helpers.assertException(function() { + this.Worker.find({ include: [ this.Task ] }) + }.bind(this), 'Task is not associated to Worker!') }) - }) - it('fetches associated objects for 1:N associations (1st direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.belongsTo(this.User) + it('returns the associated worker via task.worker', function(done) { + this.Task.find({ + where: { title: 'homework' }, + include: [ this.Worker ] + }).complete(function(err, task) { + expect(err).toBeNull() + expect(task).toBeDefined() + expect(task.worker).toBeDefined() + expect(task.worker.name).toEqual('worker') + done() + }.bind(this)) + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user.setTasks([task1, task2]).success(function() { - this.User.find({ - where: { 'UserWithNames.id': 1 }, - include: [ 'Task' ] - }).success(function(user) { - expect(user.tasks).toBeDefined() - expect( - user.tasks.map(function(t) { return t.id }) - ).toEqual( - [ task1.id, task2.id ] - ) - done() + it('returns the private and public ip', function(done) { + var Domain = this.sequelize.define('Domain', { ip: Sequelize.STRING }) + var Environment = this.sequelize.define('Environment', { name: Sequelize.STRING }) + + Environment + .belongsTo(Domain, { as: 'PrivateDomain', foreignKey: 'privateDomainId' }) + .belongsTo(Domain, { as: 'PublicDomain', foreignKey: 'publicDomainId' }) + + this.sequelize.sync({ force: true }).complete(function() { + Domain.create({ ip: '192.168.0.1' }).success(function(privateIp) { + Domain.create({ ip: '91.65.189.19' }).success(function(publicIp) { + Environment.create({ name: 'environment' }).success(function(env) { + env.setPrivateDomain(privateIp).success(function() { + env.setPublicDomain(publicIp).success(function() { + Environment.find({ + where: { name: 'environment' }, + include: [ + { daoFactory: Domain, as: 'PrivateDomain' }, + { daoFactory: Domain, as: 'PublicDomain' } + ] + }).complete(function(err, environment) { + expect(err).toBeNull() + expect(environment).toBeDefined() + expect(environment.privateDomain).toBeDefined() + expect(environment.privateDomain.ip).toEqual('192.168.0.1') + expect(environment.publicDomain).toBeDefined() + expect(environment.publicDomain.ip).toEqual('91.65.189.19') + done() + }) + }) }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + }) + }) + }) + }) + }) }) - it('fetches associated objects via "as" param for 1:N associations (1st direction)', function(done) { - this.User.hasMany(this.Task, { as: 'Homeworks' }) - this.Task.belongsTo(this.User) + describe('hasOne', function() { + before(function(done) { + this.Worker.hasOne(this.Task) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user.setHomeworks([task1, task2]).success(function() { - this.User.find({ - where: { 'UserWithNames.id': 1 }, - include: [ 'Homeworks' ] - }).success(function(user) { - expect(user.homeworks).toBeDefined() - expect( - user.homeworks.map(function(t) { return t.id }) - ).toEqual( - [ task1.id, task2.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task + + this.worker.setTask(this.task).success(done) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) - it('fetches associated objects for 1:N associations (2nd direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.belongsTo(this.User) + it('throws an error if included DaoFactory is not associated', function() { + Helpers.assertException(function() { + this.Task.find({ include: [ this.Worker ] }) + }.bind(this), 'Worker is not associated to Task!') + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user.setTasks([task1, task2]).success(function() { - this.Task.find({ - where: { 'Tasks.id': 1 }, - include: [ 'UserWithName' ] - }).success(function(task) { - expect(task.userWithName).toBeDefined() - expect(task.userWithName.name).toEqual(user.name) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('returns the associated task via worker.task', function(done) { + this.Worker.find({ + where: { name: 'worker' }, + include: [ this.Task ] + }).complete(function(err, worker) { + expect(err).toBeNull() + expect(worker).toBeDefined() + expect(worker.task).toBeDefined() + expect(worker.task.title).toEqual('homework') + done() + }.bind(this)) + }) }) - it('fetches associated objects via "as" param for 1:N associations (2nd direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.belongsTo(this.User, { as: 'Owner'}) + describe('hasOne with alias', function() { + before(function(done) { + this.Worker.hasOne(this.Task, { as: 'ToDo' }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user.setTasks([task1, task2]).success(function() { - this.Task.find({ - where: { 'Tasks.id': 1 }, - include: [ 'Owner' ] - }).success(function(task) { - expect(task.owner).toBeDefined() - expect(task.owner.name).toEqual(user.name) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task - it('fetches associated objects for N:M associations (1st direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.hasMany(this.User) + this.worker.setToDo(this.task).success(done) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user1.setTasks([task1, task2]).success(function() { - this.User.find({ - where: { 'UserWithNames.id': user1.id }, - include: [ 'Task' ] - }).success(function(user) { - expect(user.tasks).toBeDefined() - expect( - user.tasks.map(function(t) { return t.id }) - ).toEqual( - [ task1.id, task2.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create + it('throws an error if included DaoFactory is not referenced by alias', function() { + Helpers.assertException(function() { + this.Worker.find({ include: [ this.Task ] }) + }.bind(this), 'Task is not associated to Worker!') + }) + + it('throws an error if alias is not associated', function() { + Helpers.assertException(function() { + this.Worker.find({ include: [ { daoFactory: this.Task, as: 'Work' } ] }) + }.bind(this), 'Task (Work) is not associated to Worker!') + }) - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('returns the associated task via worker.task', function(done) { + this.Worker.find({ + where: { name: 'worker' }, + include: [ { daoFactory: this.Task, as: 'ToDo' } ] + }).complete(function(err, worker) { + expect(err).toBeNull() + expect(worker).toBeDefined() + expect(worker.toDo).toBeDefined() + expect(worker.toDo.title).toEqual('homework') + done() + }.bind(this)) + }) + + it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) { + this.Worker.find({ + where: { name: 'worker' }, + include: [ { model: this.Task, as: 'ToDo' } ] + }).complete(function(err, worker) { + expect(worker.toDo.title).toEqual('homework') + done() + }.bind(this)) + }) }) - it('fetches associated objects via "as" param for N:M associations (1st direction)', function(done) { - this.User.hasMany(this.Task, { as: 'Homeworks' }) - this.Task.hasMany(this.User, { as: 'Owners' }) + describe('hasMany', function() { + before(function(done) { + this.Worker.hasMany(this.Task) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user1.setHomeworks([task1, task2]).success(function() { - this.User.find({ - where: { 'UserWithNames.id': user1.id }, - include: [ 'Homeworks' ] - }).success(function(user) { - expect(user.homeworks).toBeDefined() - expect( - user.homeworks.map(function(t) { return t.id }) - ).toEqual( - [ task1.id, task2.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task + + this.worker.setTasks([ this.task ]).success(done) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('throws an error if included DaoFactory is not associated', function() { + Helpers.assertException(function() { + this.Task.find({ include: [ this.Worker ] }) + }.bind(this), 'Worker is not associated to Task!') + }) + + it('returns the associated tasks via worker.tasks', function(done) { + this.Worker.find({ + where: { name: 'worker' }, + include: [ this.Task ] + }).complete(function(err, worker) { + expect(err).toBeNull() + expect(worker).toBeDefined() + expect(worker.tasks).toBeDefined() + expect(worker.tasks[0].title).toEqual('homework') + done() + }.bind(this)) + }) }) - it('fetches associated objects for N:M associations (2nd direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.hasMany(this.User) + describe('hasMany with alias', function() { + before(function(done) { + this.Worker.hasMany(this.Task, { as: 'ToDos' }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user1.setTasks([task1, task2]).success(function() { - this.Task.find({ - where: { 'Tasks.id': task1.id }, - include: [ 'UserWithName' ] - }).success(function(task) { - expect(task.userWithNames).toBeDefined() - expect( - task.userWithNames.map(function(u) { return u.id }) - ).toEqual( - [ user1.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + this.worker.setToDos([ this.task ]).success(done) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) - it('fetches associated objects via "as" param for N:M associations (2nd direction)', function(done) { - this.User.hasMany(this.Task, { as: 'Homeworks' }) - this.Task.hasMany(this.User, { as: 'Owners' }) + it('throws an error if included DaoFactory is not referenced by alias', function() { + Helpers.assertException(function() { + this.Worker.find({ include: [ this.Task ] }) + }.bind(this), 'Task is not associated to Worker!') + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user1.setHomeworks([task1, task2]).success(function() { - this.Task.find({ - where: { 'Tasks.id': task1.id }, - include: [ 'Owners' ] - }).success(function(task) { - expect(task.owners).toBeDefined() - expect( - task.owners.map(function(u) { return u.id }) - ).toEqual( - [ user1.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create + it('throws an error if alias is not associated', function() { + Helpers.assertException(function() { + this.Worker.find({ include: [ { daoFactory: this.Task, as: 'Work' } ] }) + }.bind(this), 'Task (Work) is not associated to Worker!') + }) - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('returns the associated task via worker.task', function(done) { + this.Worker.find({ + where: { name: 'worker' }, + include: [ { daoFactory: this.Task, as: 'ToDos' } ] + }).complete(function(err, worker) { + expect(err).toBeNull() + expect(worker).toBeDefined() + expect(worker.toDos).toBeDefined() + expect(worker.toDos[0].title).toEqual('homework') + done() + }.bind(this)) + }) + + it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) { + this.Worker.find({ + where: { name: 'worker' }, + include: [ { model: this.Task, as: 'ToDos' } ] + }).complete(function(err, worker) { + expect(worker.toDos[0].title).toEqual('homework') + done() + }.bind(this)) + }) }) }) + describe('queryOptions', function() { + before(function(done) { + this.User.create({ + username: 'barfooz' + }).success(function(user) { + this.user = user + done() + }.bind(this)) + }) + + it("should return a DAO when queryOptions are not set", function (done) { + this.User.find({ where: { username: 'barfooz'}}).done(function (err, user) { + expect(user).toHavePrototype(this.User.DAO.prototype) + done(); + }.bind(this)) + }) + + it("should return a DAO when raw is false", function (done) { + this.User.find({ where: { username: 'barfooz'}}, { raw: false }).done(function (err, user) { + expect(user).toHavePrototype(this.User.DAO.prototype) + + done(); + }.bind(this)) + }) + + it("should return raw data when raw is true", function (done) { + this.User.find({ where: { username: 'barfooz'}}, { raw: true }).done(function (err, user) { + expect(user).not.toHavePrototype(this.User.DAO.prototype) + expect(user).toBeObject() + + done(); + }.bind(this)) + }) + }) // - describe: queryOptions }) //- describe: find describe('findAll', function findAll() { - describe('include', function() { + describe('eager loading', function() { before(function() { - this.Task = this.sequelize.define('Task', { - title: Sequelize.STRING + this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }) + this.Worker = this.sequelize.define('Worker', { name: Sequelize.STRING }) + }) + + describe('belongsTo', function() { + before(function(done) { + this.Task.belongsTo(this.Worker) + + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task + + this.task.setWorker(this.worker).success(done) + }.bind(this)) + }.bind(this)) + }.bind(this)) }) - this.User = this.sequelize.define('UserWithName', { - name: Sequelize.STRING + it('throws an error about unexpected input if include contains a non-object', function() { + Helpers.assertException(function() { + this.Worker.findAll({ include: [ 1 ] }) + }.bind(this), 'Include unexpected. Element has to be either an instance of DAOFactory or an object.') }) - }) - it('fetches data only for the relevant where clause', function(done) { - this.User.hasOne(this.Task) - this.Task.belongsTo(this.User) + it('throws an error about missing attributes if include contains an object with daoFactory', function() { + Helpers.assertException(function() { + this.Worker.findAll({ include: [ { daoFactory: this.Worker } ] }) + }.bind(this), 'Include malformed. Expected attributes: daoFactory, as!') + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - this.User.create({ name: 'barfooz' }).success(function(user2) { - this.Task.create({ title: 'task' }).success(function(task) { - var where = [Sequelize.Utils.addTicks(this.User.tableName) + ".`id`=?", user1.id] + it('throws an error if included DaoFactory is not associated', function() { + Helpers.assertException(function() { + this.Worker.findAll({ include: [ this.Task ] }) + }.bind(this), 'Task is not associated to Worker!') + }) - if (dialect === 'postgres') { - where = ['"' + this.User.tableName + '"."id"=?', user1.id] - } + it('returns the associated worker via task.worker', function(done) { + this.Task.findAll({ + where: { title: 'homework' }, + include: [ this.Worker ] + }).complete(function(err, tasks) { + expect(err).toBeNull() + expect(tasks).toBeDefined() + expect(tasks[0].worker).toBeDefined() + expect(tasks[0].worker.name).toEqual('worker') + done() + }.bind(this)) + }) + }) - this.User.findAll({ - where: where, - include: [ 'Task' ] - }).success(function(users){ - expect(users.length).toEqual(1) - // console.log(users[0]) - done() - }.bind(this)) + describe('hasOne', function() { + before(function(done) { + this.Worker.hasOne(this.Task) + + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task + + this.worker.setTask(this.task).success(done) }.bind(this)) }.bind(this)) }.bind(this)) - }.bind(this)) - }) + }) - it('fetches associated objects for 1:1 associations (1st direction)', function(done) { - this.User.hasOne(this.Task) - this.Task.belongsTo(this.User) + it('throws an error if included DaoFactory is not associated', function() { + Helpers.assertException(function() { + this.Task.findAll({ include: [ this.Worker ] }) + }.bind(this), 'Worker is not associated to Task!') + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task' }).success(function(task) { - user.setTask(task).success(function() { - this.User.findAll({ - where: { 'UserWithNames.id': 1 }, - include: [ 'Task' ] - }).success(function(users) { - expect(users[0].task).toBeDefined() - expect(users[0].task.id).toEqual(task.id) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('returns the associated task via worker.task', function(done) { + this.Worker.findAll({ + where: { name: 'worker' }, + include: [ this.Task ] + }).complete(function(err, workers) { + expect(err).toBeNull() + expect(workers).toBeDefined() + expect(workers[0].task).toBeDefined() + expect(workers[0].task.title).toEqual('homework') + done() + }.bind(this)) + }) }) - it('fetches associated objects via "as" param for 1:1 associations (1st direction)', function(done) { - this.User.hasOne(this.Task, { as: 'Homework' }) - this.Task.belongsTo(this.User) + describe('hasOne with alias', function() { + before(function(done) { + this.Worker.hasOne(this.Task, { as: 'ToDo' }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task' }).success(function(task) { - user.setHomework(task).success(function() { - this.User.findAll({ - where: { 'UserWithNames.id': 1 }, - include: [ 'Homework' ] - }).success(function(users) { - expect(users[0].homework).toBeDefined() - expect(users[0].homework.id).toEqual(task.id) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task - it('fetches associated objects for 1:1 associations (2nd direction)', function(done) { - this.User.hasOne(this.Task) - this.Task.belongsTo(this.User) + this.worker.setToDo(this.task).success(done) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task' }).success(function(task) { - user.setTask(task).success(function() { - this.Task.findAll({ - where: { 'Tasks.id': 1 }, - include: [ 'UserWithName' ] - }).success(function(tasks) { - expect(tasks[0].userWithName).toBeDefined() - expect(tasks[0].userWithName.id).toEqual(user.id) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + it('throws an error if included DaoFactory is not referenced by alias', function() { + Helpers.assertException(function() { + this.Worker.findAll({ include: [ this.Task ] }) + }.bind(this), 'Task is not associated to Worker!') + }) - it('fetches associated objects for 1:1 associations (2nd direction)', function(done) { - this.User.hasOne(this.Task) - this.Task.belongsTo(this.User, { as: 'Owner' }) + it('throws an error if alias is not associated', function() { + Helpers.assertException(function() { + this.Worker.findAll({ include: [ { daoFactory: this.Task, as: 'Work' } ] }) + }.bind(this), 'Task (Work) is not associated to Worker!') + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task' }).success(function(task) { - user.setTask(task).success(function() { - this.Task.findAll({ - where: { 'Tasks.id': 1 }, - include: [ 'Owner' ] - }).success(function(tasks) { - expect(tasks[0].owner).toBeDefined() - expect(tasks[0].owner.id).toEqual(user.id) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('returns the associated task via worker.task', function(done) { + this.Worker.findAll({ + where: { name: 'worker' }, + include: [ { daoFactory: this.Task, as: 'ToDo' } ] + }).complete(function(err, workers) { + expect(err).toBeNull() + expect(workers).toBeDefined() + expect(workers[0].toDo).toBeDefined() + expect(workers[0].toDo.title).toEqual('homework') + done() + }.bind(this)) + }) + + it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) { + this.Worker.findAll({ + where: { name: 'worker' }, + include: [ { model: this.Task, as: 'ToDo' } ] + }).complete(function(err, workers) { + expect(workers[0].toDo.title).toEqual('homework') + done() + }.bind(this)) + }) }) - it('fetches associated objects for 1:N associations (1st direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.belongsTo(this.User) + describe('hasMany', function() { + before(function(done) { + this.Worker.hasMany(this.Task) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user.setTasks([task1, task2]).success(function() { - this.User.findAll({ - where: { 'UserWithNames.id': 1 }, - include: [ 'Task' ] - }).success(function(users) { - expect(users[0].tasks).toBeDefined() - expect( - users[0].tasks.map(function(t) { return t.id }) - ).toEqual( - [ task1.id, task2.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task - it('fetches associated objects for 1:N associations (1st direction)', function(done) { - this.User.hasMany(this.Task, { as: 'Homeworks' }) - this.Task.belongsTo(this.User) + this.worker.setTasks([ this.task ]).success(done) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user.setHomeworks([task1, task2]).success(function() { - this.User.findAll({ - where: { 'UserWithNames.id': 1 }, - include: [ 'Homeworks' ] - }).success(function(users) { - expect(users[0].homeworks).toBeDefined() - expect( - users[0].homeworks.map(function(t) { return t.id }) - ).toEqual( - [ task1.id, task2.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('throws an error if included DaoFactory is not associated', function() { + Helpers.assertException(function() { + this.Task.findAll({ include: [ this.Worker ] }) + }.bind(this), 'Worker is not associated to Task!') + }) + + it('returns the associated tasks via worker.tasks', function(done) { + this.Worker.findAll({ + where: { name: 'worker' }, + include: [ this.Task ] + }).complete(function(err, workers) { + expect(err).toBeNull() + expect(workers).toBeDefined() + expect(workers[0].tasks).toBeDefined() + expect(workers[0].tasks[0].title).toEqual('homework') + done() + }.bind(this)) + }) }) - it('fetches associated objects for 1:N associations (2nd direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.belongsTo(this.User) + describe('hasMany with alias', function() { + before(function(done) { + this.Worker.hasMany(this.Task, { as: 'ToDos' }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user.setTasks([task1, task2]).success(function() { - this.Task.findAll({ - where: { 'Tasks.id': 1 }, - include: [ 'UserWithName' ] - }).success(function(tasks) { - expect(tasks[0].userWithName).toBeDefined() - expect(tasks[0].userWithName.name).toEqual(user.name) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + this.sequelize.sync({ force: true }).complete(function() { + this.Worker.create({ name: 'worker' }).success(function(worker) { + this.Task.create({ title: 'homework' }).success(function(task) { + this.worker = worker + this.task = task - it('fetches associated objects for 1:N associations (2nd direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.belongsTo(this.User, { as: 'Owner' }) + this.worker.setToDos([ this.task ]).success(done) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user) { - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user.setTasks([task1, task2]).success(function() { - this.Task.findAll({ - where: { 'Tasks.id': 1 }, - include: [ 'Owner' ] - }).success(function(tasks) { - expect(tasks[0].owner).toBeDefined() - expect(tasks[0].owner.name).toEqual(user.name) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + it('throws an error if included DaoFactory is not referenced by alias', function() { + Helpers.assertException(function() { + this.Worker.findAll({ include: [ this.Task ] }) + }.bind(this), 'Task is not associated to Worker!') + }) - it('fetches associated objects for N:M associations (1st direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.hasMany(this.User) + it('throws an error if alias is not associated', function() { + Helpers.assertException(function() { + this.Worker.findAll({ include: [ { daoFactory: this.Task, as: 'Work' } ] }) + }.bind(this), 'Task (Work) is not associated to Worker!') + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user1.setTasks([task1, task2]).success(function() { - this.User.findAll({ - where: { 'UserWithNames.id': user1.id }, - include: [ 'Task' ] - }).success(function(users) { - expect(users[0].tasks).toBeDefined() - expect( - users[0].tasks.map(function(t) { return t.id }) - ).toEqual( - [ task1.id, task2.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create + it('returns the associated task via worker.task', function(done) { + this.Worker.findAll({ + where: { name: 'worker' }, + include: [ { daoFactory: this.Task, as: 'ToDos' } ] + }).complete(function(err, workers) { + expect(err).toBeNull() + expect(workers).toBeDefined() + expect(workers[0].toDos).toBeDefined() + expect(workers[0].toDos[0].title).toEqual('homework') + done() + }.bind(this)) + }) - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync + it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) { + this.Worker.findAll({ + where: { name: 'worker' }, + include: [ { daoFactory: this.Task, as: 'ToDos' } ] + }).complete(function(err, workers) { + expect(workers[0].toDos[0].title).toEqual('homework') + done() + }.bind(this)) + }) }) - it('fetches associated objects for N:M associations (1st direction)', function(done) { - this.User.hasMany(this.Task, { as: 'Homeworks' }) - this.Task.hasMany(this.User) - - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user1.setHomeworks([task1, task2]).success(function() { - this.User.findAll({ - where: { 'UserWithNames.id': user1.id }, - include: [ 'Homeworks' ] - }).success(function(users) { - expect(users[0].homeworks).toBeDefined() - expect( - users[0].homeworks.map(function(t) { return t.id }) - ).toEqual( - [ task1.id, task2.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create + describe('queryOptions', function() { + before(function(done) { + this.User.create({ + username: 'barfooz' + }).success(function(user) { + this.user = user + done() + }.bind(this)) + }) - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + it("should return a DAO when queryOptions are not set", function (done) { + this.User.findAll({ where: { username: 'barfooz'}}).done(function (err, users) { + users.forEach(function (user) { + expect(user).toHavePrototype(this.User.DAO.prototype) + }, this) - it('fetches associated objects for N:M associations (2nd direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.hasMany(this.User) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user1.setTasks([task1, task2]).success(function() { - this.Task.findAll({ - where: { 'Tasks.id': task1.id }, - include: [ 'UserWithName' ] - }).success(function(tasks) { - expect(tasks[0].userWithNames).toBeDefined() - expect( - tasks[0].userWithNames.map(function(u) { return u.id }) - ).toEqual( - [ user1.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create + done(); + }.bind(this)) + }) - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + it("should return a DAO when raw is false", function (done) { + this.User.findAll({ where: { username: 'barfooz'}}, { raw: false }).done(function (err, users) { + users.forEach(function (user) { + expect(user).toHavePrototype(this.User.DAO.prototype) + }, this) - it('fetches associated objects for N:M associations (2nd direction)', function(done) { - this.User.hasMany(this.Task) - this.Task.hasMany(this.User, { as: 'Owners' }) + done(); + }.bind(this)) + }) - this.sequelize.sync({ force: true }).success(function() { - this.User.create({ name: 'barfooz' }).success(function(user1) { - - this.Task.create({ title: 'task1' }).success(function(task1) { - this.Task.create({ title: 'task2' }).success(function(task2) { - user1.setTasks([task1, task2]).success(function() { - this.Task.findAll({ - where: { 'Tasks.id': task1.id }, - include: [ 'Owners' ] - }).success(function(tasks) { - expect(tasks[0].owners).toBeDefined() - expect( - tasks[0].owners.map(function(u) { return u.id }) - ).toEqual( - [ user1.id ] - ) - done() - }) - }.bind(this)) //- setTask - }.bind(this)) //- Task.create - }.bind(this)) //- Task.create + it("should return raw data when raw is true", function (done) { + this.User.findAll({ where: { username: 'barfooz'}}, { raw: true }).done(function (err, users) { + users.forEach(function (user) { + expect(user).not.toHavePrototype(this.User.DAO.prototype) + expect(users[0]).toBeObject() + }, this) - }.bind(this)) //- User.create - }.bind(this)) //- sequelize.sync - }) + done(); + }.bind(this)) + }) + }) // - describe: queryOptions }) }) //- describe: findAll @@ -1132,7 +1487,13 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { age: Sequelize.INTEGER }) - this.UserWithAge.sync({ force: true }).success(done) + this.UserWithDec = this.sequelize.define('UserWithDec', { + value: Sequelize.DECIMAL(10, 3) + }) + + this.UserWithAge.sync({ force: true }).success(function(){ + this.UserWithDec.sync({ force: true }).success(done) + }.bind(this)) }) it("should return the min value", function(done) { @@ -1153,6 +1514,17 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { done() }) }) + + it("should allow decimals in min", function(done){ + this.UserWithDec.create({value: 3.5}).success(function(){ + this.UserWithDec.create({ value: 5.5 }).success(function(){ + this.UserWithDec.min('value').success(function(min){ + expect(min).toEqual(3.5) + done() + }) + }.bind(this)) + }.bind(this)) + }) }) //- describe: min describe('max', function() { @@ -1161,7 +1533,13 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { age: Sequelize.INTEGER }) - this.UserWithAge.sync({ force: true }).success(done) + this.UserWithDec = this.sequelize.define('UserWithDec', { + value: Sequelize.DECIMAL(10, 3) + }) + + this.UserWithAge.sync({ force: true }).success(function(){ + this.UserWithDec.sync({ force: true }).success(done) + }.bind(this)) }) it("should return the max value", function(done) { @@ -1175,6 +1553,17 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { }.bind(this)) }) + it("should allow decimals in max", function(done){ + this.UserWithDec.create({value: 3.5}).success(function(){ + this.UserWithDec.create({ value: 5.5 }).success(function(){ + this.UserWithDec.max('value').success(function(max){ + expect(max).toEqual(5.5) + done() + }) + }.bind(this)) + }.bind(this)) + }) + it('allows sql logging', function(done) { this.UserWithAge.max('age').on('sql', function(sql) { expect(sql).toBeDefined() @@ -1183,4 +1572,97 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAOFactory", function() { }) }) }) //- describe: max + + describe('schematic support', function() { + before(function(done){ + var self = this; + + this.UserPublic = this.sequelize.define('UserPublic', { + age: Sequelize.INTEGER + }) + + this.UserSpecial = this.sequelize.define('UserSpecial', { + age: Sequelize.INTEGER + }) + + self.sequelize.dropAllSchemas().success(function(){ + self.sequelize.createSchema('schema_test').success(function(){ + self.sequelize.createSchema('special').success(function(){ + self.UserSpecial.schema('special').sync({force: true}).success(function(UserSpecialSync){ + self.UserSpecialSync = UserSpecialSync; + done() + }) + }) + }) + }) + }) + + it("should be able to list schemas", function(done){ + this.sequelize.showAllSchemas().success(function(schemas){ + expect(schemas).toBeDefined() + expect(schemas[0]).toBeArray() + expect(schemas[0].length).toEqual(2) + done() + }) + }) + + if (dialect === "mysql") { + it("should take schemaDelimiter into account if applicable", function(done){ + var UserSpecialUnderscore = this.sequelize.define('UserSpecialUnderscore', {age: Sequelize.INTEGER}, {schema: 'hello', schemaDelimiter: '_'}) + var UserSpecialDblUnderscore = this.sequelize.define('UserSpecialDblUnderscore', {age: Sequelize.INTEGER}) + UserSpecialUnderscore.sync({force: true}).success(function(User){ + UserSpecialDblUnderscore.schema('hello', '__').sync({force: true}).success(function(DblUser){ + DblUser.create({age: 3}).on('sql', function(dblSql){ + User.create({age: 3}).on('sql', function(sql){ + expect(dblSql).toBeDefined() + expect(dblSql.indexOf('INSERT INTO `hello__UserSpecialDblUnderscores`')).toBeGreaterThan(-1) + expect(sql).toBeDefined() + expect(sql.indexOf('INSERT INTO `hello_UserSpecialUnderscores`')).toBeGreaterThan(-1) + done() + }) + }) + }) + }) + }) + } + + it("should be able to create and update records under any valid schematic", function(done){ + var self = this + + self.UserPublic.sync({ force: true }).success(function(UserPublicSync){ + UserPublicSync.create({age: 3}).on('sql', function(UserPublic){ + self.UserSpecialSync.schema('special').create({age: 3}) + .on('sql', function(UserSpecial){ + expect(UserSpecial).toBeDefined() + expect(UserPublic).toBeDefined() + if (dialect === "postgres") { + expect(self.UserSpecialSync.getTableName()).toEqual('"special"."UserSpecials"'); + expect(UserSpecial.indexOf('INSERT INTO "special"."UserSpecials"')).toBeGreaterThan(-1) + expect(UserPublic.indexOf('INSERT INTO "UserPublics"')).toBeGreaterThan(-1) + } else if (dialect === "sqlite") { + expect(self.UserSpecialSync.getTableName()).toEqual('`special`.`UserSpecials`'); + expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).toBeGreaterThan(-1) + expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).toBeGreaterThan(-1) + } else { + expect(self.UserSpecialSync.getTableName()).toEqual('`special.UserSpecials`'); + expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).toBeGreaterThan(-1) + expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).toBeGreaterThan(-1) + } + }) + .success(function(UserSpecial){ + UserSpecial.updateAttributes({age: 5}) + .on('sql', function(user){ + expect(user).toBeDefined() + if (dialect === "postgres") { + expect(user.indexOf('UPDATE "special"."UserSpecials"')).toBeGreaterThan(-1) + } else { + expect(user.indexOf('UPDATE `special.UserSpecials`')).toBeGreaterThan(-1) + } + done() + }) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + }) }) diff --git a/spec/dao.spec.js b/spec/dao.spec.js index 2703da8108f7..b86d46c903ac 100644 --- a/spec/dao.spec.js +++ b/spec/dao.spec.js @@ -1,12 +1,13 @@ -if(typeof require === 'function') { +if (typeof require === 'function') { const buster = require("buster") , Helpers = require('./buster-helpers') , dialect = Helpers.getTestDialect() + , _ = require('lodash') } buster.spec.expose() -describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { +describe(Helpers.getTestDialectTeaser("DAO"), function() { before(function(done) { var self = this @@ -17,15 +18,320 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { self.User = sequelize.define('User', { username: { type: DataTypes.STRING }, touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, - aNumber: { type: DataTypes.INTEGER } + aNumber: { type: DataTypes.INTEGER }, + bNumber: { type: DataTypes.INTEGER }, + + validateTest: { + type: DataTypes.INTEGER, + allowNull: true, + validate: {isInt: true} + }, + validateCustom: { + type: DataTypes.STRING, + allowNull: true, + validate: {len: {msg: 'Length failed.', args: [1,20]}} + }, + + dateAllowNullTrue: { + type: DataTypes.DATE, + allowNull: true + } + }) + + self.HistoryLog = sequelize.define('HistoryLog', { + someText: { type: DataTypes.STRING }, + aNumber: { type: DataTypes.INTEGER }, + aRandomId: { type: DataTypes.INTEGER } + }) + + self.ParanoidUser = sequelize.define('ParanoidUser', { + username: { type: DataTypes.STRING } + }, { + paranoid: true }) + + self.ParanoidUser.hasOne( self.ParanoidUser ) }, onComplete: function() { - self.User.sync({ force: true }).success(done) + self.User.sync({ force: true }).success(function(){ + self.HistoryLog.sync({ force: true }).success(function(){ + self.ParanoidUser.sync({force: true }).success(done) + }) + }) } }) }) + describe('increment', function () { + before(function (done) { + this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done) + }); + + it('with array', function (done) { + var self = this; + + // Select something + this.User.find(1).done(function (err, user1) { + user1.increment(['aNumber'], 2).done(function (err, user2) { + + self.User.find(1).done(function (err, user3) { + expect(user3.aNumber).toBe(2); + done(); + }); + }); + }); + }); + + it('with single field', function (done) { + var self = this; + + // Select something + this.User.find(1).done(function (err, user1) { + user1.increment('aNumber', 2).done(function (err, user2) { + + self.User.find(1).done(function (err, user3) { + expect(user3.aNumber).toBe(2); + done(); + }); + }); + }); + }); + + it('should still work right with other concurrent updates', function (done) { + var self = this; + // Select something + this.User.find(1).done(function (err, user1) { + // Select the user again (simulating a concurrent query) + self.User.find(1).done(function (err, user2) { + user2.updateAttributes({ + aNumber: user2.aNumber + 1 + }).done(function (err, user3) { + user1.increment(['aNumber'], 2).done(function (err, user4) { + + self.User.find(1).done(function (err, user5) { + expect(user5.aNumber).toBe(3); + done(); + }); + }); + }); + }); + }); + }); + + it('should still work right with other concurrent increments', function (done) { + var self = this; + // Select something + this.User.find(1).done(function (err, user1) { + var _done = _.after(3, function () { + self.User.find(1).done(function (err, user2) { + expect(user2.aNumber).toEqual(6); + done(); + }) + }); + + user1.increment(['aNumber'], 2).done(_done); + user1.increment(['aNumber'], 2).done(_done); + user1.increment(['aNumber'], 2).done(_done); + }); + }); + + it('with key value pair', function (done) { + var self = this; + + // Select something + this.User.find(1).done(function (err, user1) { + user1.increment({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) { + + self.User.find(1).done(function (err, user3) { + expect(user3.aNumber).toBe(1); + expect(user3.bNumber).toBe(2); + done(); + }); + }); + }); + }); + }); + + describe('decrement', function () { + before(function (done) { + this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done) + }); + + it('with array', function (done) { + var self = this; + + // Select something + this.User.find(1).done(function (err, user1) { + user1.decrement(['aNumber'], 2).done(function (err, user2) { + + self.User.find(1).done(function (err, user3) { + expect(user3.aNumber).toBe(-2); + done(); + }); + }); + }); + }); + + it('with single field', function (done) { + var self = this; + + // Select something + this.User.find(1).done(function (err, user1) { + user1.decrement('aNumber', 2).done(function (err, user2) { + + self.User.find(1).done(function (err, user3) { + expect(user3.aNumber).toBe(-2); + done(); + }); + }); + }); + }); + + it('should still work right with other concurrent updates', function (done) { + var self = this; + // Select something + this.User.find(1).done(function (err, user1) { + // Select the user again (simulating a concurrent query) + self.User.find(1).done(function (err, user2) { + user2.updateAttributes({ + aNumber: user2.aNumber + 1 + }).done(function (err, user3) { + user1.decrement(['aNumber'], 2).done(function (err, user4) { + + self.User.find(1).done(function (err, user5) { + expect(user5.aNumber).toBe(-1); + done(); + }); + }); + }); + }); + }); + }); + + it('should still work right with other concurrent increments', function (done) { + var self = this; + // Select something + this.User.find(1).done(function (err, user1) { + var _done = _.after(3, function () { + self.User.find(1).done(function (err, user2) { + expect(user2.aNumber).toEqual(-6); + done(); + }) + }); + + user1.decrement(['aNumber'], 2).done(_done); + user1.decrement(['aNumber'], 2).done(_done); + user1.decrement(['aNumber'], 2).done(_done); + }); + }); + + it('with key value pair', function (done) { + var self = this; + + // Select something + this.User.find(1).done(function (err, user1) { + user1.decrement({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) { + + self.User.find(1).done(function (err, user3) { + expect(user3.aNumber).toBe(-1); + expect(user3.bNumber).toBe(-2); + done(); + }); + }); + }); + }); + }); + + describe('reload', function () { + it("should return a reference to the same DAO instead of creating a new one", function (done) { + this.User.create({ username: 'John Doe' }).done(function (err, originalUser) { + + originalUser.updateAttributes({ username: 'Doe John' }).done(function () { + originalUser.reload().done(function (err, updatedUser) { + expect(originalUser === updatedUser).toBeTrue() + done(); + }) + }) + }) + }) + + it("should update the values on all references to the DAO", function (done) { + var self = this + + this.User.create({ username: 'John Doe' }).done(function (err, originalUser) { + self.User.find(originalUser.id).done(function (err, updater) { + updater.updateAttributes({ username: 'Doe John' }).done(function () { + // We used a different reference when calling updateAttributes, so originalUser is now out of sync + expect(originalUser.username).toEqual('John Doe') + + originalUser.reload().done(function (err, updatedUser) { + expect(originalUser.username).toEqual('Doe John') + expect(updatedUser.username).toEqual('Doe John') + + done(); + }) + }) + }) + }) + }) + + it("should update read only attributes as well (updatedAt)", function (done) { + var self = this + this.timeout = 2000; + + this.User.create({ username: 'John Doe' }).done(function (err, originalUser) { + var originallyUpdatedAt = originalUser.updatedAt + + // Wait for a second, so updatedAt will actually be different + setTimeout(function () { + self.User.find(originalUser.id).done(function (err, updater) { + updater.updateAttributes({ username: 'Doe John' }).done(function () { + originalUser.reload().done(function (err, updatedUser) { + expect(originalUser.updatedAt).toBeGreaterThan(originallyUpdatedAt) + expect(updatedUser.updatedAt).toBeGreaterThan(originallyUpdatedAt) + + done(); + }) + }) + }) + }, 1000) + }) + }) + + it("should update the associations as well", function(done) { + var Book = this.sequelize.define('Book', { title: Helpers.Sequelize.STRING }) + , Page = this.sequelize.define('Page', { content: Helpers.Sequelize.TEXT }) + + Book.hasMany(Page) + Page.belongsTo(Book) + + this.sequelize.sync({ force: true }).success(function() { + Book.create({ title: 'A very old book' }).success(function(book) { + Page.create({ content: 'om nom nom' }).success(function(page) { + book.setPages([ page ]).success(function() { + Book.find({ + where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id, + include: [Page] + }).success(function(leBook) { + page.updateAttributes({ content: 'something totally different' }).success(function(page) { + expect(leBook.pages[0].content).toEqual('om nom nom') + expect(page.content).toEqual('something totally different') + + leBook.reload().success(function(leBook) { + expect(leBook.pages[0].content).toEqual('something totally different') + expect(page.content).toEqual('something totally different') + + done() + }) + }) + }) + }) + }) + }.bind(this)) + }.bind(this)) + }) + }); + describe('default values', function() { describe('current date', function() { it('should store a date in touchedAt', function() { @@ -40,6 +346,29 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { expect(+user.touchedAt).toBe(5000) }) }) + + describe('allowNull date', function() { + it('should be just "null" and not Date with Invalid Date', function(done) { + var self = this; + this.User.build({ username: 'a user'}).save().success(function() { + self.User.find({where: {username: 'a user'}}).success(function(user) { + expect(user.dateAllowNullTrue).toBe(null) + done() + }) + }) + }) + + it('should be the same valid date when saving the date', function(done) { + var self = this; + var date = new Date(); + this.User.build({ username: 'a user', dateAllowNullTrue: date}).save().success(function() { + self.User.find({where: {username: 'a user'}}).success(function(user) { + expect(user.dateAllowNullTrue.toString()).toEqual(date.toString()) + done() + }) + }) + }) + }) }) describe('complete', function() { @@ -61,12 +390,59 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { }) describe('save', function() { + it('should fail a validation upon creating', function(done){ + this.User.create({aNumber: 0, validateTest: 'hello'}).error(function(err){ + expect(err).toBeDefined() + expect(err).toBeObject() + expect(err.validateTest).toBeArray() + expect(err.validateTest[0]).toBeDefined() + expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1); + done(); + }); + }) + + it('should fail a validation upon building', function(done){ + this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save() + .error(function(err){ + expect(err).toBeDefined() + expect(err).toBeObject() + expect(err.validateCustom).toBeDefined() + expect(err.validateCustom).toBeArray() + expect(err.validateCustom[0]).toBeDefined() + expect(err.validateCustom[0]).toEqual('Length failed.') + done() + }) + }) + + it('should fail a validation when updating', function(done){ + this.User.create({aNumber: 0}).success(function(user){ + user.updateAttributes({validateTest: 'hello'}).error(function(err){ + expect(err).toBeDefined() + expect(err).toBeObject() + expect(err.validateTest).toBeDefined() + expect(err.validateTest).toBeArray() + expect(err.validateTest[0]).toBeDefined() + expect(err.validateTest[0].indexOf('Invalid integer:')).toBeGreaterThan(-1) + done() + }) + }) + }) + it('takes zero into account', function(done) { this.User.build({ aNumber: 0 }).save([ 'aNumber' ]).success(function(user) { expect(user.aNumber).toEqual(0) done() }) }) + + it('saves a record with no primary key', function(done){ + this.HistoryLog.create({ someText: 'Some random text', aNumber: 3, aRandomId: 5 }).success(function(log) { + log.updateAttributes({ aNumber: 5 }).success(function(newLog){ + expect(newLog.aNumber).toEqual(5) + done() + }) + }) + }) }) describe('toJSON', function toJSON() { @@ -75,12 +451,14 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { username: Helpers.Sequelize.STRING, age: Helpers.Sequelize.INTEGER, isAdmin: Helpers.Sequelize.BOOLEAN - }, { - timestamps: false, - logging: true - }) + }, { timestamps: false }) - this.User.sync({ force: true }).success(done) + this.Project = this.sequelize.define('NiceProject', { title: Helpers.Sequelize.STRING }, { timestamps: false }) + + this.User.hasMany(this.Project, { as: 'Projects' }) + this.Project.belongsTo(this.User, { as: 'LovelyUser' }) + + this.sequelize.sync({ force: true }).success(done) }) it('returns an object containing all values', function() { @@ -97,6 +475,30 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true }) expect(JSON.parse(JSON.stringify(user))).toEqual({ username: 'test.user', age: 99, isAdmin: true, id: null }) }) + + it('includes the eagerly loaded associations', function(done) { + this.User.create({ username: 'fnord', age: 1, isAdmin: true }).success(function(user) { + this.Project.create({ title: 'fnord' }).success(function(project) { + user.setProjects([ project ]).success(function() { + this.User.findAll({include: [ { model: this.Project, as: 'Projects' } ]}).success(function(users) { + var _user = users[0] + + expect(_user.projects).toBeDefined() + expect(JSON.parse(JSON.stringify(_user)).projects).toBeDefined() + + this.Project.findAll({include: [ { model: this.User, as: 'LovelyUser' } ]}).success(function(projects) { + var _project = projects[0] + + expect(_project.lovelyUser).toBeDefined() + expect(JSON.parse(JSON.stringify(_project)).lovelyUser).toBeDefined() + + done() + }) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) }) describe('findAll', function findAll() { @@ -131,5 +533,155 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { }) }) }) + + it("returns the timestamps if no attributes have been specified", function(done) { + this.User.create({ username: 'fnord' }).success(function() { + this.User.findAll().success(function(users) { + expect(users[0].createdAt).toBeDefined() + done() + }.bind(this)) + }.bind(this)) + }) + + it("does not return the timestamps if the username attribute has been specified", function(done) { + this.User.create({ username: 'fnord' }).success(function() { + this.User.findAll({ attributes: ['username'] }).success(function(users) { + expect(users[0].createdAt).not.toBeDefined() + expect(users[0].username).toBeDefined() + + done() + }.bind(this)) + }.bind(this)) + }) + + it("creates the deletedAt property, when defining paranoid as true", function(done) { + this.ParanoidUser.create({ username: 'fnord' }).success(function() { + this.ParanoidUser.findAll().success(function(users) { + expect(users[0].deletedAt).toBeDefined() + expect(users[0].deletedAt).toBe(null) + done() + }.bind(this)) + }.bind(this)) + }) + + it("sets deletedAt property to a specific date when deleting an instance", function(done) { + this.ParanoidUser.create({ username: 'fnord' }).success(function() { + this.ParanoidUser.findAll().success(function(users) { + users[0].destroy().success(function(user) { + expect(user.deletedAt.getMonth).toBeDefined() + done() + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + + it("keeps the deletedAt-attribute with value null, when running updateAttributes", function(done) { + this.ParanoidUser.create({ username: 'fnord' }).success(function() { + this.ParanoidUser.findAll().success(function(users) { + users[0].updateAttributes({username: 'newFnord'}).success(function(user) { + expect(user.deletedAt).toBe(null) + done() + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + + it("keeps the deletedAt-attribute with value null, when updating associations", function(done) { + this.ParanoidUser.create({ username: 'fnord' }).success(function() { + this.ParanoidUser.findAll().success(function(users) { + this.ParanoidUser.create({ username: 'linkedFnord' }).success(function( linkedUser ) { + users[0].setParanoidUser( linkedUser ).success(function(user) { + expect(user.deletedAt).toBe(null) + done() + }.bind(this)) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + + it("can reuse query option objects", function(done) { + this.User.create({ username: 'fnord' }).success(function() { + var query = { where: { username: 'fnord' }} + + this.User.findAll(query).success(function(users) { + expect(users[0].username).toEqual('fnord') + + this.User.findAll(query).success(function(users) { + expect(users[0].username).toEqual('fnord') + done() + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + }) + + describe('find', function find() { + it("can reuse query option objects", function(done) { + this.User.create({ username: 'fnord' }).success(function() { + var query = { where: { username: 'fnord' }} + + this.User.find(query).success(function(user) { + expect(user.username).toEqual('fnord') + + this.User.find(query).success(function(user) { + expect(user.username).toEqual('fnord') + done() + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + }) + + describe('equals', function find() { + it("can compare records with Date field", function(done) { + this.User.create({ username: 'fnord' }).success(function(user1) { + var query = { where: { username: 'fnord' }} + + this.User.find(query).success(function(user2) { + expect(user1.equals(user2)).toBeTrue() + done() + }.bind(this)) + }.bind(this)) + }) + }) + + describe('updateAttributes', function() { + it('stores and restores null values', function(done) { + var Download = this.sequelize.define('download', { + startedAt: Helpers.Sequelize.DATE, + canceledAt: Helpers.Sequelize.DATE, + finishedAt: Helpers.Sequelize.DATE + }) + + Download.sync({ force: true }).success(function() { + Download.create({ + startedAt: new Date() + }).success(function(download) { + expect(download.startedAt instanceof Date).toBeTrue() + expect(download.canceledAt).toBeFalsy() + expect(download.finishedAt).toBeFalsy() + + download.updateAttributes({ + canceledAt: new Date() + }).success(function(download) { + expect(download.startedAt instanceof Date).toBeTrue() + expect(download.canceledAt instanceof Date).toBeTrue() + expect(download.finishedAt).toBeFalsy() + + Download.all({ + where: (dialect === 'postgres' ? '"finishedAt" IS NULL' : "`finishedAt` IS NULL") + }).success(function(downloads) { + downloads.forEach(function(download) { + expect(download.startedAt instanceof Date).toBeTrue() + expect(download.canceledAt instanceof Date).toBeTrue() + expect(download.finishedAt).toBeFalsy() + }) + + done() + }) + }) + }) + }) + }) }) }) diff --git a/spec/dao.validations.spec.js b/spec/dao.validations.spec.js index 460b742555b9..4d234bb91457 100644 --- a/spec/dao.validations.spec.js +++ b/spec/dao.validations.spec.js @@ -7,7 +7,7 @@ if(typeof require === 'function') { buster.spec.expose() -describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { +describe(Helpers.getTestDialectTeaser("DAO"), function() { describe('validations', function() { before(function(done) { Helpers.initTests({ @@ -42,6 +42,10 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { fail: "abc", pass: "129.89.23.1" } + , isIPv6 : { + fail: '1111:2222:3333::5555:', + pass: 'fe80:0000:0000:0000:0204:61ff:fe9d:f156' + } , isAlpha : { fail: "012", pass: "abc" @@ -278,5 +282,29 @@ describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() { var successfulUser = User.build({ name : "2" }) expect(successfulUser.validate()).toBeNull() }) + + it('skips other validations if allowNull is true and the value is null', function() { + var User = this.sequelize.define('User' + Math.random(), { + age: { + type: Sequelize.INTEGER, + allowNull: true, + validate: { + min: { args: 0, msg: 'must be positive' } + } + } + }) + + var failingUser = User.build({ age: -1 }) + , errors = failingUser.validate() + + expect(errors).not.toBeNull(null) + expect(errors).toEqual({ age: ['must be positive'] }) + + var successfulUser1 = User.build({ age: null }) + expect(successfulUser1.validate()).toBeNull() + + var successfulUser2 = User.build({ age: 1 }) + expect(successfulUser2.validate()).toBeNull() + }) }) }) diff --git a/spec/data-types.spec.js b/spec/data-types.spec.js new file mode 100644 index 000000000000..898bf100dc48 --- /dev/null +++ b/spec/data-types.spec.js @@ -0,0 +1,66 @@ +if(typeof require === 'function') { + const buster = require("buster") + , Sequelize = require("../index") + , Helpers = require('./buster-helpers') + , dialect = Helpers.getTestDialect() +} + +buster.spec.expose() + +describe(Helpers.getTestDialectTeaser('DataTypes'), function() { + it('should return DECIMAL for the default decimal type', function() { + expect(Sequelize.DECIMAL).toEqual('DECIMAL'); + }); + + it('should return DECIMAL(10,2) for the default decimal type with arguments', function() { + expect(Sequelize.DECIMAL(10, 2)).toEqual('DECIMAL(10,2)'); + }); + + var tests = [ + [Sequelize.STRING, 'STRING', 'VARCHAR(255)'], + [Sequelize.STRING(1234), 'STRING(1234)', 'VARCHAR(1234)'], + [Sequelize.STRING(1234).BINARY, 'STRING(1234).BINARY', 'VARCHAR(1234) BINARY'], + [Sequelize.STRING.BINARY, 'STRING.BINARY', 'VARCHAR(255) BINARY'], + + [Sequelize.TEXT, 'TEXT', 'TEXT'], + [Sequelize.DATE, 'DATE', 'DATETIME'], + [Sequelize.NOW, 'NOW', 'NOW'], + [Sequelize.BOOLEAN, 'BOOLEAN', 'TINYINT(1)'], + + [Sequelize.INTEGER, 'INTEGER', 'INTEGER'], + [Sequelize.INTEGER.UNSIGNED, 'INTEGER.UNSIGNED', 'INTEGER UNSIGNED'], + [Sequelize.INTEGER(11), 'INTEGER(11)','INTEGER(11)'], + [Sequelize.INTEGER(11).UNSIGNED, 'INTEGER(11).UNSIGNED', 'INTEGER(11) UNSIGNED'], + [Sequelize.INTEGER(11).UNSIGNED.ZEROFILL,'INTEGER(11).UNSIGNED.ZEROFILL','INTEGER(11) UNSIGNED ZEROFILL'], + [Sequelize.INTEGER(11).ZEROFILL,'INTEGER(11).ZEROFILL', 'INTEGER(11) ZEROFILL'], + [Sequelize.INTEGER(11).ZEROFILL.UNSIGNED,'INTEGER(11).ZEROFILL.UNSIGNED', 'INTEGER(11) UNSIGNED ZEROFILL'], + + [Sequelize.BIGINT, 'BIGINT', 'BIGINT'], + [Sequelize.BIGINT.UNSIGNED, 'BIGINT.UNSIGNED', 'BIGINT UNSIGNED'], + [Sequelize.BIGINT(11), 'BIGINT(11)','BIGINT(11)'], + [Sequelize.BIGINT(11).UNSIGNED, 'BIGINT(11).UNSIGNED', 'BIGINT(11) UNSIGNED'], + [Sequelize.BIGINT(11).UNSIGNED.ZEROFILL, 'BIGINT(11).UNSIGNED.ZEROFILL','BIGINT(11) UNSIGNED ZEROFILL'], + [Sequelize.BIGINT(11).ZEROFILL, 'BIGINT(11).ZEROFILL', 'BIGINT(11) ZEROFILL'], + [Sequelize.BIGINT(11).ZEROFILL.UNSIGNED, 'BIGINT(11).ZEROFILL.UNSIGNED', 'BIGINT(11) UNSIGNED ZEROFILL'], + + [Sequelize.FLOAT, 'FLOAT', 'FLOAT'], + [Sequelize.FLOAT.UNSIGNED, 'FLOAT.UNSIGNED', 'FLOAT UNSIGNED'], + [Sequelize.FLOAT(11), 'FLOAT(11)','FLOAT(11)'], + [Sequelize.FLOAT(11).UNSIGNED, 'FLOAT(11).UNSIGNED', 'FLOAT(11) UNSIGNED'], + [Sequelize.FLOAT(11).UNSIGNED.ZEROFILL,'FLOAT(11).UNSIGNED.ZEROFILL','FLOAT(11) UNSIGNED ZEROFILL'], + [Sequelize.FLOAT(11).ZEROFILL,'FLOAT(11).ZEROFILL', 'FLOAT(11) ZEROFILL'], + [Sequelize.FLOAT(11).ZEROFILL.UNSIGNED,'FLOAT(11).ZEROFILL.UNSIGNED', 'FLOAT(11) UNSIGNED ZEROFILL'], + + [Sequelize.FLOAT(11, 12), 'FLOAT(11,12)','FLOAT(11,12)'], + [Sequelize.FLOAT(11, 12).UNSIGNED, 'FLOAT(11,12).UNSIGNED', 'FLOAT(11,12) UNSIGNED'], + [Sequelize.FLOAT(11, 12).UNSIGNED.ZEROFILL,'FLOAT(11,12).UNSIGNED.ZEROFILL','FLOAT(11,12) UNSIGNED ZEROFILL'], + [Sequelize.FLOAT(11, 12).ZEROFILL,'FLOAT(11,12).ZEROFILL', 'FLOAT(11,12) ZEROFILL'], + [Sequelize.FLOAT(11, 12).ZEROFILL.UNSIGNED,'FLOAT(11,12).ZEROFILL.UNSIGNED', 'FLOAT(11,12) UNSIGNED ZEROFILL'] + ] + + tests.forEach(function(test) { + it('transforms "' + test[1] + '" to "' + test[2] + '"', function() { + expect(test[0]).toEqual(test[2]) + }) + }) +}) diff --git a/spec/emitters/custom-event-emitter.js b/spec/emitters/custom-event-emitter.js new file mode 100644 index 000000000000..89f985c98c50 --- /dev/null +++ b/spec/emitters/custom-event-emitter.js @@ -0,0 +1,66 @@ +if(typeof require === 'function') { + const buster = require("buster") + , CustomEventEmitter = require("../../lib/emitters/custom-event-emitter") + , Helpers = require('../buster-helpers') + , dialect = Helpers.getTestDialect() +} + +buster.spec.expose() +buster.testRunner.timeout = 1000 + +describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() { + describe("proxy", function () { + /* Tests could _probably_ be run synchronously, but for future proofing we're basing it on the events */ + + it("should correctly work with success listeners", function (done) { + var emitter = new CustomEventEmitter() + , proxy = new CustomEventEmitter() + , success = this.spy() + + emitter.success(success) + proxy.success(function () { + process.nextTick(function () { + expect(success.called).toEqual(true) + done(); + }) + }) + + proxy.proxy(emitter) + proxy.emit('success') + }) + + it("should correctly work with error listeners", function (done) { + var emitter = new CustomEventEmitter() + , proxy = new CustomEventEmitter() + , error = this.spy() + + emitter.error(error) + proxy.error(function () { + process.nextTick(function () { + expect(error.called).toEqual(true) + done(); + }) + }) + + proxy.proxy(emitter) + proxy.emit('error') + }) + + it("should correctly work with complete/done listeners", function (done) { + var emitter = new CustomEventEmitter() + , proxy = new CustomEventEmitter() + , complete = this.spy() + + emitter.complete(complete) + proxy.complete(function () { + process.nextTick(function () { + expect(complete.called).toEqual(true) + done(); + }) + }) + + proxy.proxy(emitter) + proxy.emit('success') + }) + }); +}); \ No newline at end of file diff --git a/spec/migrator.spec.js b/spec/migrator.spec.js new file mode 100644 index 000000000000..dd39606ca33d --- /dev/null +++ b/spec/migrator.spec.js @@ -0,0 +1,272 @@ +if(typeof require === 'function') { + const buster = require("buster") + , QueryChainer = require("../lib/query-chainer") + , CustomEventEmitter = require("../lib/emitters/custom-event-emitter") + , Helpers = require('./buster-helpers') + , dialect = Helpers.getTestDialect() + , Migrator = require("../lib/migrator") +} + +buster.spec.expose() +buster.testRunner.timeout = 10000 + +describe(Helpers.getTestDialectTeaser("Migrator"), function() { + before(function(done) { + this.init = function(options, callback) { + options = Helpers.Sequelize.Utils._.extend({ + path: __dirname + '/assets/migrations', + logging: function(){} + }, options || {}) + + var migrator = new Migrator(this.sequelize, options) + + migrator + .findOrCreateSequelizeMetaDAO({ force: true }) + .success(function(SequelizeMeta) { + callback && callback(migrator, SequelizeMeta) + }) + .error(function(err) { console.log(err) }) + }.bind(this) + + Helpers.initTests({ dialect: dialect, onComplete: done, context: this }) + }) + + it("as", function() { + expect(1).toEqual(1) + }) + + describe('getUndoneMigrations', function() { + it("returns no files if timestamps are after the files timestamp", function(done) { + this.init({ from: 20120101010101 }, function(migrator) { + migrator.getUndoneMigrations(function(err, migrations) { + expect(err).toBeNull() + expect(migrations.length).toEqual(0) + done() + }.bind(this)) + }.bind(this)) + }) + + it("returns only files between from and to", function(done) { + this.init({ from: 19700101000000, to: 20111117063700 }, function(migrator) { + migrator.getUndoneMigrations(function(err, migrations) { + expect(err).toBeNull() + expect(migrations.length).toEqual(1) + expect(migrations[migrations.length - 1].filename).toEqual('20111117063700-createPerson.js') + done() + }.bind(this)) + }.bind(this)) + }) + + it("returns exactly the migration which is defined in from and to", function(done) { + this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) { + migrator.getUndoneMigrations(function(err, migrations) { + expect(err).toBeNull() + expect(migrations.length).toEqual(1) + expect(migrations[migrations.length - 1].filename).toEqual('20111117063700-createPerson.js') + done() + }.bind(this)) + }.bind(this)) + }) + + it("returns also the file which is exactly options.from or options.to", function(done) { + this.init({ from: 20111117063700, to: 20111130161100 }, function(migrator) { + migrator.getUndoneMigrations(function(err, migrations) { + expect(err).toBeNull() + expect(migrations.length).toEqual(2) + expect(migrations[0].filename).toEqual('20111117063700-createPerson.js') + expect(migrations[1].filename).toEqual('20111130161100-emptyMigration.js') + done() + }.bind(this)) + }.bind(this)) + }) + + it("returns all files to options.to if no options.from is defined", function(done) { + this.init({ to: 20111130161100 }, function(migrator) { + migrator.getUndoneMigrations(function(err, migrations) { + expect(err).toBeNull() + expect(migrations.length).toEqual(2) + done() + }.bind(this)) + }.bind(this)) + }) + + it("returns all files from last migration id stored in database", function(done) { + this.init(undefined, function(migrator, SequelizeMeta) { + SequelizeMeta.create({ from: null, to: 20111117063700 }).success(function() { + migrator.getUndoneMigrations(function(err, migrations) { + expect(err).toBeNull() + expect(migrations.length).toEqual(6) + expect(migrations[0].filename).toEqual('20111130161100-emptyMigration.js') + done() + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + }) + + describe('migrations', function() { + before(function(done) { + this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) { + this.migrator = migrator + this.migrator.migrate().success(done) + }.bind(this)) + }) + + describe('executions', function() { + it("executes migration #20111117063700 and correctly creates the table", function(done) { + this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) { + tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) + expect(tableNames).toEqual([ 'Person' ]) + done() + }) + }) + + it("executes migration #20111117063700 and correctly adds isBetaMember", function(done) { + this.sequelize.getQueryInterface().describeTable('Person').success(function(data) { + var fields = Helpers.Sequelize.Utils._.keys(data).sort() + expect(fields).toEqual([ 'isBetaMember', 'name' ]) + done() + }) + }) + + it("executes migration #20111117063700 correctly up (createTable) and downwards (dropTable)", function(done) { + this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) { + tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) + expect(tableNames).toEqual([ 'Person' ]) + + this.migrator.migrate({ method: 'down' }).success(function() { + this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) { + tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) + expect(tableNames).toEqual([]) + done() + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + + it("executes the empty migration #20111130161100", function(done) { + this.init({ from: 20111130161100, to: 20111130161100 }, function(migrator) { + // this migration isn't actually testing anything but + // should not timeout + + expect(1).toEqual(1) + + migrator + .migrate() + .success(done) + .error(function(err) { console.log(err) }) + }) + }) + }) + + describe('renameTable', function() { + before(function(done) { + this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) { + this.migrator = migrator + this.migrator.migrate().success(done) + }.bind(this)) + }) + + it("executes migration #20111205064000 and renames a table", function(done) { + this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) { + tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) + expect(tableNames).toContain('Person') + + this.init({ from: 20111205064000, to: 20111205064000 }, function(migrator) { + migrator.migrate().success(function() { + this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) { + tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) + expect(tableNames).toEqual([ 'User' ]) + done() + }) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + }) + + describe('addColumn', function() { + it('adds a column to the user table', function(done) { + this.init({ from: 20111117063700, to: 20111205162700 }, function(migrator) { + migrator.migrate().complete(function(err) { + this.sequelize.getQueryInterface().describeTable('User').complete(function(err, data) { + var signature = data.signature + , isAdmin = data.isAdmin + , shopId = data.shopId + + expect(signature.allowNull).toEqual(true) + expect(isAdmin.allowNull).toEqual(false) + expect(isAdmin.defaultValue).toEqual(false) + expect(shopId.allowNull).toEqual(true) + + done() + }) + }.bind(this)) + }.bind(this)) + }) + }) + + describe('removeColumn', function() { + it('removes the shopId column from user', function(done) { + this.init({ to: 20111206061400 }, function(migrator) { + migrator.migrate().success(function(){ + this.sequelize.getQueryInterface().describeTable('User').success(function(data) { + var signature = data.signature + , isAdmin = data.isAdmin + , shopId = data.shopId + + expect(signature.allowNull).toEqual(true) + expect(isAdmin.allowNull).toEqual(false) + expect(isAdmin.defaultValue).toEqual(false) + + expect(shopId).toBeFalsy() + + done() + }) + }.bind(this)) + }.bind(this)) + }) + }) + + describe('changeColumn', function() { + it('changes the signature column from user to default "signature" + notNull', function(done) { + this.init({ to: 20111206063000 }, function(migrator) { + migrator.migrate().success(function() { + this.sequelize.getQueryInterface().describeTable('User').success(function(data) { + var signature = data.signature + + if (dialect === 'postgres') { + expect(signature.type).toEqual('CHARACTER VARYING') + } else { + expect(signature.type).toEqual('VARCHAR(255)') + } + expect(signature.allowNull).toEqual(false) + expect(signature.defaultValue).toEqual('Signature') + + done() + }) + }.bind(this)) + }.bind(this)) + }) + }) + }) + + describe('renameColumn', function() { + it("renames the signature column from user to sig", function(done) { + this.init({ to: 20111206163300 }, function(migrator) { + migrator.migrate().success(function(){ + this.sequelize.getQueryInterface().describeTable('User').success(function(data) { + var signature = data.signature + , sig = data.sig + + expect(signature).toBeFalsy() + expect(sig).toBeTruthy() + + done() + }) + }.bind(this)) + }.bind(this)) + }) + }) +}) + diff --git a/spec/postgres/dao.spec.js b/spec/postgres/dao.spec.js new file mode 100644 index 000000000000..3df5778805b8 --- /dev/null +++ b/spec/postgres/dao.spec.js @@ -0,0 +1,68 @@ +if(typeof require === 'function') { + const buster = require("buster") + , Helpers = require('../buster-helpers') + , dialect = Helpers.getTestDialect() +} + +buster.spec.expose() + +if (dialect.match(/^postgres/)) { + describe('[POSTGRES] DAO', function() { + before(function(done) { + var self = this + + Helpers.initTests({ + dialect: dialect, + beforeComplete: function(sequelize, DataTypes) { + self.sequelize = sequelize + + self.User = sequelize.define('User', { + username: DataTypes.STRING, + email: {type: DataTypes.ARRAY(DataTypes.TEXT)}, + document: {type: DataTypes.HSTORE, defaultValue: 'default=>value'} + }) + }, + onComplete: function() { + self.User.sync({ force: true }).success(done) + } + }) + }) + + describe('model', function() { + it("create handles array correctly", function(done) { + var self = this + + this.User + .create({ username: 'user', email: ['foo@bar.com', 'bar@baz.com'] }) + .success(function(oldUser) { + expect(oldUser.email).toEqual(['foo@bar.com', 'bar@baz.com']) + done() + }) + .error(function(err) { + console.log(err) + }) + }) + + it("should handle hstore correctly", function(done) { + var self = this + + this.User + .create({ username: 'user', email: ['foo@bar.com'], document: {hello: 'world'}}) + .success(function(newUser) { + expect(newUser.document).toEqual({hello: 'world'}) + // Check to see if updating an hstore field works + newUser.updateAttributes({document: {should: 'update', to: 'this', first: 'place'}}).success(function(oldUser){ + // Postgres always returns keys in alphabetical order (ascending) + expect(oldUser.document).toEqual({first: 'place', should: 'update', to: 'this'}) + // Check to see if the default value for an hstore field works + self.User.create({ username: 'user2', email: ['bar@baz.com']}).success(function(defaultUser){ + expect(defaultUser.document).toEqual({default: 'value'}) + done() + }) + }) + }) + .error(console.log) + }) + }) + }) +} diff --git a/spec/promise.spec.js b/spec/promise.spec.js new file mode 100644 index 000000000000..9ed37e23ebf9 --- /dev/null +++ b/spec/promise.spec.js @@ -0,0 +1,295 @@ +if (typeof require === 'function') { + const buster = require("buster") + , Helpers = require('./buster-helpers') + , dialect = Helpers.getTestDialect() + , _ = require('lodash') +} + +buster.spec.expose() + +describe(Helpers.getTestDialectTeaser("Promise"), function () { + before(function (done) { + var self = this + + Helpers.initTests({ + dialect: dialect, + beforeComplete: function (sequelize, DataTypes) { + self.sequelize = sequelize + self.User = sequelize.define('User', { + username: { type: DataTypes.STRING }, + touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, + aNumber: { type: DataTypes.INTEGER }, + bNumber: { type: DataTypes.INTEGER }, + + validateTest: { + type: DataTypes.INTEGER, + allowNull: true, + validate: {isInt: true} + }, + validateCustom: { + type: DataTypes.STRING, + allowNull: true, + validate: {len: {msg: 'Length failed.', args: [1, 20]}} + }, + + dateAllowNullTrue: { + type: DataTypes.DATE, + allowNull: true + } + }) + + self.HistoryLog = sequelize.define('HistoryLog', { + someText: { type: DataTypes.STRING }, + aNumber: { type: DataTypes.INTEGER }, + aRandomId: { type: DataTypes.INTEGER } + }) + + self.ParanoidUser = sequelize.define('ParanoidUser', { + username: { type: DataTypes.STRING } + }, { + paranoid: true + }) + + self.ParanoidUser.hasOne(self.ParanoidUser) + }, + onComplete: function () { + self.User.sync({ force: true }).then(function () { + return self.HistoryLog.sync({ force: true }) + }).then(function () { + return self.ParanoidUser.sync({force: true }) + }) + .then(function () {done()}, done) + } + }) + }) + + describe('increment', function () { + before(function (done) { + this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done) + }); + + it('with array', function (done) { + var self = this; + + // Select something + this.User.find(1).then(function (user1) { + return user1.increment(['aNumber'], 2) + }).then(function (user2) { + return self.User.find(1) + }).then(function (user3) { + expect(user3.aNumber).toBe(2); + done(); + }, done); + }); + + it('should still work right with other concurrent updates', function (done) { + var self = this; + // Select something + this.User.find(1).then(function (user1) { + // Select the user again (simulating a concurrent query) + return self.User.find(1).then(function (user2) { + return user2.updateAttributes({ + aNumber: user2.aNumber + 1 + }).then(function (user3) { + return user1.increment(['aNumber'], 2) + }).then(function (user4) { + return self.User.find(1) + }).then(function (user5) { + expect(user5.aNumber).toBe(3); + done(); + }, done) + }); + }); + }); + + it('with key value pair', function (done) { + var self = this; + + // Select something + this.User.find(1).then(function (user1) { + return user1.increment({ 'aNumber': 1, 'bNumber': 2}) + }).then(function () { + return self.User.find(1) + }).then(function (user3) { + expect(user3.aNumber).toBe(1); + expect(user3.bNumber).toBe(2); + done(); + }, done); + }); + }); + + describe('decrement', function () { + before(function (done) { + this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done) + }); + + it('with array', function (done) { + var self = this; + + // Select something + this.User.find(1).then(function (user1) { + return user1.decrement(['aNumber'], 2) + }).then(function () { + return self.User.find(1); + }).then(function (user3) { + expect(user3.aNumber).toBe(-2); + done(); + }, done); + }); + + it('with single field', function (done) { + var self = this; + + // Select something + this.User.find(1).then(function (user1) { + return user1.decrement(['aNumber'], 2) + }).then(function () { + return self.User.find(1); + }).then(function (user3) { + expect(user3.aNumber).toBe(-2); + done(); + }, done); + }); + + it('should still work right with other concurrent decrements', function (done) { + var self = this; + // Select something + this.User.find(1).then(function (user1) { + var _done = _.after(3, function () { + self.User.find(1).then(function (user2) { + expect(user2.aNumber).toEqual(-6); + done(); + }) + }); + + user1.decrement(['aNumber'], 2).done(_done); + user1.decrement(['aNumber'], 2).done(_done); + user1.decrement(['aNumber'], 2).done(_done); + }); + }); + }); + + describe('reload', function () { + it("should return a reference to the same DAO instead of creating a new one", function (done) { + this.User.create({ username: 'John Doe' }).then(function (originalUser) { + + return originalUser.updateAttributes({ username: 'Doe John' }).then(function () { + return originalUser.reload() + }).then(function (updatedUser) { + expect(originalUser === updatedUser).toBeTrue() + done(); + }, done) + }) + }) + + it("should update the values on all references to the DAO", function (done) { + var self = this + + this.User.create({ username: 'John Doe' }).then(function (originalUser) { + return self.User.find(originalUser.id).then(function (updater) { + return updater.updateAttributes({ username: 'Doe John' }) + }).then(function () { + // We used a different reference when calling updateAttributes, so originalUser is now out of sync + expect(originalUser.username).toEqual('John Doe') + return originalUser.reload() + }).then(function (updatedUser) { + expect(originalUser.username).toEqual('Doe John') + expect(updatedUser.username).toEqual('Doe John') + + done(); + }, done) + }) + }) + + + it("should update the associations as well", function (done) { + var Book = this.sequelize.define('Book', { title: Helpers.Sequelize.STRING }) + , Page = this.sequelize.define('Page', { content: Helpers.Sequelize.TEXT }) + + Book.hasMany(Page) + Page.belongsTo(Book) + + this.sequelize.sync({ force: true }).then(function () { + return Book.create({ title: 'A very old book' }) + }).then(function (book) { + return Page.create({ content: 'om nom nom' }).then(function (page) { + return book.setPages([ page ]).then(function () { + return Book.find({ + where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id, + include: [Page] + }).then(function (leBook) { + return page.updateAttributes({ content: 'something totally different' }).then(function (page) { + expect(leBook.pages[0].content).toEqual('om nom nom') + expect(page.content).toEqual('something totally different') + + return leBook.reload().then(function (leBook) { + expect(leBook.pages[0].content).toEqual('something totally different') + expect(page.content).toEqual('something totally different') + + done() + }) + }) + }) + }) + }) + }, done) + }) + }); + + describe('complete', function () { + it("gets triggered if an error occurs", function (done) { + this.User.find({ where: "asdasdasd" }).then(null, function (err) { + expect(err).toBeDefined() + expect(err.message).toBeDefined() + done() + }) + }) + + it("gets triggered if everything was ok", function (done) { + this.User.count().then(function (result) { + expect(result).toBeDefined() + done() + }) + }) + }) + + describe('save', function () { + it('should fail a validation upon creating', function (done) { + this.User.create({aNumber: 0, validateTest: 'hello'}).then(null, function (err) { + expect(err).toBeDefined() + expect(err).toBeObject() + expect(err.validateTest).toBeArray() + expect(err.validateTest[0]).toBeDefined() + expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1); + done(); + }); + }) + + it('should fail a validation upon building', function (done) { + this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save() + .then(null, function (err) { + expect(err).toBeDefined() + expect(err).toBeObject() + expect(err.validateCustom).toBeDefined() + expect(err.validateCustom).toBeArray() + expect(err.validateCustom[0]).toBeDefined() + expect(err.validateCustom[0]).toEqual('Length failed.') + done() + }) + }) + + it('should fail a validation when updating', function (done) { + this.User.create({aNumber: 0}).then(function (user) { + return user.updateAttributes({validateTest: 'hello'}) + }).then(null, function (err) { + expect(err).toBeDefined() + expect(err).toBeObject() + expect(err.validateTest).toBeDefined() + expect(err.validateTest).toBeArray() + expect(err.validateTest[0]).toBeDefined() + expect(err.validateTest[0].indexOf('Invalid integer:')).toBeGreaterThan(-1) + done() + }) + }) + }) +}) diff --git a/spec/query-chainer.spec.js b/spec/query-chainer.spec.js index 07e76ca70e72..c842debd6e23 100644 --- a/spec/query-chainer.spec.js +++ b/spec/query-chainer.spec.js @@ -9,7 +9,7 @@ if(typeof require === 'function') { buster.spec.expose() buster.testRunner.timeout = 1000 -describe("[" + Helpers.getTestDialectTeaser() + "] QueryChainer", function() { +describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { before(function() { this.queryChainer = new QueryChainer() }) diff --git a/spec/query-interface.spec.js b/spec/query-interface.spec.js new file mode 100644 index 000000000000..1889142cef7a --- /dev/null +++ b/spec/query-interface.spec.js @@ -0,0 +1,119 @@ +if(typeof require === 'function') { + const buster = require("buster") + , CustomEventEmitter = require("../lib/emitters/custom-event-emitter") + , Helpers = require('./buster-helpers') + , dialect = Helpers.getTestDialect() +} + +buster.spec.expose() +buster.testRunner.timeout = 1000 + +describe(Helpers.getTestDialectTeaser("QueryInterface"), function() { + before(function(done) { + Helpers.initTests({ + dialect: dialect, + beforeComplete: function(sequelize) { + this.sequelize = sequelize + }.bind(this), + onComplete: function() { + this.interface = this.sequelize.getQueryInterface() + done() + }.bind(this) + }) + }) + + describe('dropAllTables', function() { + it("should drop all tables", function(done) { + this.interface.dropAllTables().complete(function(err) { + expect(err).toBeNull() + + this.interface.showAllTables().complete(function(err, tableNames) { + expect(err).toBeNull() + expect(tableNames.length).toEqual(0) + + this.interface.createTable('table', { name: Helpers.Sequelize.STRING }).complete(function(err) { + expect(err).toBeNull() + + this.interface.showAllTables().complete(function(err, tableNames) { + expect(err).toBeNull() + expect(tableNames.length).toEqual(1) + + this.interface.dropAllTables().complete(function(err) { + expect(err).toBeNull() + + this.interface.showAllTables().complete(function(err, tableNames) { + expect(err).toBeNull() + expect(tableNames.length).toEqual(0) + done() + }) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + }) + + describe('indexes', function() { + before(function(done) { + this.interface.createTable('User', { + username: Helpers.Sequelize.STRING, + isAdmin: Helpers.Sequelize.BOOLEAN + }).success(done) + }) + + it('adds, reads and removes an index to the table', function(done) { + this.interface.addIndex('User', ['username', 'isAdmin']).complete(function(err) { + expect(err).toBeNull() + + this.interface.showIndex('User').complete(function(err, indexes) { + expect(err).toBeNull() + + var indexColumns = Helpers.Sequelize.Utils._.uniq(indexes.map(function(index) { return index.name })) + expect(indexColumns).toEqual(['user_username_is_admin']) + + this.interface.removeIndex('User', ['username', 'isAdmin']).complete(function(err) { + expect(err).toBeNull() + + this.interface.showIndex('User').complete(function(err, indexes) { + expect(err).toBeNull() + + indexColumns = Helpers.Sequelize.Utils._.uniq(indexes.map(function(index) { return index.name })) + expect(indexColumns).toEqual([]) + + done() + }) + }.bind(this)) + }.bind(this)) + }.bind(this)) + }) + }) + + describe('describeTable', function() { + before(function(done) { + this.interface.createTable('User', { + username: Helpers.Sequelize.STRING, + isAdmin: Helpers.Sequelize.BOOLEAN + }).success(done) + }) + + it('reads the metadata of the table', function(done) { + this.interface.describeTable('User').complete(function(err, metadata) { + expect(err).toBeNull() + + var username = metadata.username + var isAdmin = metadata.isAdmin + + expect(username.type).toEqual(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)') + expect(username.allowNull).toBeTrue() + expect(username.defaultValue).toBeNull() + + expect(isAdmin.type).toEqual(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)') + expect(isAdmin.allowNull).toBeTrue() + expect(isAdmin.defaultValue).toBeNull() + + done() + }) + }) + }) +}) diff --git a/spec/sequelize.spec.js b/spec/sequelize.spec.js index 11a524dd3202..a55f33db8202 100644 --- a/spec/sequelize.spec.js +++ b/spec/sequelize.spec.js @@ -2,14 +2,24 @@ if(typeof require === 'function') { const buster = require("buster") , Helpers = require('./buster-helpers') , dialect = Helpers.getTestDialect() +} +var qq = function(str) { + if (dialect == 'postgres' || dialect == 'sqlite') { + return '"' + str + '"' + } else if (dialect == 'mysql') { + return '`' + str + '`' + } else { + return str + } } buster.spec.expose() -describe("[" + Helpers.getTestDialectTeaser() + "] Sequelize", function() { +describe(Helpers.getTestDialectTeaser("Sequelize"), function() { before(function(done) { Helpers.initTests({ + dialect: dialect, beforeComplete: function(sequelize) { this.sequelize = sequelize }.bind(this), onComplete: done }) @@ -34,7 +44,7 @@ describe("[" + Helpers.getTestDialectTeaser() + "] Sequelize", function() { username: Helpers.Sequelize.STRING }) - this.insertQuery = "INSERT INTO " + this.User.tableName + " (username, createdAt, updatedAt) VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')" + this.insertQuery = "INSERT INTO " + qq(this.User.tableName) + " (username, " + qq("createdAt") + ", " + qq("updatedAt") + ") VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')" this.User.sync().success(done).error(function(err) { console(err) @@ -43,67 +53,170 @@ describe("[" + Helpers.getTestDialectTeaser() + "] Sequelize", function() { }) it('executes a query the internal way', function(done) { - this.sequelize.query(this.insertQuery, null, { raw: true }).success(function(result) { + this.sequelize.query(this.insertQuery, null, { raw: true }) + .complete(function(err, result) { + if (err) { + console.log(err) + } + expect(err).toBeNull() expect(result).toBeNull() done() }) - .error(function(err) { - console.log(err) - expect(err).not.toBeDefined() - done() - }) }) it('executes a query if only the sql is passed', function(done) { - this.sequelize.query(this.insertQuery).success(function(result) { + this.sequelize.query(this.insertQuery) + .complete(function(err, result) { + if (err) { + console.log(err) + } + expect(err).toBeNull() expect(result).not.toBeDefined() done() }) - .error(function(err) { - console.log(err) - expect(err).not.toBeDefined() - done() - }) }) it('executes select queries correctly', function(done) { this.sequelize.query(this.insertQuery).success(function() { this.sequelize - .query("select * from " + this.User.tableName) - .success(function(users) { + .query("select * from " + qq(this.User.tableName) + "") + .complete(function(err, users) { + if (err) { + console.log(err) + } + expect(err).toBeNull() expect(users.map(function(u){ return u.username })).toEqual(['john']) done() }) - .error(function(err) { - console.log(err) - expect(err).not.toBeDefined() + }.bind(this)) + }) + + it('executes select query and parses dot notation results', function(done) { + this.sequelize.query(this.insertQuery).success(function() { + this.sequelize + .query("select username as " + qq("user.username") + " from " + qq(this.User.tableName) + "") + .complete(function(err, users) { + if (err) { + console.log(err) + } + expect(err).toBeNull() + expect(users.map(function(u){ return u.user })).toEqual([{'username':'john'}]) done() }) }.bind(this)) }) - it('executes stored procedures', function(done) { - this.sequelize.query(this.insertQuery).success(function() { - this.sequelize.query('DROP PROCEDURE IF EXISTS foo').success(function() { - this.sequelize.query( - "CREATE PROCEDURE foo()\nSELECT * FROM " + this.User.tableName + ";" - ).success(function() { - this.sequelize.query('CALL foo()').success(function(users) { - expect(users.map(function(u){ return u.username })).toEqual(['john']) - done() - }) + if (dialect == 'mysql') { + it('executes stored procedures', function(done) { + this.sequelize.query(this.insertQuery).success(function() { + this.sequelize.query('DROP PROCEDURE IF EXISTS foo').success(function() { + this.sequelize.query( + "CREATE PROCEDURE foo()\nSELECT * FROM " + this.User.tableName + ";" + ).success(function() { + this.sequelize.query('CALL foo()').success(function(users) { + expect(users.map(function(u){ return u.username })).toEqual(['john']) + done() + }) + }.bind(this)) }.bind(this)) }.bind(this)) - }.bind(this)) - }) + }) + } else { + console.log('FIXME: I want to be supported in this dialect as well :-(') + } it('uses the passed DAOFactory', function(done) { this.sequelize.query(this.insertQuery).success(function() { - this.sequelize.query("SELECT * FROM " + this.User.tableName + ";", this.User).success(function(users) { + this.sequelize.query("SELECT * FROM " + qq(this.User.tableName) + ";", this.User).success(function(users) { expect(users[0].__factory).toEqual(this.User) done() }.bind(this)) }.bind(this)) }) + + it('destructs dot separated attributes when doing a raw query', function(done) { + var tickChar = (dialect === 'postgres') ? '"' : '`' + , sql = "select 1 as " + Helpers.Sequelize.Utils.addTicks('foo.bar.baz', tickChar) + + this.sequelize.query(sql, null, { raw: true }).success(function(result) { + expect(result).toEqual([ { foo: { bar: { baz: 1 } } } ]) + done() + }) + }) + + it('replaces token with the passed array', function(done) { + this.sequelize.query('select ? as foo, ? as bar', null, { raw: true }, [ 1, 2 ]).success(function(result) { + expect(result).toEqual([{ foo: 1, bar: 2 }]) + done() + }) + }) + }) + + describe('define', function() { + [ + { type: Helpers.Sequelize.ENUM, values: ['scheduled', 'active', 'finished']}, + Helpers.Sequelize.ENUM('scheduled', 'active', 'finished') + ].forEach(function(status) { + describe('enum', function() { + before(function(done) { + this.Review = this.sequelize.define('review', { status: status }) + this.Review.sync({ force: true }).success(done) + }) + + it('raises an error if no values are defined', function() { + Helpers.assertException(function() { + this.sequelize.define('omnomnom', { + bla: { type: Helpers.Sequelize.ENUM } + }) + }.bind(this), 'Values for ENUM haven\'t been defined.') + }) + + it('correctly stores values', function(done) { + this.Review.create({ status: 'active' }).success(function(review) { + expect(review.status).toEqual('active') + done() + }) + }) + + it('correctly loads values', function(done) { + this.Review.create({ status: 'active' }).success(function() { + this.Review.findAll().success(function(reviews) { + expect(reviews[0].status).toEqual('active') + done() + }) + }.bind(this)) + }) + + it("doesn't save an instance if value is not in the range of enums", function() { + Helpers.assertException(function() { + this.Review.create({ status: 'fnord' }) + }.bind(this), 'Value "fnord" for ENUM status is out of allowed scope. Allowed values: scheduled, active, finished') + }) + }) + }) + + describe('table', function() { + [ + { id: { type: Helpers.Sequelize.BIGINT } }, + { id: { type: Helpers.Sequelize.STRING, allowNull: true } }, + { id: { type: Helpers.Sequelize.BIGINT, allowNull: false, primaryKey: true, autoIncrement: true } } + ].forEach(function(customAttributes) { + + it('should be able to override options on the default attributes', function(done) { + var Picture = this.sequelize.define('picture', Helpers.Sequelize.Utils._.cloneDeep(customAttributes)) + Picture.sync({ force: true }).success(function() { + Object.keys(customAttributes).forEach(function(attribute) { + Object.keys(customAttributes[attribute]).forEach(function(option) { + var optionValue = customAttributes[attribute][option]; + expect(Picture.rawAttributes[attribute][option]).toBe(optionValue) + }); + }) + done() + }) + }) + + }) + }) + }) })