From 300ee615c625cc317fa4510e665076322a8ae96d Mon Sep 17 00:00:00 2001 From: Sushant Date: Sat, 19 Oct 2019 12:38:05 +0530 Subject: [PATCH 1/7] build: setup publish config for v5 --- .travis.yml | 17 +++++++++-------- package.json | 10 ++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9d2fc64f9057..8288bbfc332c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ language: node_js branches: only: - master + - v5 - /^greenkeeper/.*$/ except: - /^v\d+\.\d+\.\d+$/ @@ -76,16 +77,16 @@ jobs: node_js: '8' script: - npm run semantic-release - before_deploy: - - npm run docs - deploy: - provider: surge - project: ./esdoc/ - domain: docs.sequelizejs.com - skip_cleanup: true + # before_deploy: + # - npm run docs + # deploy: + # provider: surge + # project: ./esdoc/ + # domain: docs.sequelizejs.com + # skip_cleanup: true stages: - lint - test - name: release - if: branch = master AND type = push AND fork = false + if: branch = v5 AND type = push AND fork = false diff --git a/package.json b/package.json index 005e5c4fa736..a6dfa3bbb6a9 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,16 @@ "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }, + "release": { + "branch": "v5", + "verifyConditions": [ + "@semantic-release/npm", + "@semantic-release/github" + ] + }, + "publishConfig": { + "tag": "v5" + }, "scripts": { "lint": "eslint lib test --quiet", "lint-docs": "markdownlint docs", From caa677f063389d7adadb38682b508bc4e8fd748d Mon Sep 17 00:00:00 2001 From: Sushant Date: Sat, 19 Oct 2019 12:43:12 +0530 Subject: [PATCH 2/7] build: enable appveyor --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 5a0826006d61..151c9ed7dbb0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,4 +40,5 @@ after_test: branches: only: - master + - v5 - /^greenkeeper/.*$/ From 10bf060716f70b942a5fd778f03d430ca023966b Mon Sep 17 00:00:00 2001 From: Sushant Date: Sat, 19 Oct 2019 15:57:20 +0530 Subject: [PATCH 3/7] [ci-skip] change: use latest tag --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a6dfa3bbb6a9..d741c4b762c1 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ ] }, "publishConfig": { - "tag": "v5" + "tag": "latest" }, "scripts": { "lint": "eslint lib test --quiet", From 2083c9a2e412e1dd593d740f1f37fddfb39c69b5 Mon Sep 17 00:00:00 2001 From: Takashi Sasaki Date: Sat, 19 Oct 2019 20:38:33 +0900 Subject: [PATCH 4/7] fix(associations): allow binary key for belongs-to-many (#11581) --- lib/associations/belongs-to-many.js | 3 +- .../associations/belongs-to-many.test.js | 54 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/associations/belongs-to-many.js b/lib/associations/belongs-to-many.js index a96e00e2268f..9fcbe758b627 100644 --- a/lib/associations/belongs-to-many.js +++ b/lib/associations/belongs-to-many.js @@ -532,7 +532,8 @@ class BelongsToMany extends Association { }; return this.get(sourceInstance, options).then(associatedObjects => - _.differenceBy(instancePrimaryKeys, associatedObjects, this.targetKey).length === 0 + _.differenceWith(instancePrimaryKeys, associatedObjects, + (a, b) => _.isEqual(a[this.targetKey], b[this.targetKey])).length === 0 ); } diff --git a/test/integration/associations/belongs-to-many.test.js b/test/integration/associations/belongs-to-many.test.js index 472ddfc5ab4e..7251975694aa 100644 --- a/test/integration/associations/belongs-to-many.test.js +++ b/test/integration/associations/belongs-to-many.test.js @@ -1267,6 +1267,60 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), () => { }); }); + describe('hasAssociations with binary key', () => { + beforeEach(function() { + const keyDataType = dialect === 'mysql' || dialect === 'mariadb' ? 'BINARY(255)' : DataTypes.BLOB('tiny'); + this.Article = this.sequelize.define('Article', { + id: { + type: keyDataType, + primaryKey: true + } + }); + this.Label = this.sequelize.define('Label', { + id: { + type: keyDataType, + primaryKey: true + } + }); + this.ArticleLabel = this.sequelize.define('ArticleLabel'); + + this.Article.belongsToMany(this.Label, { through: this.ArticleLabel }); + this.Label.belongsToMany(this.Article, { through: this.ArticleLabel }); + + return this.sequelize.sync({ force: true }); + }); + + it('answers true for labels that have been assigned', function() { + return Promise.all([ + this.Article.create({ + id: Buffer.alloc(255) + }), + this.Label.create({ + id: Buffer.alloc(255) + }) + ]).then(([article, label]) => Promise.all([ + article, + label, + article.addLabel(label, { + through: 'ArticleLabel' + }) + ])).then(([article, label]) => article.hasLabels([label])) + .then(result => expect(result).to.be.true); + }); + + it('answer false for labels that have not been assigned', function() { + return Promise.all([ + this.Article.create({ + id: Buffer.alloc(255) + }), + this.Label.create({ + id: Buffer.alloc(255) + }) + ]).then(([article, label]) => article.hasLabels([label])) + .then(result => expect(result).to.be.false); + }); + }); + describe('countAssociations', () => { beforeEach(function() { this.User = this.sequelize.define('User', { From 6c781d66340402a0841ea56e01a9d161ff95cef4 Mon Sep 17 00:00:00 2001 From: Alejandro Corredor <9114987+aecorredor@users.noreply.github.com> Date: Tue, 29 Oct 2019 15:06:27 -0400 Subject: [PATCH 5/7] fix(typings): transaction locking (#11621) --- types/lib/model.d.ts | 8 ++- types/lib/transaction.d.ts | 109 ++++++++++++++++++++++--------------- types/test/transaction.ts | 42 ++++++++++++++ 3 files changed, 111 insertions(+), 48 deletions(-) diff --git a/types/lib/model.d.ts b/types/lib/model.d.ts index 98bd56e0f484..6294c3ee0f9b 100644 --- a/types/lib/model.d.ts +++ b/types/lib/model.d.ts @@ -18,7 +18,7 @@ import Op = require('./operators'); import { Promise } from './promise'; import { QueryOptions, IndexesOptions } from './query-interface'; import { Config, Options, Sequelize, SyncOptions } from './sequelize'; -import { Transaction } from './transaction'; +import { Transaction, LOCK } from './transaction'; import { Col, Fn, Literal, Where } from './utils'; import { IndexHints } from '..'; @@ -548,8 +548,10 @@ export interface FindOptions extends QueryOptions, Filterable, Projectable, Para * Postgres also supports transaction.LOCK.KEY_SHARE, transaction.LOCK.NO_KEY_UPDATE and specific model * locks with joins. See [transaction.LOCK for an example](transaction#lock) */ - lock?: Transaction.LOCK | { level: Transaction.LOCK; of: typeof Model }; - + lock?: + | LOCK + | { level: LOCK; of: typeof Model } + | boolean; /** * Skip locked rows. Only supported in Postgres. */ diff --git a/types/lib/transaction.d.ts b/types/lib/transaction.d.ts index 4b4faa09c3a5..73ac6f703ef6 100644 --- a/types/lib/transaction.d.ts +++ b/types/lib/transaction.d.ts @@ -26,6 +26,18 @@ export class Transaction { * Adds hook that is run after a transaction is committed */ public afterCommit(fn: (transaction: this) => void | Promise): void; + + /** + * Returns possible options for row locking + */ + static get LOCK(): LOCK; + + /** + * Same as its static version, but can also be called on instances of + * transactions to get possible options for row locking directly from the + * instance. + */ + get LOCK(): LOCK; } // tslint:disable-next-line no-namespace @@ -71,54 +83,61 @@ export namespace Transaction { IMMEDIATE = 'IMMEDIATE', EXCLUSIVE = 'EXCLUSIVE', } +} +/** + * Possible options for row locking. Used in conjunction with `find` calls: + * + * ```js + * t1 // is a transaction + * t1.LOCK.UPDATE, + * t1.LOCK.SHARE, + * t1.LOCK.KEY_SHARE, // Postgres 9.3+ only + * t1.LOCK.NO_KEY_UPDATE // Postgres 9.3+ only + * ``` + * + * Usage: + * ```js + * t1 // is a transaction + * Model.findAll({ + * where: ..., + * transaction: t1, + * lock: t1.LOCK... + * }); + * ``` + * + * Postgres also supports specific locks while eager loading by using OF: + * ```js + * UserModel.findAll({ + * where: ..., + * include: [TaskModel, ...], + * transaction: t1, + * lock: { + * level: t1.LOCK..., + * of: UserModel + * } + * }); + * ``` + * UserModel will be locked but TaskModel won't! + */ +export enum LOCK { + UPDATE = 'UPDATE', + SHARE = 'SHARE', /** - * Possible options for row locking. Used in conjunction with `find` calls: - * - * ```js - * t1 // is a transaction - * t1.LOCK.UPDATE, - * t1.LOCK.SHARE, - * t1.LOCK.KEY_SHARE, // Postgres 9.3+ only - * t1.LOCK.NO_KEY_UPDATE // Postgres 9.3+ only - * ``` - * - * Usage: - * ```js - * t1 // is a transaction - * Model.findAll({ - * where: ..., - * transaction: t1, - * lock: t1.LOCK... - * }); - * ``` - * - * Postgres also supports specific locks while eager loading by using OF: - * ```js - * UserModel.findAll({ - * where: ..., - * include: [TaskModel, ...], - * transaction: t1, - * lock: { - * level: t1.LOCK..., - * of: UserModel - * } - * }); - * ``` - * UserModel will be locked but TaskModel won't! + * Postgres 9.3+ only */ - enum LOCK { - UPDATE = 'UPDATE', - SHARE = 'SHARE', - /** - * Postgres 9.3+ only - */ - KEY_SHARE = 'KEY SHARE', - /** - * Postgres 9.3+ only - */ - NO_KEY_UPDATE = 'NO KEY UPDATE', - } + KEY_SHARE = 'KEY SHARE', + /** + * Postgres 9.3+ only + */ + NO_KEY_UPDATE = 'NO KEY UPDATE', +} + +interface LOCK { + UPDATE: LOCK.UPDATE; + SHARE: LOCK.SHARE; + KEY_SHARE: LOCK.KEY_SHARE; + NO_KEY_UPDATE: LOCK.NO_KEY_UPDATE; } /** diff --git a/types/test/transaction.ts b/types/test/transaction.ts index 8f4e7e02b997..e4aeaacf9fdc 100644 --- a/types/test/transaction.ts +++ b/types/test/transaction.ts @@ -18,6 +18,48 @@ async function trans() { }); } +async function trans2() { + return await sequelize.transaction(async transaction => { + transaction.afterCommit(() => console.log('transaction complete')); + User.findAll( + { + transaction, + lock: transaction.LOCK.UPDATE, + } + ); + return 1; + }); +} + +async function trans3() { + return await sequelize.transaction(async transaction => { + transaction.afterCommit(() => console.log('transaction complete')); + User.findAll( + { + transaction, + lock: true, + } + ); + return 1; + }); +} + +async function trans4() { + return await sequelize.transaction(async transaction => { + transaction.afterCommit(() => console.log('transaction complete')); + User.findAll( + { + transaction, + lock: { + level: transaction.LOCK.UPDATE, + of: User, + }, + } + ); + return 1; + }); +} + async function transact() { const t = await sequelize.transaction({ deferrable: Deferrable.SET_DEFERRED(['test']), From 81c33d8c2fb719409bf5692eb6a50a5883c2b6f3 Mon Sep 17 00:00:00 2001 From: Anton Vynogradenko Date: Thu, 12 Dec 2019 22:26:23 -0800 Subject: [PATCH 6/7] fix(model): generate ON CONFLICT ... DO UPDATE correctly (#11666) (#11744) --- lib/model.js | 2 +- test/integration/model/bulk-create.test.js | 45 ++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/model.js b/lib/model.js index 7a9273a3dcdd..5a755e28cf01 100644 --- a/lib/model.js +++ b/lib/model.js @@ -2703,7 +2703,7 @@ class Model { // Get primary keys for postgres to enable updateOnDuplicate options.upsertKeys = _.chain(model.primaryKeys).values().map('field').value(); if (Object.keys(model.uniqueKeys).length > 0) { - options.upsertKeys = _.chain(model.uniqueKeys).values().filter(c => c.fields.length === 1).map('column').value(); + options.upsertKeys = _.chain(model.uniqueKeys).values().filter(c => c.fields.length === 1).map(c => c.fields[0]).value(); } } diff --git a/test/integration/model/bulk-create.test.js b/test/integration/model/bulk-create.test.js index 10ee9845472c..98ba2f6d44fd 100644 --- a/test/integration/model/bulk-create.test.js +++ b/test/integration/model/bulk-create.test.js @@ -542,8 +542,53 @@ describe(Support.getTestDialectTeaser('Model'), () => { }); }); }); + + it('when the primary key column names and model field names are different and have unique constraints', function() { + const Person = this.sequelize.define('Person', { + emailAddress: { + type: DataTypes.STRING, + allowNull: false, + primaryKey: true, + unique: true, + field: 'email_address' + }, + name: { + type: DataTypes.STRING, + allowNull: false, + field: 'name' + } + }, {}); + + return Person.sync({ force: true }) + .then(() => { + const inserts = [ + { emailAddress: 'a@example.com', name: 'Alice' } + ]; + return Person.bulkCreate(inserts); + }) + .then(people => { + expect(people.length).to.equal(1); + expect(people[0].emailAddress).to.equal('a@example.com'); + expect(people[0].name).to.equal('Alice'); + + const updates = [ + { emailAddress: 'a@example.com', name: 'CHANGED NAME' }, + { emailAddress: 'b@example.com', name: 'Bob' } + ]; + + return Person.bulkCreate(updates, { updateOnDuplicate: ['emailAddress', 'name'] }); + }) + .then(people => { + expect(people.length).to.equal(2); + expect(people[0].emailAddress).to.equal('a@example.com'); + expect(people[0].name).to.equal('CHANGED NAME'); + expect(people[1].emailAddress).to.equal('b@example.com'); + expect(people[1].name).to.equal('Bob'); + }); + }); }); + it('should reject for non array updateOnDuplicate option', function() { const data = [ { uniqueName: 'Peter', secretValue: '42' }, From bbc3eba36272c4def99208a1c17365542b3b5a3a Mon Sep 17 00:00:00 2001 From: Dima Volovich Date: Mon, 6 Feb 2023 10:23:29 +0200 Subject: [PATCH 7/7] Houzz sequelize upgrades --- lib/dialects/abstract/query.js | 6 +++++- lib/dialects/sqlite/query.js | 3 +++ lib/hooks.js | 3 ++- lib/sequelize.js | 10 +++++++--- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/dialects/abstract/query.js b/lib/dialects/abstract/query.js index 6cd6b8b0289d..4b65279bcdaf 100755 --- a/lib/dialects/abstract/query.js +++ b/lib/dialects/abstract/query.js @@ -331,6 +331,8 @@ class AbstractQuery { _logQuery(sql, debugContext, parameters) { const { connection, options } = this; const benchmark = this.sequelize.options.benchmark || options.benchmark; + const instanceType = connection.queryType === 'read' ? 'replica' : 'primary'; + const instanceTypeMessage = `on ${instanceType} DB`; const logQueryParameters = this.sequelize.options.logQueryParameters || options.logQueryParameters; const startTime = Date.now(); let logParameter = ''; @@ -355,7 +357,9 @@ class AbstractQuery { const afterMsg = `Executed ${fmt}`; debugContext(afterMsg); if (benchmark) { - this.sequelize.log(afterMsg, Date.now() - startTime, options); + // this.sequelize.log(afterMsg, Date.now() - startTime, options); + const elapsedTime = Date.now() - startTime; + this.sequelize.log(`Executed ${elapsedTime > this.sequelize.options.queryThreshold ? 'SLOW QUERY ' : ''} (${instanceTypeMessage}, ${fmt}`, elapsedTime, options); } }; } diff --git a/lib/dialects/sqlite/query.js b/lib/dialects/sqlite/query.js index 83e26c73cb32..d178e6bd4adc 100644 --- a/lib/dialects/sqlite/query.js +++ b/lib/dialects/sqlite/query.js @@ -419,6 +419,9 @@ class Query extends AbstractQuery { return new sequelizeErrors.TimeoutError(err); default: + if (err.message && err.message.includes("SQLITE_ERROR: no such table")) { + console.log(err.sql); + } return new sequelizeErrors.DatabaseError(err); } } diff --git a/lib/hooks.js b/lib/hooks.js index 2599bf44f4cd..23b729deb3d8 100644 --- a/lib/hooks.js +++ b/lib/hooks.js @@ -49,7 +49,8 @@ const hookTypes = { beforeBulkSync: { params: 1 }, afterBulkSync: { params: 1 }, beforeQuery: { params: 2 }, - afterQuery: { params: 2 } + afterQuery: { params: 2 }, + beforeGetConnection: {params: 2} }; exports.hooks = hookTypes; diff --git a/lib/sequelize.js b/lib/sequelize.js index 491b30cd9e6b..ee08ca68331b 100755 --- a/lib/sequelize.js +++ b/lib/sequelize.js @@ -635,9 +635,13 @@ class Sequelize { checkTransaction(); - return options.transaction - ? options.transaction.connection - : this.connectionManager.getConnection(options); + return (options.transaction ? + options.transaction.connection : + this.runHooks('beforeGetConnection', options, sql).then(() => this.connectionManager.getConnection(options))); + + // return options.transaction + // ? options.transaction.connection + // : this.connectionManager.getConnection(options); }).then(connection => { const query = new this.dialect.Query(connection, this, options); return this.runHooks('beforeQuery', options, query)