From df7f15290421ff17c4fc749e196c58db8de44be6 Mon Sep 17 00:00:00 2001 From: Victor Korzunin Date: Fri, 10 Sep 2021 18:51:44 +0200 Subject: [PATCH 1/2] fix(select): do not force set subQuery to false this occurred on validating included elements in spite of user set it to true in include options --- lib/model.js | 3 +- test/unit/sql/select.test.js | 120 ++++++++++++++++++++++++++++++----- 2 files changed, 104 insertions(+), 19 deletions(-) diff --git a/lib/model.js b/lib/model.js index 595d18147e8d..4aee55023636 100644 --- a/lib/model.js +++ b/lib/model.js @@ -528,7 +528,7 @@ class Model { if (include.subQuery !== false && options.hasDuplicating && options.topLimit) { if (include.duplicating) { - include.subQuery = false; + include.subQuery = include.subQuery || false; include.subQueryFilter = include.hasRequired; } else { include.subQuery = include.hasRequired; @@ -538,7 +538,6 @@ class Model { include.subQuery = include.subQuery || false; if (include.duplicating) { include.subQueryFilter = include.subQuery; - include.subQuery = false; } else { include.subQueryFilter = false; include.subQuery = include.subQuery || include.hasParentRequired && include.hasRequired && !include.separate; diff --git a/test/unit/sql/select.test.js b/test/unit/sql/select.test.js index a511bbb66ef3..34e21c324dec 100644 --- a/test/unit/sql/select.test.js +++ b/test/unit/sql/select.test.js @@ -449,7 +449,7 @@ describe(Support.getTestDialectTeaser('SQL'), () => { }); }); - it('include (subQuery alias)', () => { + describe('include (subQuery alias)', () => { const User = Support.sequelize.define('User', { name: DataTypes.STRING, age: DataTypes.INTEGER @@ -466,29 +466,115 @@ describe(Support.getTestDialectTeaser('SQL'), () => { User.Posts = User.hasMany(Post, { foreignKey: 'user_id', as: 'postaliasname' }); - expectsql(sql.selectQuery('User', { - table: User.getTableName(), - model: User, - attributes: ['name', 'age'], + it('w/o filters', () => { + expectsql(sql.selectQuery('User', { + table: User.getTableName(), + model: User, + attributes: ['name', 'age'], + include: Model._validateIncludedElements({ + include: [{ + attributes: ['title'], + association: User.Posts, + subQuery: true, + required: true + }], + as: 'User' + }).include, + subQuery: true + }, User), { + default: 'SELECT [User].* FROM ' + + '(SELECT [User].[name], [User].[age], [User].[id] AS [id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' + + 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' + + 'WHERE ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) LIMIT 1 ) IS NOT NULL) AS [User];', + mssql: 'SELECT [User].* FROM ' + + '(SELECT [User].[name], [User].[age], [User].[id] AS [id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' + + 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' + + 'WHERE ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) ORDER BY [postaliasname].[id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY ) IS NOT NULL) AS [User];' + }); + }); + + it('w/ nested column filter', () => { + expectsql(sql.selectQuery('User', { + table: User.getTableName(), + model: User, + attributes: ['name', 'age'], + where: { '$postaliasname.title$': 'test' }, + include: Model._validateIncludedElements({ + include: [{ + attributes: ['title'], + association: User.Posts, + subQuery: true, + required: true + }], + as: 'User' + }).include, + subQuery: true + }, User), { + default: 'SELECT [User].* FROM ' + + '(SELECT [User].[name], [User].[age], [User].[id] AS [id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' + + 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' + + 'WHERE [postaliasname].[title] = \'test\' AND ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) LIMIT 1 ) IS NOT NULL) AS [User];', + mssql: 'SELECT [User].* FROM ' + + '(SELECT [User].[name], [User].[age], [User].[id] AS [id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' + + 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' + + 'WHERE [postaliasname].[title] = N\'test\' AND ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) ORDER BY [postaliasname].[id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY ) IS NOT NULL) AS [User];' + }); + }); + }); + + it('include w/ subQuery + nested filter + paging', () => { + const User = Support.sequelize.define('User', { + scopeId: DataTypes.INTEGER + }); + + const Company = Support.sequelize.define('Company', { + name: DataTypes.STRING, + public: DataTypes.BOOLEAN, + scopeId: DataTypes.INTEGER + }); + + const Profession = Support.sequelize.define('Profession', { + name: DataTypes.STRING, + scopeId: DataTypes.INTEGER + }); + + User.Company = User.belongsTo(Company, { foreignKey: 'companyId' }); + User.Profession = User.belongsTo(Profession, { foreignKey: 'professionId' }); + Company.Users = Company.hasMany(User, { as: 'Users', foreignKey: 'companyId' }); + Profession.Users = Profession.hasMany(User, { as: 'Users', foreignKey: 'professionId' }); + + expectsql(sql.selectQuery('Company', { + table: Company.getTableName(), + model: Company, + attributes: ['name', 'public'], + where: { '$Users.Profession.name$': 'test', [Op.and]: { scopeId: [42] } }, include: Model._validateIncludedElements({ include: [{ - attributes: ['title'], - association: User.Posts, + association: Company.Users, + attributes: [], + include: [{ + association: User.Profession, + attributes: [], + required: true + }], subQuery: true, required: true }], - as: 'User' + model: Company }).include, + limit: 5, + offset: 0, subQuery: true - }, User), { - default: 'SELECT [User].*, [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM ' + - '(SELECT [User].[name], [User].[age], [User].[id] AS [id] FROM [User] AS [User] ' + - 'WHERE ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) LIMIT 1 ) IS NOT NULL) AS [User] ' + - 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id];', - mssql: 'SELECT [User].*, [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM ' + - '(SELECT [User].[name], [User].[age], [User].[id] AS [id] FROM [User] AS [User] ' + - 'WHERE ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) ORDER BY [postaliasname].[id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY ) IS NOT NULL) AS [User] ' + - 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id];' + }, Company), { + default: 'SELECT [Company].* FROM (' + + 'SELECT [Company].[name], [Company].[public], [Company].[id] AS [id] FROM [Company] AS [Company] ' + + 'INNER JOIN [Users] AS [Users] ON [Company].[id] = [Users].[companyId] ' + + 'INNER JOIN [Professions] AS [Users->Profession] ON [Users].[professionId] = [Users->Profession].[id] ' + + `WHERE ([Company].[scopeId] IN (42)) AND [Users->Profession].[name] = ${sql.escape('test')} AND ( ` + + 'SELECT [Users].[companyId] FROM [Users] AS [Users] ' + + 'INNER JOIN [Professions] AS [Profession] ON [Users].[professionId] = [Profession].[id] ' + + `WHERE ([Users].[companyId] = [Company].[id])${sql.addLimitAndOffset({ limit: 1, tableAs: 'Users' }, User)} ` + + `) IS NOT NULL${sql.addLimitAndOffset({ limit: 5, offset: 0, tableAs: 'Company' }, Company)}) AS [Company];` }); }); From 58f213f7c38ba69575a450e9bea584009e142cbc Mon Sep 17 00:00:00 2001 From: Victor Korzunin Date: Mon, 13 Sep 2021 09:05:31 +0200 Subject: [PATCH 2/2] test: make include (subQuery alias) tests dry --- test/unit/sql/select.test.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/test/unit/sql/select.test.js b/test/unit/sql/select.test.js index 34e21c324dec..727ae7790c75 100644 --- a/test/unit/sql/select.test.js +++ b/test/unit/sql/select.test.js @@ -485,11 +485,7 @@ describe(Support.getTestDialectTeaser('SQL'), () => { default: 'SELECT [User].* FROM ' + '(SELECT [User].[name], [User].[age], [User].[id] AS [id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' + 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' + - 'WHERE ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) LIMIT 1 ) IS NOT NULL) AS [User];', - mssql: 'SELECT [User].* FROM ' + - '(SELECT [User].[name], [User].[age], [User].[id] AS [id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' + - 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' + - 'WHERE ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) ORDER BY [postaliasname].[id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY ) IS NOT NULL) AS [User];' + `WHERE ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id])${sql.addLimitAndOffset({ limit: 1, tableAs: 'postaliasname' }, User)} ) IS NOT NULL) AS [User];` }); }); @@ -513,11 +509,7 @@ describe(Support.getTestDialectTeaser('SQL'), () => { default: 'SELECT [User].* FROM ' + '(SELECT [User].[name], [User].[age], [User].[id] AS [id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' + 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' + - 'WHERE [postaliasname].[title] = \'test\' AND ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) LIMIT 1 ) IS NOT NULL) AS [User];', - mssql: 'SELECT [User].* FROM ' + - '(SELECT [User].[name], [User].[age], [User].[id] AS [id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' + - 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' + - 'WHERE [postaliasname].[title] = N\'test\' AND ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id]) ORDER BY [postaliasname].[id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY ) IS NOT NULL) AS [User];' + `WHERE [postaliasname].[title] = ${sql.escape('test')} AND ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE ([postaliasname].[user_id] = [User].[id])${sql.addLimitAndOffset({ limit: 1, tableAs: 'postaliasname' }, User)} ) IS NOT NULL) AS [User];` }); }); });