From 4038ce662f548d17d70a560617a823e4b73f94f9 Mon Sep 17 00:00:00 2001 From: Daniel Schwartz Date: Tue, 16 Feb 2021 19:01:52 -0500 Subject: [PATCH 01/10] fix(types): adds missing function signature to Model.prototype.previous() types --- types/lib/model.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/lib/model.d.ts b/types/lib/model.d.ts index 71a6c499acd1..a77cc13698f3 100644 --- a/types/lib/model.d.ts +++ b/types/lib/model.d.ts @@ -2708,6 +2708,7 @@ export abstract class Model; public previous(key: K): this[K]; /** From 2bd14a0b13e816f085804107da90b89a480addbe Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Thu, 4 Mar 2021 20:29:38 +0530 Subject: [PATCH 02/10] docs(geometry): update link (#13067 #12937) Co-authored-by: john gravois --- lib/data-types.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/data-types.js b/lib/data-types.js index 36e520f5f2f7..e96e334180c7 100644 --- a/lib/data-types.js +++ b/lib/data-types.js @@ -794,7 +794,7 @@ class ARRAY extends ABSTRACT { * In PostGIS, the GeoJSON is parsed using the PostGIS function `ST_GeomFromGeoJSON`. * In MySQL it is parsed using the function `GeomFromText`. * - * Therefore, one can just follow the [GeoJSON spec](http://geojson.org/geojson-spec.html) for handling geometry objects. See the following examples: + * Therefore, one can just follow the [GeoJSON spec](https://tools.ietf.org/html/rfc7946) for handling geometry objects. See the following examples: * * @example Defining a Geometry type attribute * DataTypes.GEOMETRY From b0394eeeeb540bf123f82b7b536bf20c2579801e Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Wed, 10 Mar 2021 08:25:20 +0530 Subject: [PATCH 03/10] docs(getters-setters-virtuals): fix typos (#13074 #12811 #12655) Co-authored-by: sliterok Co-authored-by: William Gurzoni --- docs/manual/core-concepts/getters-setters-virtuals.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/manual/core-concepts/getters-setters-virtuals.md b/docs/manual/core-concepts/getters-setters-virtuals.md index b171a02e156b..c280dc3ae344 100644 --- a/docs/manual/core-concepts/getters-setters-virtuals.md +++ b/docs/manual/core-concepts/getters-setters-virtuals.md @@ -15,7 +15,7 @@ const User = sequelize.define('user', { username: { type: DataTypes.STRING, get() { - const rawValue = this.getDataValue(username); + const rawValue = this.getDataValue('username'); return rawValue ? rawValue.toUpperCase() : null; } } @@ -27,10 +27,10 @@ This getter, just like a standard JavaScript getter, is called automatically whe ```js const user = User.build({ username: 'SuperUser123' }); console.log(user.username); // 'SUPERUSER123' -console.log(user.getDataValue(username)); // 'SuperUser123' +console.log(user.getDataValue('username')); // 'SuperUser123' ``` -Note that, although `SUPERUSER123` was logged above, the value truly stored in the database is still `SuperUser123`. We used `this.getDataValue(username)` to obtain this value, and converted it to uppercase. +Note that, although `SUPERUSER123` was logged above, the value truly stored in the database is still `SuperUser123`. We used `this.getDataValue('username')` to obtain this value, and converted it to uppercase. Had we tried to use `this.username` in the getter instead, we would have gotten an infinite loop! This is why Sequelize provides the `getDataValue` method. @@ -55,7 +55,7 @@ const User = sequelize.define('user', { ```js const user = User.build({ username: 'someone', password: 'NotSo§tr0ngP4$SW0RD!' }); console.log(user.password); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc' -console.log(user.getDataValue(password)); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc' +console.log(user.getDataValue('password')); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc' ``` Observe that Sequelize called the setter automatically, before even sending data to the database. The only data the database ever saw was the already hashed value. From e890e04aec063d01df02a63992395e2c7023cbd7 Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Wed, 10 Mar 2021 00:34:44 -0300 Subject: [PATCH 04/10] test(dropEnum): minor refactor (#13084) --- test/integration/query-interface/dropEnum.test.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/test/integration/query-interface/dropEnum.test.js b/test/integration/query-interface/dropEnum.test.js index 595d347162c1..136e30331a5c 100644 --- a/test/integration/query-interface/dropEnum.test.js +++ b/test/integration/query-interface/dropEnum.test.js @@ -19,18 +19,9 @@ describe(Support.getTestDialectTeaser('QueryInterface'), () => { describe('dropEnum', () => { beforeEach(async function() { await this.queryInterface.createTable('menus', { - structuretype: { - type: DataTypes.ENUM('menus', 'submenu', 'routine'), - allowNull: true - }, - sequence: { - type: DataTypes.INTEGER, - allowNull: true - }, - name: { - type: DataTypes.STRING, - allowNull: true - } + structuretype: DataTypes.ENUM('menus', 'submenu', 'routine'), + sequence: DataTypes.INTEGER, + name: DataTypes.STRING }); }); From ab769c5876d906d8d3b94811c84f8cc56525cdf1 Mon Sep 17 00:00:00 2001 From: Thomas Hoppe Date: Thu, 11 Mar 2021 14:06:53 +0100 Subject: [PATCH 05/10] chore: fix docker-compose volume declaration (#13089) --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 31ea58d455e4..cdda5c535eb4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,7 +53,7 @@ services: MYSQL_USER: sequelize_test MYSQL_PASSWORD: sequelize_test volumes: - - $MARIADB_ENTRYPOINT:/docker-entrypoint-initdb.d + - $MARIADB_ENTRYPOINT/:/docker-entrypoint-initdb.d ports: - "8960:3306" container_name: mariadb-103 @@ -67,7 +67,7 @@ services: MYSQL_USER: sequelize_test MYSQL_PASSWORD: sequelize_test volumes: - - $MYSQLDB_ENTRYPOINT:/docker-entrypoint-initdb.d + - $MYSQLDB_ENTRYPOINT/:/docker-entrypoint-initdb.d ports: - "8980:3306" container_name: mysql-57 From 661073970927b9de2940d74fb317c926be316332 Mon Sep 17 00:00:00 2001 From: Wong Yong Jie Date: Sat, 13 Mar 2021 13:23:54 +0800 Subject: [PATCH 06/10] fix(types): allow transaction to be `null` (#13093) --- types/lib/model.d.ts | 2 +- types/test/transaction.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/types/lib/model.d.ts b/types/lib/model.d.ts index a77cc13698f3..1b92a6afc781 100644 --- a/types/lib/model.d.ts +++ b/types/lib/model.d.ts @@ -35,7 +35,7 @@ export interface Transactionable { /** * Transaction to run query under */ - transaction?: Transaction; + transaction?: Transaction | null; } export interface SearchPathable { diff --git a/types/test/transaction.ts b/types/test/transaction.ts index 96bdb14574fd..b77cf12e4eaf 100644 --- a/types/test/transaction.ts +++ b/types/test/transaction.ts @@ -78,3 +78,9 @@ async function nestedTransact() { }); await tr.commit(); } + +async function excludeFromTransaction() { + await sequelize.transaction(async t => + await sequelize.query('SELECT 1', { transaction: null }) + ); +} From 172c95b9bf3177282db5643574ca46c8753a813c Mon Sep 17 00:00:00 2001 From: Pedro Augusto de Paula Barbosa Date: Sun, 14 Mar 2021 02:52:50 -0300 Subject: [PATCH 07/10] fix(mysql): release connection on deadlocks (#13102) * test(mysql, mariadb): improve transaction tests - Greatly improve test for `SELECT ... LOCK IN SHARE MODE` - Greatly improve test for deadlock handling * fix(mysql): release connection on deadlocks This is a follow-up for a problem not covered by #12841. * refactor(mariadb): `query.js` similar to mysql's * Update comments with a reference to this PR --- lib/dialects/mariadb/query.js | 36 +--- lib/dialects/mysql/query.js | 25 ++- package.json | 1 + test/integration/replication.test.js | 4 +- test/integration/transaction.test.js | 288 ++++++++++++++++++++++----- test/support.js | 16 +- 6 files changed, 281 insertions(+), 89 deletions(-) diff --git a/lib/dialects/mariadb/query.js b/lib/dialects/mariadb/query.js index d1dd48394e01..2f78761d91bc 100644 --- a/lib/dialects/mariadb/query.js +++ b/lib/dialects/mariadb/query.js @@ -7,6 +7,7 @@ const DataTypes = require('../../data-types'); const { logger } = require('../../utils/logger'); const ER_DUP_ENTRY = 1062; +const ER_DEADLOCK = 1213; const ER_ROW_IS_REFERENCED = 1451; const ER_NO_REFERENCED_ROW = 1452; @@ -46,40 +47,25 @@ class Query extends AbstractQuery { try { results = await connection.query(this.sql, parameters); - complete(); - - // Log warnings if we've got them. - if (showWarnings && results && results.warningStatus > 0) { - await this.logWarnings(results); - } - } catch (err) { - // MariaDB automatically rolls-back transactions in the event of a - // deadlock. - // - // Even though we shouldn't need to do this, we initiate a manual - // rollback. Without the rollback, the next transaction using the - // connection seems to retain properties of the previous transaction - // (e.g. isolation level) and not work as expected. - // - // For example (in our tests), a follow-up READ_COMMITTED transaction - // doesn't work as expected unless we explicitly rollback the - // transaction: it would fail to read a value inserted outside of that - // transaction. - if (options.transaction && err.errno === 1213) { + } catch (error) { + if (options.transaction && error.errno === ER_DEADLOCK) { + // MariaDB automatically rolls-back transactions in the event of a deadlock. + // However, we still initiate a manual rollback to ensure the connection gets released - see #13102. try { await options.transaction.rollback(); - } catch (err) { + } catch (error_) { // Ignore errors - since MariaDB automatically rolled back, we're // not that worried about this redundant rollback failing. } + options.transaction.finished = 'rollback'; } + error.sql = sql; + error.parameters = parameters; + throw this.formatError(error); + } finally { complete(); - - err.sql = sql; - err.parameters = parameters; - throw this.formatError(err); } if (showWarnings && results && results.warningStatus > 0) { diff --git a/lib/dialects/mysql/query.js b/lib/dialects/mysql/query.js index 41ea95ef29d8..a0745a799bcc 100644 --- a/lib/dialects/mysql/query.js +++ b/lib/dialects/mysql/query.js @@ -6,6 +6,7 @@ const _ = require('lodash'); const { logger } = require('../../utils/logger'); const ER_DUP_ENTRY = 1062; +const ER_DEADLOCK = 1213; const ER_ROW_IS_REFERENCED = 1451; const ER_NO_REFERENCED_ROW = 1452; @@ -57,19 +58,27 @@ class Query extends AbstractQuery { .setMaxListeners(100); }); } - } catch (err) { - // MySQL automatically rolls-back transactions in the event of a deadlock - if (options.transaction && err.errno === 1213) { + } catch (error) { + if (options.transaction && error.errno === ER_DEADLOCK) { + // MySQL automatically rolls-back transactions in the event of a deadlock. + // However, we still initiate a manual rollback to ensure the connection gets released - see #13102. + try { + await options.transaction.rollback(); + } catch (error_) { + // Ignore errors - since MySQL automatically rolled back, we're + // not that worried about this redundant rollback failing. + } + options.transaction.finished = 'rollback'; } - err.sql = sql; - err.parameters = parameters; - throw this.formatError(err); + error.sql = sql; + error.parameters = parameters; + throw this.formatError(error); + } finally { + complete(); } - complete(); - if (showWarnings && results && results.warningStatus > 0) { await this.logWarnings(results); } diff --git a/package.json b/package.json index 56987cebb78f..6d9004d3b9c1 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "nyc": "^15.0.0", "p-map": "^4.0.0", "p-props": "^4.0.0", + "p-settle": "^4.1.1", "p-timeout": "^4.0.0", "pg": "^8.2.1", "pg-hstore": "^2.x", diff --git a/test/integration/replication.test.js b/test/integration/replication.test.js index 65c1ba483351..1ba98bb12912 100644 --- a/test/integration/replication.test.js +++ b/test/integration/replication.test.js @@ -18,8 +18,8 @@ describe(Support.getTestDialectTeaser('Replication'), () => { this.sequelize = Support.getSequelizeInstance(null, null, null, { replication: { - write: Support.getConnectionOptions(), - read: [Support.getConnectionOptions()] + write: Support.getConnectionOptionsWithoutPool(), + read: [Support.getConnectionOptionsWithoutPool()] } }); diff --git a/test/integration/transaction.test.js b/test/integration/transaction.test.js index fce1e74a8468..d4605dca3caf 100644 --- a/test/integration/transaction.test.js +++ b/test/integration/transaction.test.js @@ -1,15 +1,14 @@ 'use strict'; -const chai = require('chai'), - expect = chai.expect, - Support = require('./support'), - dialect = Support.getTestDialect(), - Sequelize = require('../../index'), - QueryTypes = require('../../lib/query-types'), - Transaction = require('../../lib/transaction'), - sinon = require('sinon'), - current = Support.sequelize, - delay = require('delay'); +const chai = require('chai'); +const expect = chai.expect; +const Support = require('./support'); +const dialect = Support.getTestDialect(); +const { Sequelize, QueryTypes, DataTypes, Transaction } = require('../../index'); +const sinon = require('sinon'); +const current = Support.sequelize; +const delay = require('delay'); +const pSettle = require('p-settle'); if (current.dialect.supports.transactions) { @@ -493,7 +492,7 @@ if (current.dialect.supports.transactions) { expect(count).to.equal(2, 'transactions were fully rolled-back, and no new rows were added'); }); - it('should release the connection for a deadlocked transaction', async function() { + it('should release the connection for a deadlocked transaction (1/2)', async function() { const Task = await getAndInitializeTaskModel(this.sequelize); // 1 of 2 queries should deadlock and be rolled back by InnoDB @@ -523,6 +522,127 @@ if (current.dialect.supports.transactions) { } ); }); + + it('should release the connection for a deadlocked transaction (2/2)', async function() { + const verifyDeadlock = async () => { + const User = this.sequelize.define('user', { + username: DataTypes.STRING, + awesome: DataTypes.BOOLEAN + }, { timestamps: false }); + + await this.sequelize.sync({ force: true }); + const { id } = await User.create({ username: 'jan' }); + + // First, we start a transaction T1 and perform a SELECT with it using the `LOCK.SHARE` mode (setting a shared mode lock on the row). + // This will cause other sessions to be able to read the row but not modify it. + // So, if another transaction tries to update those same rows, it will wait until T1 commits (or rolls back). + // https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html + const t1 = await this.sequelize.transaction(); + const t1Jan = await User.findByPk(id, { lock: t1.LOCK.SHARE, transaction: t1 }); + + // Then we start another transaction T2 and see that it can indeed read the same row. + const t2 = await this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED }); + const t2Jan = await User.findByPk(id, { transaction: t2 }); + + // Then, we want to see that an attempt to update that row from T2 will be queued until T1 commits. + // However, before commiting T1 we will also perform an update via T1 on the same rows. + // This should cause T2 to notice that it can't function anymore, so it detects a deadlock and automatically rolls itself back (and throws an error). + // Meanwhile, T1 should still be ok. + const executionOrder = []; + const [t2AttemptData, t1AttemptData] = await pSettle([ + (async () => { + try { + executionOrder.push('Begin attempt to update via T2'); + await t2Jan.update({ awesome: false }, { transaction: t2 }); + executionOrder.push('Done updating via T2'); // Shouldn't happen + } catch (error) { + executionOrder.push('Failed to update via T2'); + throw error; + } + + await delay(30); + + try { + // We shouldn't reach this point, but if we do, let's at least commit the transaction + // to avoid forever occupying one connection of the pool with a pending transaction. + executionOrder.push('Attempting to commit T2'); + await t2.commit(); + executionOrder.push('Done committing T2'); + } catch { + executionOrder.push('Failed to commit T2'); + } + })(), + (async () => { + await delay(100); + + try { + executionOrder.push('Begin attempt to update via T1'); + await t1Jan.update({ awesome: true }, { transaction: t1 }); + executionOrder.push('Done updating via T1'); + } catch (error) { + executionOrder.push('Failed to update via T1'); // Shouldn't happen + throw error; + } + + await delay(150); + + try { + executionOrder.push('Attempting to commit T1'); + await t1.commit(); + executionOrder.push('Done committing T1'); + } catch { + executionOrder.push('Failed to commit T1'); // Shouldn't happen + } + })() + ]); + + expect(t1AttemptData.isFulfilled).to.be.true; + expect(t2AttemptData.isRejected).to.be.true; + expect(t2AttemptData.reason.message).to.include('Deadlock found when trying to get lock; try restarting transaction'); + expect(t1.finished).to.equal('commit'); + expect(t2.finished).to.equal('rollback'); + + const expectedExecutionOrder = [ + 'Begin attempt to update via T2', + 'Begin attempt to update via T1', // 100ms after + 'Done updating via T1', // right after + 'Failed to update via T2', // right after + 'Attempting to commit T1', // 150ms after + 'Done committing T1' // right after + ]; + + // The order things happen in the database must be the one shown above. However, sometimes it can happen that + // the calls in the JavaScript event loop that are communicating with the database do not match exactly this order. + // In particular, it is possible that the JS event loop logs `'Failed to update via T2'` before logging `'Done updating via T1'`, + // even though the database updated T1 first (and then rushed to declare a deadlock for T2). + + const anotherAcceptableExecutionOrderFromJSPerspective = [ + 'Begin attempt to update via T2', + 'Begin attempt to update via T1', // 100ms after + 'Failed to update via T2', // right after + 'Done updating via T1', // right after + 'Attempting to commit T1', // 150ms after + 'Done committing T1' // right after + ]; + + const executionOrderOk = Support.isDeepEqualToOneOf( + executionOrder, + [ + expectedExecutionOrder, + anotherAcceptableExecutionOrderFromJSPerspective + ] + ); + + if (!executionOrderOk) { + throw new Error(`Unexpected execution order: ${executionOrder.join(' > ')}`); + } + }; + + for (let i = 0; i < 3 * Support.getPoolMax(); i++) { + await verifyDeadlock(); + await delay(10); + } + }); }); } @@ -916,55 +1036,123 @@ if (current.dialect.supports.transactions) { }); } - it('supports for share', async function() { - const User = this.sequelize.define('user', { - username: Support.Sequelize.STRING, - awesome: Support.Sequelize.BOOLEAN - }, { timestamps: false }); + it('supports for share (i.e. `SELECT ... LOCK IN SHARE MODE`)', async function() { + const verifySelectLockInShareMode = async () => { + const User = this.sequelize.define('user', { + username: DataTypes.STRING, + awesome: DataTypes.BOOLEAN + }, { timestamps: false }); - const t1CommitSpy = sinon.spy(); - const t2FindSpy = sinon.spy(); - const t2UpdateSpy = sinon.spy(); + await this.sequelize.sync({ force: true }); + const { id } = await User.create({ username: 'jan' }); - await this.sequelize.sync({ force: true }); - const user = await User.create({ username: 'jan' }); + // First, we start a transaction T1 and perform a SELECT with it using the `LOCK.SHARE` mode (setting a shared mode lock on the row). + // This will cause other sessions to be able to read the row but not modify it. + // So, if another transaction tries to update those same rows, it will wait until T1 commits (or rolls back). + // https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html + const t1 = await this.sequelize.transaction(); + await User.findByPk(id, { lock: t1.LOCK.SHARE, transaction: t1 }); - const t1 = await this.sequelize.transaction(); - const t1Jan = await User.findByPk(user.id, { - lock: t1.LOCK.SHARE, - transaction: t1 - }); + // Then we start another transaction T2 and see that it can indeed read the same row. + const t2 = await this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED }); + const t2Jan = await User.findByPk(id, { transaction: t2 }); - const t2 = await this.sequelize.transaction({ - isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED - }); + // Then, we want to see that an attempt to update that row from T2 will be queued until T1 commits. + const executionOrder = []; + const [t2AttemptData, t1AttemptData] = await pSettle([ + (async () => { + try { + executionOrder.push('Begin attempt to update via T2'); + await t2Jan.update({ awesome: false }, { transaction: t2 }); + executionOrder.push('Done updating via T2'); + } catch (error) { + executionOrder.push('Failed to update via T2'); // Shouldn't happen + throw error; + } - await Promise.all([ - (async () => { - const t2Jan = await User.findByPk(user.id, { - transaction: t2 - }); + await delay(30); - t2FindSpy(); + try { + executionOrder.push('Attempting to commit T2'); + await t2.commit(); + executionOrder.push('Done committing T2'); + } catch { + executionOrder.push('Failed to commit T2'); // Shouldn't happen + } + })(), + (async () => { + await delay(100); - await t2Jan.update({ awesome: false }, { transaction: t2 }); - t2UpdateSpy(); + try { + executionOrder.push('Begin attempt to read via T1'); + await User.findAll({ transaction: t1 }); + executionOrder.push('Done reading via T1'); + } catch (error) { + executionOrder.push('Failed to read via T1'); // Shouldn't happen + throw error; + } - await t2.commit(); - })(), - (async () => { - await t1Jan.update({ awesome: true }, { transaction: t1 }); - await delay(2000); - t1CommitSpy(); - await t1.commit(); - })() - ]); + await delay(150); - // (t2) find call should have returned before (t1) commit - expect(t2FindSpy).to.have.been.calledBefore(t1CommitSpy); + try { + executionOrder.push('Attempting to commit T1'); + await t1.commit(); + executionOrder.push('Done committing T1'); + } catch { + executionOrder.push('Failed to commit T1'); // Shouldn't happen + } + })() + ]); + + expect(t1AttemptData.isFulfilled).to.be.true; + expect(t2AttemptData.isFulfilled).to.be.true; + expect(t1.finished).to.equal('commit'); + expect(t2.finished).to.equal('commit'); + + const expectedExecutionOrder = [ + 'Begin attempt to update via T2', + 'Begin attempt to read via T1', // 100ms after + 'Done reading via T1', // right after + 'Attempting to commit T1', // 150ms after + 'Done committing T1', // right after + 'Done updating via T2', // right after + 'Attempting to commit T2', // 30ms after + 'Done committing T2' // right after + ]; + + // The order things happen in the database must be the one shown above. However, sometimes it can happen that + // the calls in the JavaScript event loop that are communicating with the database do not match exactly this order. + // In particular, it is possible that the JS event loop logs `'Done updating via T2'` before logging `'Done committing T1'`, + // even though the database committed T1 first (and then rushed to complete the pending update query from T2). + + const anotherAcceptableExecutionOrderFromJSPerspective = [ + 'Begin attempt to update via T2', + 'Begin attempt to read via T1', // 100ms after + 'Done reading via T1', // right after + 'Attempting to commit T1', // 150ms after + 'Done updating via T2', // right after + 'Done committing T1', // right after + 'Attempting to commit T2', // 30ms after + 'Done committing T2' // right after + ]; + + const executionOrderOk = Support.isDeepEqualToOneOf( + executionOrder, + [ + expectedExecutionOrder, + anotherAcceptableExecutionOrderFromJSPerspective + ] + ); - // But (t2) update call should not happen before (t1) commit - expect(t2UpdateSpy).to.have.been.calledAfter(t1CommitSpy); + if (!executionOrderOk) { + throw new Error(`Unexpected execution order: ${executionOrder.join(' > ')}`); + } + }; + + for (let i = 0; i < 3 * Support.getPoolMax(); i++) { + await verifySelectLockInShareMode(); + await delay(10); + } }); }); } diff --git a/test/support.js b/test/support.js index 2fe0f36f402e..c2d721df9079 100644 --- a/test/support.js +++ b/test/support.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); +const { isDeepStrictEqual } = require('util'); const _ = require('lodash'); const Sequelize = require('../index'); const Config = require('./config/config'); @@ -119,11 +120,10 @@ const Support = { return this.getSequelizeInstance(config.database, config.username, config.password, sequelizeOptions); }, - getConnectionOptions() { - const config = Config[this.getTestDialect()]; - + getConnectionOptionsWithoutPool() { + // Do not break existing config object - shallow clone before `delete config.pool` + const config = { ...Config[this.getTestDialect()] }; delete config.pool; - return config; }, @@ -207,6 +207,10 @@ const Support = { return `[${dialect.toUpperCase()}] ${moduleName}`; }, + getPoolMax() { + return Config[this.getTestDialect()].pool.max; + }, + expectsql(query, assertions) { const expectations = assertions.query || assertions; let expectation = expectations[Support.sequelize.dialect.name]; @@ -234,6 +238,10 @@ const Support = { const bind = assertions.bind[Support.sequelize.dialect.name] || assertions.bind['default'] || assertions.bind; expect(query.bind).to.deep.equal(bind); } + }, + + isDeepEqualToOneOf(actual, expectedOptions) { + return expectedOptions.some(expected => isDeepStrictEqual(actual, expected)); } }; From 6cedb00d480fec9752da2513dc2e603556ec4652 Mon Sep 17 00:00:00 2001 From: Daniel Schwartz Date: Mon, 15 Mar 2021 13:30:08 -0400 Subject: [PATCH 08/10] fix(types): update function signature to Partial --- types/lib/model.d.ts | 315 ++++++++++++++++++++++++++++--------------- 1 file changed, 209 insertions(+), 106 deletions(-) diff --git a/types/lib/model.d.ts b/types/lib/model.d.ts index 1b92a6afc781..2a1ccced2835 100644 --- a/types/lib/model.d.ts +++ b/types/lib/model.d.ts @@ -1518,9 +1518,14 @@ export interface AddScopeOptions { override: boolean; } -export abstract class Model - extends Hooks, TModelAttributes, TCreationAttributes> -{ +export abstract class Model< + TModelAttributes extends {} = any, + TCreationAttributes extends {} = TModelAttributes +> extends Hooks< + Model, + TModelAttributes, + TCreationAttributes +> { /** * A dummy variable that doesn't exist on the real object. This exists so * Typescript can infer the type of the attributes in static functions. Don't @@ -1571,7 +1576,9 @@ export abstract class Model( this: ModelStatic, - attributes: ModelAttributes, options: InitOptions + attributes: ModelAttributes, + options: InitOptions ): Model; /** @@ -1659,7 +1667,7 @@ export abstract class Model, schema: string, options?: SchemaOptions - ): { new(): M } & typeof Model; + ): { new (): M } & typeof Model; /** * Get the tablename of the model, taking schema into account. The method will return The name as a string @@ -1670,11 +1678,13 @@ export abstract class Model( this: ModelStatic, - options?: string | ScopeOptions | readonly (string | ScopeOptions)[] | WhereAttributeHash + options?: + | string + | ScopeOptions + | readonly (string | ScopeOptions)[] + | WhereAttributeHash ): ModelCtor; /** @@ -1741,13 +1755,13 @@ export abstract class Model( this: ModelStatic, name: string, - scope: FindOptions, + scope: FindOptions, options?: AddScopeOptions ): void; public static addScope( this: ModelStatic, name: string, - scope: (...args: readonly any[]) => FindOptions, + scope: (...args: readonly any[]) => FindOptions, options?: AddScopeOptions ): void; @@ -1815,7 +1829,8 @@ export abstract class Model( this: ModelStatic, - options?: FindOptions): Promise; + options?: FindOptions + ): Promise; /** * Search for a single instance by its primary key. This applies LIMIT 1, so the listener will @@ -1824,12 +1839,12 @@ export abstract class Model( this: ModelStatic, identifier: Identifier, - options: Omit, 'where'> + options: Omit, "where"> ): Promise; public static findByPk( this: ModelStatic, identifier?: Identifier, - options?: Omit, 'where'> + options?: Omit, "where"> ): Promise; /** @@ -1837,11 +1852,11 @@ export abstract class Model( this: ModelStatic, - options: NonNullFindOptions + options: NonNullFindOptions ): Promise; public static findOne( this: ModelStatic, - options?: FindOptions + options?: FindOptions ): Promise; /** @@ -1855,9 +1870,9 @@ export abstract class Model( this: ModelStatic, - field: keyof M['_attributes'] | '*', + field: keyof M["_attributes"] | "*", aggregateFunction: string, - options?: AggregateOptions + options?: AggregateOptions ): Promise; /** @@ -1865,7 +1880,7 @@ export abstract class Model( this: ModelStatic, - options: CountWithOptions + options: CountWithOptions ): Promise<{ [key: string]: number }>; /** @@ -1875,7 +1890,7 @@ export abstract class Model( this: ModelStatic, - options?: CountOptions + options?: CountOptions ): Promise; /** @@ -1915,7 +1930,7 @@ export abstract class Model( this: ModelStatic, - options?: FindAndCountOptions + options?: FindAndCountOptions ): Promise<{ rows: M[]; count: number }>; /** @@ -1923,8 +1938,8 @@ export abstract class Model( this: ModelStatic, - field: keyof M['_attributes'], - options?: AggregateOptions + field: keyof M["_attributes"], + options?: AggregateOptions ): Promise; /** @@ -1932,8 +1947,8 @@ export abstract class Model( this: ModelStatic, - field: keyof M['_attributes'], - options?: AggregateOptions + field: keyof M["_attributes"], + options?: AggregateOptions ): Promise; /** @@ -1941,8 +1956,8 @@ export abstract class Model( this: ModelStatic, - field: keyof M['_attributes'], - options?: AggregateOptions + field: keyof M["_attributes"], + options?: AggregateOptions ): Promise; /** @@ -1950,7 +1965,7 @@ export abstract class Model( this: ModelStatic, - record?: M['_creationAttributes'], + record?: M["_creationAttributes"], options?: BuildOptions ): M; @@ -1959,7 +1974,7 @@ export abstract class Model( this: ModelStatic, - records: ReadonlyArray, + records: ReadonlyArray, options?: BuildOptions ): M[]; @@ -1968,13 +1983,13 @@ export abstract class Model( this: ModelStatic, - values?: M['_creationAttributes'], - options?: CreateOptions + values?: M["_creationAttributes"], + options?: CreateOptions ): Promise; public static create( this: ModelStatic, - values: M['_creationAttributes'], - options: CreateOptions & { returning: false } + values: M["_creationAttributes"], + options: CreateOptions & { returning: false } ): Promise; /** @@ -1983,7 +1998,7 @@ export abstract class Model( this: ModelStatic, - options: FindOrCreateOptions + options: FindOrCreateOptions ): Promise<[M, boolean]>; /** @@ -1999,7 +2014,7 @@ export abstract class Model( this: ModelStatic, - options: FindOrCreateOptions + options: FindOrCreateOptions ): Promise<[M, boolean]>; /** @@ -2008,7 +2023,7 @@ export abstract class Model( this: ModelStatic, - options: FindOrCreateOptions + options: FindOrCreateOptions ): Promise<[M, boolean]>; /** @@ -2032,8 +2047,8 @@ export abstract class Model( this: ModelStatic, - values: M['_creationAttributes'], - options?: UpsertOptions + values: M["_creationAttributes"], + options?: UpsertOptions ): Promise<[M, boolean | null]>; /** @@ -2049,8 +2064,8 @@ export abstract class Model( this: ModelStatic, - records: ReadonlyArray, - options?: BulkCreateOptions + records: ReadonlyArray, + options?: BulkCreateOptions ): Promise; /** @@ -2058,7 +2073,7 @@ export abstract class Model( this: ModelStatic, - options?: TruncateOptions + options?: TruncateOptions ): Promise; /** @@ -2068,7 +2083,7 @@ export abstract class Model( this: ModelStatic, - options?: DestroyOptions + options?: DestroyOptions ): Promise; /** @@ -2076,7 +2091,7 @@ export abstract class Model( this: ModelStatic, - options?: RestoreOptions + options?: RestoreOptions ): Promise; /** @@ -2086,8 +2101,8 @@ export abstract class Model( this: ModelStatic, - values: Partial, - options: UpdateOptions + values: Partial, + options: UpdateOptions ): Promise<[number, M[]]>; /** @@ -2095,8 +2110,8 @@ export abstract class Model( this: ModelStatic, - field: keyof M['_attributes'], - options: IncrementDecrementOptionsWithBy + field: keyof M["_attributes"], + options: IncrementDecrementOptionsWithBy ): Promise; /** @@ -2104,8 +2119,8 @@ export abstract class Model( this: ModelStatic, - fields: ReadonlyArray, - options: IncrementDecrementOptionsWithBy + fields: ReadonlyArray, + options: IncrementDecrementOptionsWithBy ): Promise; /** @@ -2113,8 +2128,8 @@ export abstract class Model( this: ModelStatic, - fields: { [key in keyof M['_attributes']]?: number }, - options: IncrementDecrementOptions + fields: { [key in keyof M["_attributes"]]?: number }, + options: IncrementDecrementOptions ): Promise; /** @@ -2169,11 +2184,11 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instance: M, options: CreateOptions) => HookReturn + fn: (instance: M, options: CreateOptions) => HookReturn ): void; public static beforeCreate( this: ModelStatic, - fn: (instance: M, options: CreateOptions) => HookReturn + fn: (instance: M, options: CreateOptions) => HookReturn ): void; /** @@ -2185,11 +2200,11 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instance: M, options: CreateOptions) => HookReturn + fn: (instance: M, options: CreateOptions) => HookReturn ): void; public static afterCreate( this: ModelStatic, - fn: (instance: M, options: CreateOptions) => HookReturn + fn: (instance: M, options: CreateOptions) => HookReturn ): void; /** @@ -2233,11 +2248,11 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instance: M, options: UpdateOptions) => HookReturn + fn: (instance: M, options: UpdateOptions) => HookReturn ): void; public static beforeUpdate( this: ModelStatic, - fn: (instance: M, options: UpdateOptions) => HookReturn + fn: (instance: M, options: UpdateOptions) => HookReturn ): void; /** @@ -2249,11 +2264,11 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instance: M, options: UpdateOptions) => HookReturn + fn: (instance: M, options: UpdateOptions) => HookReturn ): void; public static afterUpdate( this: ModelStatic, - fn: (instance: M, options: UpdateOptions) => HookReturn + fn: (instance: M, options: UpdateOptions) => HookReturn ): void; /** @@ -2265,11 +2280,17 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instance: M, options: UpdateOptions | SaveOptions) => HookReturn + fn: ( + instance: M, + options: UpdateOptions | SaveOptions + ) => HookReturn ): void; public static beforeSave( this: ModelStatic, - fn: (instance: M, options: UpdateOptions | SaveOptions) => HookReturn + fn: ( + instance: M, + options: UpdateOptions | SaveOptions + ) => HookReturn ): void; /** @@ -2281,11 +2302,17 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instance: M, options: UpdateOptions | SaveOptions) => HookReturn + fn: ( + instance: M, + options: UpdateOptions | SaveOptions + ) => HookReturn ): void; public static afterSave( this: ModelStatic, - fn: (instance: M, options: UpdateOptions | SaveOptions) => HookReturn + fn: ( + instance: M, + options: UpdateOptions | SaveOptions + ) => HookReturn ): void; /** @@ -2297,11 +2324,17 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instances: M[], options: BulkCreateOptions) => HookReturn + fn: ( + instances: M[], + options: BulkCreateOptions + ) => HookReturn ): void; public static beforeBulkCreate( this: ModelStatic, - fn: (instances: M[], options: BulkCreateOptions) => HookReturn + fn: ( + instances: M[], + options: BulkCreateOptions + ) => HookReturn ): void; /** @@ -2313,11 +2346,17 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instances: readonly M[], options: BulkCreateOptions) => HookReturn + fn: ( + instances: readonly M[], + options: BulkCreateOptions + ) => HookReturn ): void; public static afterBulkCreate( this: ModelStatic, - fn: (instances: readonly M[], options: BulkCreateOptions) => HookReturn + fn: ( + instances: readonly M[], + options: BulkCreateOptions + ) => HookReturn ): void; /** @@ -2328,10 +2367,12 @@ export abstract class Model( this: ModelStatic, - name: string, fn: (options: BulkCreateOptions) => HookReturn): void; + name: string, + fn: (options: BulkCreateOptions) => HookReturn + ): void; public static beforeBulkDestroy( this: ModelStatic, - fn: (options: BulkCreateOptions) => HookReturn + fn: (options: BulkCreateOptions) => HookReturn ): void; /** @@ -2342,11 +2383,12 @@ export abstract class Model( this: ModelStatic, - name: string, fn: (options: DestroyOptions) => HookReturn + name: string, + fn: (options: DestroyOptions) => HookReturn ): void; public static afterBulkDestroy( this: ModelStatic, - fn: (options: DestroyOptions) => HookReturn + fn: (options: DestroyOptions) => HookReturn ): void; /** @@ -2357,11 +2399,12 @@ export abstract class Model( this: ModelStatic, - name: string, fn: (options: UpdateOptions) => HookReturn + name: string, + fn: (options: UpdateOptions) => HookReturn ): void; public static beforeBulkUpdate( this: ModelStatic, - fn: (options: UpdateOptions) => HookReturn + fn: (options: UpdateOptions) => HookReturn ): void; /** @@ -2372,11 +2415,12 @@ export abstract class Model( this: ModelStatic, - name: string, fn: (options: UpdateOptions) => HookReturn + name: string, + fn: (options: UpdateOptions) => HookReturn ): void; public static afterBulkUpdate( this: ModelStatic, - fn: (options: UpdateOptions) => HookReturn + fn: (options: UpdateOptions) => HookReturn ): void; /** @@ -2387,11 +2431,12 @@ export abstract class Model( this: ModelStatic, - name: string, fn: (options: FindOptions) => HookReturn + name: string, + fn: (options: FindOptions) => HookReturn ): void; public static beforeFind( this: ModelStatic, - fn: (options: FindOptions) => HookReturn + fn: (options: FindOptions) => HookReturn ): void; /** @@ -2402,11 +2447,12 @@ export abstract class Model( this: ModelStatic, - name: string, fn: (options: CountOptions) => HookReturn + name: string, + fn: (options: CountOptions) => HookReturn ): void; public static beforeCount( this: ModelStatic, - fn: (options: CountOptions) => HookReturn + fn: (options: CountOptions) => HookReturn ): void; /** @@ -2417,11 +2463,12 @@ export abstract class Model( this: ModelStatic, - name: string, fn: (options: FindOptions) => HookReturn + name: string, + fn: (options: FindOptions) => HookReturn ): void; public static beforeFindAfterExpandIncludeAll( this: ModelStatic, - fn: (options: FindOptions) => HookReturn + fn: (options: FindOptions) => HookReturn ): void; /** @@ -2432,11 +2479,12 @@ export abstract class Model( this: ModelStatic, - name: string, fn: (options: FindOptions) => HookReturn + name: string, + fn: (options: FindOptions) => HookReturn ): void; public static beforeFindAfterOptions( this: ModelStatic, - fn: (options: FindOptions) => void + fn: (options: FindOptions) => void ): HookReturn; /** @@ -2448,39 +2496,57 @@ export abstract class Model( this: ModelStatic, name: string, - fn: (instancesOrInstance: readonly M[] | M | null, options: FindOptions) => HookReturn + fn: ( + instancesOrInstance: readonly M[] | M | null, + options: FindOptions + ) => HookReturn ): void; public static afterFind( this: ModelStatic, - fn: (instancesOrInstance: readonly M[] | M | null, options: FindOptions) => HookReturn + fn: ( + instancesOrInstance: readonly M[] | M | null, + options: FindOptions + ) => HookReturn ): void; /** * A hook that is run before sequelize.sync call * @param fn A callback function that is called with options passed to sequelize.sync */ - public static beforeBulkSync(name: string, fn: (options: SyncOptions) => HookReturn): void; + public static beforeBulkSync( + name: string, + fn: (options: SyncOptions) => HookReturn + ): void; public static beforeBulkSync(fn: (options: SyncOptions) => HookReturn): void; /** * A hook that is run after sequelize.sync call * @param fn A callback function that is called with options passed to sequelize.sync */ - public static afterBulkSync(name: string, fn: (options: SyncOptions) => HookReturn): void; + public static afterBulkSync( + name: string, + fn: (options: SyncOptions) => HookReturn + ): void; public static afterBulkSync(fn: (options: SyncOptions) => HookReturn): void; /** * A hook that is run before Model.sync call * @param fn A callback function that is called with options passed to Model.sync */ - public static beforeSync(name: string, fn: (options: SyncOptions) => HookReturn): void; + public static beforeSync( + name: string, + fn: (options: SyncOptions) => HookReturn + ): void; public static beforeSync(fn: (options: SyncOptions) => HookReturn): void; /** * A hook that is run after Model.sync call * @param fn A callback function that is called with options passed to Model.sync */ - public static afterSync(name: string, fn: (options: SyncOptions) => HookReturn): void; + public static afterSync( + name: string, + fn: (options: SyncOptions) => HookReturn + ): void; public static afterSync(fn: (options: SyncOptions) => HookReturn): void; /** @@ -2493,7 +2559,9 @@ export abstract class Model( - this: ModelStatic, target: ModelStatic, options?: HasOneOptions + this: ModelStatic, + target: ModelStatic, + options?: HasOneOptions ): HasOne; /** @@ -2506,7 +2574,9 @@ export abstract class Model( - this: ModelStatic, target: ModelStatic, options?: BelongsToOptions + this: ModelStatic, + target: ModelStatic, + options?: BelongsToOptions ): BelongsTo; /** @@ -2563,7 +2633,9 @@ export abstract class Model( - this: ModelStatic, target: ModelStatic, options?: HasManyOptions + this: ModelStatic, + target: ModelStatic, + options?: HasManyOptions ): HasMany; /** @@ -2616,7 +2688,9 @@ export abstract class Model( - this: ModelStatic, target: ModelStatic, options: BelongsToManyOptions + this: ModelStatic, + target: ModelStatic, + options: BelongsToManyOptions ): BelongsToMany; /** @@ -2643,12 +2717,17 @@ export abstract class Model(key: K): TModelAttributes[K]; + public getDataValue( + key: K + ): TModelAttributes[K]; /** * Update the underlying data value */ - public setDataValue(key: K, value: TModelAttributes[K]): void; + public setDataValue( + key: K, + value: TModelAttributes[K] + ): void; /** * If no key is given, returns all values of the instance, also invoking virtual getters. @@ -2659,8 +2738,14 @@ export abstract class Model(key: K, options?: { plain?: boolean; clone?: boolean }): this[K]; - public get(key: string, options?: { plain?: boolean; clone?: boolean }): unknown; + public get( + key: K, + options?: { plain?: boolean; clone?: boolean } + ): this[K]; + public get( + key: string, + options?: { plain?: boolean; clone?: boolean } + ): unknown; /** * Set is used to update values on the instance (the sequelize representation of the instance that is, @@ -2686,10 +2771,21 @@ export abstract class Model(key: K, value: TModelAttributes[K], options?: SetOptions): this; + public set( + key: K, + value: TModelAttributes[K], + options?: SetOptions + ): this; public set(keys: Partial, options?: SetOptions): this; - public setAttributes(key: K, value: TModelAttributes[K], options?: SetOptions): this; - public setAttributes(keys: Partial, options?: SetOptions): this; + public setAttributes( + key: K, + value: TModelAttributes[K], + options?: SetOptions + ): this; + public setAttributes( + keys: Partial, + options?: SetOptions + ): this; /** * If changed is called with a string it will return a boolean indicating whether the value of that key in @@ -2708,7 +2804,7 @@ export abstract class Model; + public previous(): Partial; public previous(key: K): this[K]; /** @@ -2743,8 +2839,15 @@ export abstract class Model(key: K, value: this[K], options?: InstanceUpdateOptions): Promise; - public update(keys: object, options?: InstanceUpdateOptions): Promise; + public update( + key: K, + value: this[K], + options?: InstanceUpdateOptions + ): Promise; + public update( + keys: object, + options?: InstanceUpdateOptions + ): Promise; /** * Destroy the row corresponding to this instance. Depending on your setting for paranoid, the row will From 719a97e201b589d6a5483981ac5554148420cd0e Mon Sep 17 00:00:00 2001 From: Daniel Schwartz Date: Mon, 15 Mar 2021 13:49:30 -0400 Subject: [PATCH 09/10] fix(types): fix auto formatting issue --- types/lib/model.d.ts | 313 +++++++++++++++---------------------------- 1 file changed, 105 insertions(+), 208 deletions(-) diff --git a/types/lib/model.d.ts b/types/lib/model.d.ts index 2a1ccced2835..897535854554 100644 --- a/types/lib/model.d.ts +++ b/types/lib/model.d.ts @@ -1518,14 +1518,9 @@ export interface AddScopeOptions { override: boolean; } -export abstract class Model< - TModelAttributes extends {} = any, - TCreationAttributes extends {} = TModelAttributes -> extends Hooks< - Model, - TModelAttributes, - TCreationAttributes -> { +export abstract class Model + extends Hooks, TModelAttributes, TCreationAttributes> +{ /** * A dummy variable that doesn't exist on the real object. This exists so * Typescript can infer the type of the attributes in static functions. Don't @@ -1576,9 +1571,7 @@ export abstract class Model< /** * The attributes of the model */ - public static readonly rawAttributes: { - [attribute: string]: ModelAttributeColumnOptions; - }; + public static readonly rawAttributes: { [attribute: string]: ModelAttributeColumnOptions }; /** * Reference to the sequelize instance the model was initialized with @@ -1630,8 +1623,7 @@ export abstract class Model< */ public static init( this: ModelStatic, - attributes: ModelAttributes, - options: InitOptions + attributes: ModelAttributes, options: InitOptions ): Model; /** @@ -1667,7 +1659,7 @@ export abstract class Model< this: ModelStatic, schema: string, options?: SchemaOptions - ): { new (): M } & typeof Model; + ): { new(): M } & typeof Model; /** * Get the tablename of the model, taking schema into account. The method will return The name as a string @@ -1678,13 +1670,11 @@ export abstract class Model< * (eg. * subscribers_1, subscribers_2) */ - public static getTableName(): - | string - | { - tableName: string; - schema: string; - delimiter: string; - }; + public static getTableName(): string | { + tableName: string; + schema: string; + delimiter: string; + }; /** * Apply a scope created in `define` to the model. First let's look at how to create scopes: @@ -1737,11 +1727,7 @@ export abstract class Model< */ public static scope( this: ModelStatic, - options?: - | string - | ScopeOptions - | readonly (string | ScopeOptions)[] - | WhereAttributeHash + options?: string | ScopeOptions | readonly (string | ScopeOptions)[] | WhereAttributeHash ): ModelCtor; /** @@ -1755,13 +1741,13 @@ export abstract class Model< public static addScope( this: ModelStatic, name: string, - scope: FindOptions, + scope: FindOptions, options?: AddScopeOptions ): void; public static addScope( this: ModelStatic, name: string, - scope: (...args: readonly any[]) => FindOptions, + scope: (...args: readonly any[]) => FindOptions, options?: AddScopeOptions ): void; @@ -1829,8 +1815,7 @@ export abstract class Model< */ public static findAll( this: ModelStatic, - options?: FindOptions - ): Promise; + options?: FindOptions): Promise; /** * Search for a single instance by its primary key. This applies LIMIT 1, so the listener will @@ -1839,12 +1824,12 @@ export abstract class Model< public static findByPk( this: ModelStatic, identifier: Identifier, - options: Omit, "where"> + options: Omit, 'where'> ): Promise; public static findByPk( this: ModelStatic, identifier?: Identifier, - options?: Omit, "where"> + options?: Omit, 'where'> ): Promise; /** @@ -1852,11 +1837,11 @@ export abstract class Model< */ public static findOne( this: ModelStatic, - options: NonNullFindOptions + options: NonNullFindOptions ): Promise; public static findOne( this: ModelStatic, - options?: FindOptions + options?: FindOptions ): Promise; /** @@ -1870,9 +1855,9 @@ export abstract class Model< */ public static aggregate( this: ModelStatic, - field: keyof M["_attributes"] | "*", + field: keyof M['_attributes'] | '*', aggregateFunction: string, - options?: AggregateOptions + options?: AggregateOptions ): Promise; /** @@ -1880,7 +1865,7 @@ export abstract class Model< */ public static count( this: ModelStatic, - options: CountWithOptions + options: CountWithOptions ): Promise<{ [key: string]: number }>; /** @@ -1890,7 +1875,7 @@ export abstract class Model< */ public static count( this: ModelStatic, - options?: CountOptions + options?: CountOptions ): Promise; /** @@ -1930,7 +1915,7 @@ export abstract class Model< */ public static findAndCountAll( this: ModelStatic, - options?: FindAndCountOptions + options?: FindAndCountOptions ): Promise<{ rows: M[]; count: number }>; /** @@ -1938,8 +1923,8 @@ export abstract class Model< */ public static max( this: ModelStatic, - field: keyof M["_attributes"], - options?: AggregateOptions + field: keyof M['_attributes'], + options?: AggregateOptions ): Promise; /** @@ -1947,8 +1932,8 @@ export abstract class Model< */ public static min( this: ModelStatic, - field: keyof M["_attributes"], - options?: AggregateOptions + field: keyof M['_attributes'], + options?: AggregateOptions ): Promise; /** @@ -1956,8 +1941,8 @@ export abstract class Model< */ public static sum( this: ModelStatic, - field: keyof M["_attributes"], - options?: AggregateOptions + field: keyof M['_attributes'], + options?: AggregateOptions ): Promise; /** @@ -1965,7 +1950,7 @@ export abstract class Model< */ public static build( this: ModelStatic, - record?: M["_creationAttributes"], + record?: M['_creationAttributes'], options?: BuildOptions ): M; @@ -1974,7 +1959,7 @@ export abstract class Model< */ public static bulkBuild( this: ModelStatic, - records: ReadonlyArray, + records: ReadonlyArray, options?: BuildOptions ): M[]; @@ -1983,13 +1968,13 @@ export abstract class Model< */ public static create( this: ModelStatic, - values?: M["_creationAttributes"], - options?: CreateOptions + values?: M['_creationAttributes'], + options?: CreateOptions ): Promise; public static create( this: ModelStatic, - values: M["_creationAttributes"], - options: CreateOptions & { returning: false } + values: M['_creationAttributes'], + options: CreateOptions & { returning: false } ): Promise; /** @@ -1998,7 +1983,7 @@ export abstract class Model< */ public static findOrBuild( this: ModelStatic, - options: FindOrCreateOptions + options: FindOrCreateOptions ): Promise<[M, boolean]>; /** @@ -2014,7 +1999,7 @@ export abstract class Model< */ public static findOrCreate( this: ModelStatic, - options: FindOrCreateOptions + options: FindOrCreateOptions ): Promise<[M, boolean]>; /** @@ -2023,7 +2008,7 @@ export abstract class Model< */ public static findCreateFind( this: ModelStatic, - options: FindOrCreateOptions + options: FindOrCreateOptions ): Promise<[M, boolean]>; /** @@ -2047,8 +2032,8 @@ export abstract class Model< */ public static upsert( this: ModelStatic, - values: M["_creationAttributes"], - options?: UpsertOptions + values: M['_creationAttributes'], + options?: UpsertOptions ): Promise<[M, boolean | null]>; /** @@ -2064,8 +2049,8 @@ export abstract class Model< */ public static bulkCreate( this: ModelStatic, - records: ReadonlyArray, - options?: BulkCreateOptions + records: ReadonlyArray, + options?: BulkCreateOptions ): Promise; /** @@ -2073,7 +2058,7 @@ export abstract class Model< */ public static truncate( this: ModelStatic, - options?: TruncateOptions + options?: TruncateOptions ): Promise; /** @@ -2083,7 +2068,7 @@ export abstract class Model< */ public static destroy( this: ModelStatic, - options?: DestroyOptions + options?: DestroyOptions ): Promise; /** @@ -2091,7 +2076,7 @@ export abstract class Model< */ public static restore( this: ModelStatic, - options?: RestoreOptions + options?: RestoreOptions ): Promise; /** @@ -2101,8 +2086,8 @@ export abstract class Model< */ public static update( this: ModelStatic, - values: Partial, - options: UpdateOptions + values: Partial, + options: UpdateOptions ): Promise<[number, M[]]>; /** @@ -2110,8 +2095,8 @@ export abstract class Model< */ public static increment( this: ModelStatic, - field: keyof M["_attributes"], - options: IncrementDecrementOptionsWithBy + field: keyof M['_attributes'], + options: IncrementDecrementOptionsWithBy ): Promise; /** @@ -2119,8 +2104,8 @@ export abstract class Model< */ public static increment( this: ModelStatic, - fields: ReadonlyArray, - options: IncrementDecrementOptionsWithBy + fields: ReadonlyArray, + options: IncrementDecrementOptionsWithBy ): Promise; /** @@ -2128,8 +2113,8 @@ export abstract class Model< */ public static increment( this: ModelStatic, - fields: { [key in keyof M["_attributes"]]?: number }, - options: IncrementDecrementOptions + fields: { [key in keyof M['_attributes']]?: number }, + options: IncrementDecrementOptions ): Promise; /** @@ -2184,11 +2169,11 @@ export abstract class Model< public static beforeCreate( this: ModelStatic, name: string, - fn: (instance: M, options: CreateOptions) => HookReturn + fn: (instance: M, options: CreateOptions) => HookReturn ): void; public static beforeCreate( this: ModelStatic, - fn: (instance: M, options: CreateOptions) => HookReturn + fn: (instance: M, options: CreateOptions) => HookReturn ): void; /** @@ -2200,11 +2185,11 @@ export abstract class Model< public static afterCreate( this: ModelStatic, name: string, - fn: (instance: M, options: CreateOptions) => HookReturn + fn: (instance: M, options: CreateOptions) => HookReturn ): void; public static afterCreate( this: ModelStatic, - fn: (instance: M, options: CreateOptions) => HookReturn + fn: (instance: M, options: CreateOptions) => HookReturn ): void; /** @@ -2248,11 +2233,11 @@ export abstract class Model< public static beforeUpdate( this: ModelStatic, name: string, - fn: (instance: M, options: UpdateOptions) => HookReturn + fn: (instance: M, options: UpdateOptions) => HookReturn ): void; public static beforeUpdate( this: ModelStatic, - fn: (instance: M, options: UpdateOptions) => HookReturn + fn: (instance: M, options: UpdateOptions) => HookReturn ): void; /** @@ -2264,11 +2249,11 @@ export abstract class Model< public static afterUpdate( this: ModelStatic, name: string, - fn: (instance: M, options: UpdateOptions) => HookReturn + fn: (instance: M, options: UpdateOptions) => HookReturn ): void; public static afterUpdate( this: ModelStatic, - fn: (instance: M, options: UpdateOptions) => HookReturn + fn: (instance: M, options: UpdateOptions) => HookReturn ): void; /** @@ -2280,17 +2265,11 @@ export abstract class Model< public static beforeSave( this: ModelStatic, name: string, - fn: ( - instance: M, - options: UpdateOptions | SaveOptions - ) => HookReturn + fn: (instance: M, options: UpdateOptions | SaveOptions) => HookReturn ): void; public static beforeSave( this: ModelStatic, - fn: ( - instance: M, - options: UpdateOptions | SaveOptions - ) => HookReturn + fn: (instance: M, options: UpdateOptions | SaveOptions) => HookReturn ): void; /** @@ -2302,17 +2281,11 @@ export abstract class Model< public static afterSave( this: ModelStatic, name: string, - fn: ( - instance: M, - options: UpdateOptions | SaveOptions - ) => HookReturn + fn: (instance: M, options: UpdateOptions | SaveOptions) => HookReturn ): void; public static afterSave( this: ModelStatic, - fn: ( - instance: M, - options: UpdateOptions | SaveOptions - ) => HookReturn + fn: (instance: M, options: UpdateOptions | SaveOptions) => HookReturn ): void; /** @@ -2324,17 +2297,11 @@ export abstract class Model< public static beforeBulkCreate( this: ModelStatic, name: string, - fn: ( - instances: M[], - options: BulkCreateOptions - ) => HookReturn + fn: (instances: M[], options: BulkCreateOptions) => HookReturn ): void; public static beforeBulkCreate( this: ModelStatic, - fn: ( - instances: M[], - options: BulkCreateOptions - ) => HookReturn + fn: (instances: M[], options: BulkCreateOptions) => HookReturn ): void; /** @@ -2346,17 +2313,11 @@ export abstract class Model< public static afterBulkCreate( this: ModelStatic, name: string, - fn: ( - instances: readonly M[], - options: BulkCreateOptions - ) => HookReturn + fn: (instances: readonly M[], options: BulkCreateOptions) => HookReturn ): void; public static afterBulkCreate( this: ModelStatic, - fn: ( - instances: readonly M[], - options: BulkCreateOptions - ) => HookReturn + fn: (instances: readonly M[], options: BulkCreateOptions) => HookReturn ): void; /** @@ -2367,12 +2328,10 @@ export abstract class Model< */ public static beforeBulkDestroy( this: ModelStatic, - name: string, - fn: (options: BulkCreateOptions) => HookReturn - ): void; + name: string, fn: (options: BulkCreateOptions) => HookReturn): void; public static beforeBulkDestroy( this: ModelStatic, - fn: (options: BulkCreateOptions) => HookReturn + fn: (options: BulkCreateOptions) => HookReturn ): void; /** @@ -2383,12 +2342,11 @@ export abstract class Model< */ public static afterBulkDestroy( this: ModelStatic, - name: string, - fn: (options: DestroyOptions) => HookReturn + name: string, fn: (options: DestroyOptions) => HookReturn ): void; public static afterBulkDestroy( this: ModelStatic, - fn: (options: DestroyOptions) => HookReturn + fn: (options: DestroyOptions) => HookReturn ): void; /** @@ -2399,12 +2357,11 @@ export abstract class Model< */ public static beforeBulkUpdate( this: ModelStatic, - name: string, - fn: (options: UpdateOptions) => HookReturn + name: string, fn: (options: UpdateOptions) => HookReturn ): void; public static beforeBulkUpdate( this: ModelStatic, - fn: (options: UpdateOptions) => HookReturn + fn: (options: UpdateOptions) => HookReturn ): void; /** @@ -2415,12 +2372,11 @@ export abstract class Model< */ public static afterBulkUpdate( this: ModelStatic, - name: string, - fn: (options: UpdateOptions) => HookReturn + name: string, fn: (options: UpdateOptions) => HookReturn ): void; public static afterBulkUpdate( this: ModelStatic, - fn: (options: UpdateOptions) => HookReturn + fn: (options: UpdateOptions) => HookReturn ): void; /** @@ -2431,12 +2387,11 @@ export abstract class Model< */ public static beforeFind( this: ModelStatic, - name: string, - fn: (options: FindOptions) => HookReturn + name: string, fn: (options: FindOptions) => HookReturn ): void; public static beforeFind( this: ModelStatic, - fn: (options: FindOptions) => HookReturn + fn: (options: FindOptions) => HookReturn ): void; /** @@ -2447,12 +2402,11 @@ export abstract class Model< */ public static beforeCount( this: ModelStatic, - name: string, - fn: (options: CountOptions) => HookReturn + name: string, fn: (options: CountOptions) => HookReturn ): void; public static beforeCount( this: ModelStatic, - fn: (options: CountOptions) => HookReturn + fn: (options: CountOptions) => HookReturn ): void; /** @@ -2463,12 +2417,11 @@ export abstract class Model< */ public static beforeFindAfterExpandIncludeAll( this: ModelStatic, - name: string, - fn: (options: FindOptions) => HookReturn + name: string, fn: (options: FindOptions) => HookReturn ): void; public static beforeFindAfterExpandIncludeAll( this: ModelStatic, - fn: (options: FindOptions) => HookReturn + fn: (options: FindOptions) => HookReturn ): void; /** @@ -2479,12 +2432,11 @@ export abstract class Model< */ public static beforeFindAfterOptions( this: ModelStatic, - name: string, - fn: (options: FindOptions) => HookReturn + name: string, fn: (options: FindOptions) => HookReturn ): void; public static beforeFindAfterOptions( this: ModelStatic, - fn: (options: FindOptions) => void + fn: (options: FindOptions) => void ): HookReturn; /** @@ -2496,57 +2448,39 @@ export abstract class Model< public static afterFind( this: ModelStatic, name: string, - fn: ( - instancesOrInstance: readonly M[] | M | null, - options: FindOptions - ) => HookReturn + fn: (instancesOrInstance: readonly M[] | M | null, options: FindOptions) => HookReturn ): void; public static afterFind( this: ModelStatic, - fn: ( - instancesOrInstance: readonly M[] | M | null, - options: FindOptions - ) => HookReturn + fn: (instancesOrInstance: readonly M[] | M | null, options: FindOptions) => HookReturn ): void; /** * A hook that is run before sequelize.sync call * @param fn A callback function that is called with options passed to sequelize.sync */ - public static beforeBulkSync( - name: string, - fn: (options: SyncOptions) => HookReturn - ): void; + public static beforeBulkSync(name: string, fn: (options: SyncOptions) => HookReturn): void; public static beforeBulkSync(fn: (options: SyncOptions) => HookReturn): void; /** * A hook that is run after sequelize.sync call * @param fn A callback function that is called with options passed to sequelize.sync */ - public static afterBulkSync( - name: string, - fn: (options: SyncOptions) => HookReturn - ): void; + public static afterBulkSync(name: string, fn: (options: SyncOptions) => HookReturn): void; public static afterBulkSync(fn: (options: SyncOptions) => HookReturn): void; /** * A hook that is run before Model.sync call * @param fn A callback function that is called with options passed to Model.sync */ - public static beforeSync( - name: string, - fn: (options: SyncOptions) => HookReturn - ): void; + public static beforeSync(name: string, fn: (options: SyncOptions) => HookReturn): void; public static beforeSync(fn: (options: SyncOptions) => HookReturn): void; /** * A hook that is run after Model.sync call * @param fn A callback function that is called with options passed to Model.sync */ - public static afterSync( - name: string, - fn: (options: SyncOptions) => HookReturn - ): void; + public static afterSync(name: string, fn: (options: SyncOptions) => HookReturn): void; public static afterSync(fn: (options: SyncOptions) => HookReturn): void; /** @@ -2559,9 +2493,7 @@ export abstract class Model< * @param options Options for the association */ public static hasOne( - this: ModelStatic, - target: ModelStatic, - options?: HasOneOptions + this: ModelStatic, target: ModelStatic, options?: HasOneOptions ): HasOne; /** @@ -2574,9 +2506,7 @@ export abstract class Model< * @param options Options for the association */ public static belongsTo( - this: ModelStatic, - target: ModelStatic, - options?: BelongsToOptions + this: ModelStatic, target: ModelStatic, options?: BelongsToOptions ): BelongsTo; /** @@ -2633,9 +2563,7 @@ export abstract class Model< * @param options Options for the association */ public static hasMany( - this: ModelStatic, - target: ModelStatic, - options?: HasManyOptions + this: ModelStatic, target: ModelStatic, options?: HasManyOptions ): HasMany; /** @@ -2688,9 +2616,7 @@ export abstract class Model< * */ public static belongsToMany( - this: ModelStatic, - target: ModelStatic, - options: BelongsToManyOptions + this: ModelStatic, target: ModelStatic, options: BelongsToManyOptions ): BelongsToMany; /** @@ -2717,17 +2643,12 @@ export abstract class Model< /** * Get the value of the underlying data value */ - public getDataValue( - key: K - ): TModelAttributes[K]; + public getDataValue(key: K): TModelAttributes[K]; /** * Update the underlying data value */ - public setDataValue( - key: K, - value: TModelAttributes[K] - ): void; + public setDataValue(key: K, value: TModelAttributes[K]): void; /** * If no key is given, returns all values of the instance, also invoking virtual getters. @@ -2738,14 +2659,8 @@ export abstract class Model< * @param options.plain If set to true, included instances will be returned as plain objects */ public get(options?: { plain?: boolean; clone?: boolean }): TModelAttributes; - public get( - key: K, - options?: { plain?: boolean; clone?: boolean } - ): this[K]; - public get( - key: string, - options?: { plain?: boolean; clone?: boolean } - ): unknown; + public get(key: K, options?: { plain?: boolean; clone?: boolean }): this[K]; + public get(key: string, options?: { plain?: boolean; clone?: boolean }): unknown; /** * Set is used to update values on the instance (the sequelize representation of the instance that is, @@ -2771,21 +2686,10 @@ export abstract class Model< * @param options.raw If set to true, field and virtual setters will be ignored * @param options.reset Clear all previously set data values */ - public set( - key: K, - value: TModelAttributes[K], - options?: SetOptions - ): this; + public set(key: K, value: TModelAttributes[K], options?: SetOptions): this; public set(keys: Partial, options?: SetOptions): this; - public setAttributes( - key: K, - value: TModelAttributes[K], - options?: SetOptions - ): this; - public setAttributes( - keys: Partial, - options?: SetOptions - ): this; + public setAttributes(key: K, value: TModelAttributes[K], options?: SetOptions): this; + public setAttributes(keys: Partial, options?: SetOptions): this; /** * If changed is called with a string it will return a boolean indicating whether the value of that key in @@ -2839,15 +2743,8 @@ export abstract class Model< /** * This is the same as calling `set` and then calling `save`. */ - public update( - key: K, - value: this[K], - options?: InstanceUpdateOptions - ): Promise; - public update( - keys: object, - options?: InstanceUpdateOptions - ): Promise; + public update(key: K, value: this[K], options?: InstanceUpdateOptions): Promise; + public update(keys: object, options?: InstanceUpdateOptions): Promise; /** * Destroy the row corresponding to this instance. Depending on your setting for paranoid, the row will From 83eab936f4b1a6a7b9ee35ae3f722cc25fd84e3c Mon Sep 17 00:00:00 2001 From: Daniel Schwartz Date: Mon, 22 Mar 2021 15:24:12 -0400 Subject: [PATCH 10/10] fix(types): add test for empty previous() call --- types/test/model.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/test/model.ts b/types/test/model.ts index 0bc5486ca352..d7eaf3b0851a 100644 --- a/types/test/model.ts +++ b/types/test/model.ts @@ -184,3 +184,4 @@ expectTypeOf(modelWithAttributes.previous).parameter(0).toEqualTypeOf(); expectTypeOf(modelWithAttributes.previous).returns.toEqualTypeOf(); expectTypeOf(modelWithAttributes.previous('name')).toEqualTypeOf(); +expectTypeOf(modelWithAttributes.previous()).toEqualTypeOf>();