From ff71c45a99a19093338215227ffaeb8ec916e4e8 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Wed, 16 Sep 2020 22:23:26 +0300 Subject: [PATCH 1/6] Table group joins for subclasses in Criteria --- .../NHSpecificTest/NH2049/Fixture2049.cs | 3 +- .../Async/NHSpecificTest/NH2208/Filter.cs | 12 +- .../NHSpecificTest/NH2049/Fixture2049.cs | 3 +- .../NHSpecificTest/NH2049/Mappings.hbm.xml | 4 +- .../NHSpecificTest/NH2208/Filter.cs | 12 +- src/NHibernate/Engine/IJoin.cs | 15 ++ src/NHibernate/Engine/JoinSequence.cs | 118 +--------------- src/NHibernate/Engine/TableGroupJoinHelper.cs | 128 ++++++++++++++++++ src/NHibernate/Loader/JoinWalker.cs | 18 ++- .../Loader/OuterJoinableAssociation.cs | 6 +- 10 files changed, 190 insertions(+), 129 deletions(-) create mode 100644 src/NHibernate/Engine/IJoin.cs create mode 100644 src/NHibernate/Engine/TableGroupJoinHelper.cs diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH2049/Fixture2049.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH2049/Fixture2049.cs index 7dba9726ebd..3431ed1c59f 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH2049/Fixture2049.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH2049/Fixture2049.cs @@ -48,13 +48,12 @@ protected override void OnTearDown() } [Test] - [KnownBug("Known bug NH-2049.")] public async Task CanCriteriaQueryWithFilterOnJoinClassBaseClassPropertyAsync() { using (ISession session = OpenSession()) { session.EnableFilter("DeletedCustomer").SetParameter("deleted", false); - IList persons = await (session.CreateCriteria(typeof (Person)).ListAsync()); + IList persons = await (session.QueryOver().JoinQueryOver(x => x.IndividualCustomer).ListAsync()); Assert.That(persons, Has.Count.EqualTo(1)); Assert.That(persons[0].Id, Is.EqualTo(1)); diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH2208/Filter.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH2208/Filter.cs index 60f0af7aa3b..7d0b3077007 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH2208/Filter.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH2208/Filter.cs @@ -17,7 +17,7 @@ namespace NHibernate.Test.NHSpecificTest.NH2208 public class FilterAsync : BugTestCase { [Test] - public async Task TestAsync() + public async Task TestHqlAsync() { using (ISession session = OpenSession()) { @@ -25,5 +25,15 @@ public async Task TestAsync() await (session.CreateQuery("from E1 e join fetch e.BO").ListAsync()); } } + + [Test] + public async Task TestQueryOverAsync() + { + using (ISession session = OpenSession()) + { + session.EnableFilter("myfilter"); + await (session.QueryOver().JoinQueryOver(x => x.BO).ListAsync()); + } + } } } diff --git a/src/NHibernate.Test/NHSpecificTest/NH2049/Fixture2049.cs b/src/NHibernate.Test/NHSpecificTest/NH2049/Fixture2049.cs index 8d5356b488a..7aa2d4e6508 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2049/Fixture2049.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH2049/Fixture2049.cs @@ -37,13 +37,12 @@ protected override void OnTearDown() } [Test] - [KnownBug("Known bug NH-2049.")] public void CanCriteriaQueryWithFilterOnJoinClassBaseClassProperty() { using (ISession session = OpenSession()) { session.EnableFilter("DeletedCustomer").SetParameter("deleted", false); - IList persons = session.CreateCriteria(typeof (Person)).List(); + IList persons = session.QueryOver().JoinQueryOver(x => x.IndividualCustomer).List(); Assert.That(persons, Has.Count.EqualTo(1)); Assert.That(persons[0].Id, Is.EqualTo(1)); diff --git a/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml index d0ad311eea8..70df70ffb99 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml @@ -20,11 +20,11 @@ - + - \ No newline at end of file + diff --git a/src/NHibernate.Test/NHSpecificTest/NH2208/Filter.cs b/src/NHibernate.Test/NHSpecificTest/NH2208/Filter.cs index 2301c20e3ba..5fa579fa297 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2208/Filter.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH2208/Filter.cs @@ -6,7 +6,7 @@ namespace NHibernate.Test.NHSpecificTest.NH2208 public class Filter : BugTestCase { [Test] - public void Test() + public void TestHql() { using (ISession session = OpenSession()) { @@ -14,5 +14,15 @@ public void Test() session.CreateQuery("from E1 e join fetch e.BO").List(); } } + + [Test] + public void TestQueryOver() + { + using (ISession session = OpenSession()) + { + session.EnableFilter("myfilter"); + session.QueryOver().JoinQueryOver(x => x.BO).List(); + } + } } } diff --git a/src/NHibernate/Engine/IJoin.cs b/src/NHibernate/Engine/IJoin.cs new file mode 100644 index 00000000000..ec0ed3e2c26 --- /dev/null +++ b/src/NHibernate/Engine/IJoin.cs @@ -0,0 +1,15 @@ +using NHibernate.Persister.Entity; +using NHibernate.SqlCommand; +using NHibernate.Type; + +namespace NHibernate.Engine +{ + internal interface IJoin + { + IJoinable Joinable { get; } + string[] LHSColumns { get; } + string Alias { get; } + IAssociationType AssociationType { get; } + JoinType JoinType { get; } + } +} diff --git a/src/NHibernate/Engine/JoinSequence.cs b/src/NHibernate/Engine/JoinSequence.cs index 3b3bae924cc..5ad62b78a1e 100644 --- a/src/NHibernate/Engine/JoinSequence.cs +++ b/src/NHibernate/Engine/JoinSequence.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using NHibernate.Hql.Ast.ANTLR.Tree; using NHibernate.Persister.Collection; @@ -41,7 +40,7 @@ public override string ToString() return buf.Append('}').ToString(); } - private sealed class Join + private sealed class Join : IJoin { private readonly IAssociationType associationType; private readonly IJoinable joinable; @@ -50,7 +49,7 @@ private sealed class Join private readonly string[] lhsColumns; public Join(ISessionFactoryImplementor factory, IAssociationType associationType, string alias, JoinType joinType, - string[] lhsColumns) + string[] lhsColumns) { this.associationType = associationType; this.joinable = associationType.GetAssociatedJoinable(factory); @@ -182,7 +181,7 @@ internal JoinFragment ToJoinFragment( last = join.Joinable; } - if (rootJoinable == null && ProcessAsTableGroupJoin(includeAllSubclassJoins, withClauses, joinFragment)) + if (rootJoinable == null && TableGroupJoinHelper.ProcessAsTableGroupJoin(joins, withClauses, includeAllSubclassJoins, joinFragment, alias => IsIncluded(alias), factory)) { return joinFragment; } @@ -253,117 +252,6 @@ private SqlString GetWithClause(IDictionary enabledFilters, ref return SqlStringHelper.JoinParts(" and ", withConditions); } - private bool ProcessAsTableGroupJoin(bool includeAllSubclassJoins, SqlString[] withClauseFragments, JoinFragment joinFragment) - { - if (!NeedsTableGroupJoin(joins, withClauseFragments, includeAllSubclassJoins)) - return false; - - var first = joins[0]; - string joinString = ANSIJoinFragment.GetJoinString(first.JoinType); - joinFragment.AddFromFragmentString( - new SqlString( - joinString, - " (", - first.Joinable.TableName, - " ", - first.Alias - )); - - foreach (var join in joins) - { - if (join != first) - joinFragment.AddJoin( - join.Joinable.TableName, - join.Alias, - join.LHSColumns, - JoinHelper.GetRHSColumnNames(join.AssociationType, factory), - join.JoinType, - SqlString.Empty); - - AddSubclassJoins( - joinFragment, - join.Alias, - join.Joinable, - // TODO (from hibernate): Think about if this could be made always true - // NH Specific: made always true (original check: join.JoinType == JoinType.InnerJoin) - true, - includeAllSubclassJoins - ); - } - - var tableGroupWithClause = GetTableGroupJoinWithClause(withClauseFragments, first); - joinFragment.AddFromFragmentString(tableGroupWithClause); - return true; - } - - private SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragments, Join first) - { - SqlStringBuilder fromFragment = new SqlStringBuilder(); - fromFragment.Add(")").Add(" on "); - - String[] lhsColumns = first.LHSColumns; - var isAssociationJoin = lhsColumns.Length > 0; - if (isAssociationJoin) - { - String rhsAlias = first.Alias; - String[] rhsColumns = JoinHelper.GetRHSColumnNames(first.AssociationType, factory); - for (int j = 0; j < lhsColumns.Length; j++) - { - fromFragment.Add(lhsColumns[j]); - fromFragment.Add("="); - fromFragment.Add(rhsAlias); - fromFragment.Add("."); - fromFragment.Add(rhsColumns[j]); - if (j < lhsColumns.Length - 1) - { - fromFragment.Add(" and "); - } - } - } - - for (var i= 0; i < withClauseFragments.Length; i++) - { - var withClause = withClauseFragments[i]; - if (SqlStringHelper.IsEmpty(withClause)) - continue; - - if (withClause.StartsWithCaseInsensitive(" and ")) - { - if (!isAssociationJoin) - { - withClause = withClause.Substring(4); - } - } - else if (isAssociationJoin) - { - fromFragment.Add(" and "); - } - - fromFragment.Add(withClause); - } - - return fromFragment.ToSqlString(); - } - - private bool NeedsTableGroupJoin(List joins, SqlString[] withClauseFragments, bool includeSubclasses) - { - // If the rewrite is disabled or we don't have a with clause, we don't need a table group join - if ( /*!collectionJoinSubquery ||*/ withClauseFragments.All(x => SqlStringHelper.IsEmpty(x))) - { - return false; - } - // If we only have one join, a table group join is only necessary if subclass columns are used in the with clause - if (joins.Count == 1) - { - return joins[0].Joinable is AbstractEntityPersister persister && persister.HasSubclassJoins(includeSubclasses); - //NH Specific: No alias processing - //return isSubclassAliasDereferenced( joins[ 0], withClauseFragment ); - } - - //NH Specific: No alias processing (see hibernate JoinSequence.NeedsTableGroupJoin) - return true; - } - private bool IsManyToManyRoot(IJoinable joinable) { if (joinable != null && joinable.IsCollection) diff --git a/src/NHibernate/Engine/TableGroupJoinHelper.cs b/src/NHibernate/Engine/TableGroupJoinHelper.cs new file mode 100644 index 00000000000..fc417529c80 --- /dev/null +++ b/src/NHibernate/Engine/TableGroupJoinHelper.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NHibernate.Persister.Entity; +using NHibernate.SqlCommand; + +namespace NHibernate.Engine +{ + //Generates table group join if neccessary. Example of generated query wit table group join: + // SELECT * + // FROM Person person0_ + // INNER JOIN ( + // IndividualCustomer individual1_ + // INNER JOIN Customer individual1_1_ ON individual1_.IndividualCustomerID = individual1_1_.Id + // ) ON person0_.Id = individual1_.PersonID AND individual1_1_.Deleted = @p0 + internal class TableGroupJoinHelper + { + internal static bool ProcessAsTableGroupJoin(IReadOnlyList tableGroupJoinables, SqlString[] withClauseFragments, bool includeAllSubclassJoins, JoinFragment joinFragment, Func isSubclassIncluded, ISessionFactoryImplementor sessionFactoryImplementor) + { + if (!TableGroupJoinHelper.NeedsTableGroupJoin(tableGroupJoinables, withClauseFragments, includeAllSubclassJoins)) + return false; + + var first = tableGroupJoinables[0]; + string joinString = ANSIJoinFragment.GetJoinString(first.JoinType); + joinFragment.AddFromFragmentString( + new SqlString( + joinString, + " (", + first.Joinable.TableName, + " ", + first.Alias + )); + + foreach (var join in tableGroupJoinables) + { + if (join != first) + joinFragment.AddJoin( + join.Joinable.TableName, + join.Alias, + join.LHSColumns, + JoinHelper.GetRHSColumnNames(join.AssociationType, sessionFactoryImplementor), + join.JoinType, + SqlString.Empty); + + bool include = includeAllSubclassJoins && isSubclassIncluded(join.Alias); + // TODO (from hibernate): Think about if this could be made always true + // NH Specific: made always true (original check: join.JoinType == JoinType.InnerJoin) + var innerJoin = true; + joinFragment.AddJoins( + join.Joinable.FromJoinFragment(join.Alias, innerJoin, include), + join.Joinable.WhereJoinFragment(join.Alias, innerJoin, include)); + } + + var withClause = TableGroupJoinHelper.GetTableGroupJoinWithClause(withClauseFragments, first, sessionFactoryImplementor); + joinFragment.AddFromFragmentString(withClause); + return true; + } + + private static bool NeedsTableGroupJoin(IReadOnlyList joins, SqlString[] withClauseFragments, bool includeSubclasses) + { + // If the rewrite is disabled or we don't have a with clause, we don't need a table group join + if ( /*!collectionJoinSubquery ||*/ withClauseFragments.All(x => SqlStringHelper.IsEmpty(x))) + { + return false; + } + + // If we only have one join, a table group join is only necessary if subclass columns are used in the with clause + if (joins.Count == 1) + { + return joins[0].Joinable is AbstractEntityPersister persister && persister.HasSubclassJoins(includeSubclasses); + //NH Specific: No alias processing + //return isSubclassAliasDereferenced( joins[ 0], withClauseFragment ); + } + + //NH Specific: No alias processing (see hibernate JoinSequence.NeedsTableGroupJoin) + return true; + } + + private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragments, IJoin first, ISessionFactoryImplementor factory) + { + SqlStringBuilder fromFragment = new SqlStringBuilder(); + fromFragment.Add(")").Add(" on "); + + string[] lhsColumns = first.LHSColumns; + var isAssociationJoin = lhsColumns.Length > 0; + if (isAssociationJoin) + { + string rhsAlias = first.Alias; + string[] rhsColumns = JoinHelper.GetRHSColumnNames(first.AssociationType, factory); + for (int j = 0; j < lhsColumns.Length; j++) + { + fromFragment.Add(lhsColumns[j]); + fromFragment.Add("="); + fromFragment.Add(rhsAlias); + fromFragment.Add("."); + fromFragment.Add(rhsColumns[j]); + if (j < lhsColumns.Length - 1) + { + fromFragment.Add(" and "); + } + } + } + + for (var i = 0; i < withClauseFragments.Length; i++) + { + var withClause = withClauseFragments[i]; + if (SqlStringHelper.IsEmpty(withClause)) + continue; + + if (withClause.StartsWithCaseInsensitive(" and ")) + { + if (!isAssociationJoin) + { + withClause = withClause.Substring(4); + } + } + else if (isAssociationJoin) + { + fromFragment.Add(" and "); + } + + fromFragment.Add(withClause); + } + + return fromFragment.ToSqlString(); + } + } +} diff --git a/src/NHibernate/Loader/JoinWalker.cs b/src/NHibernate/Loader/JoinWalker.cs index 2d6259ab0ca..4ed83d52150 100644 --- a/src/NHibernate/Loader/JoinWalker.cs +++ b/src/NHibernate/Loader/JoinWalker.cs @@ -848,21 +848,29 @@ protected JoinFragment MergeOuterJoins(IList associati } else { - oj.AddJoins(outerjoin); // NH Different behavior : NH1179 and NH1293 // Apply filters in Many-To-One association + SqlString filter = null; if (enabledFiltersForManyToOne.Count > 0) { string manyToOneFilterFragment = oj.Joinable.FilterFragment(oj.RHSAlias, enabledFiltersForManyToOne); bool joinClauseDoesNotContainsFilterAlready = - outerjoin.ToFromFragmentString.IndexOfCaseInsensitive(manyToOneFilterFragment) == -1; + oj.On?.IndexOfCaseInsensitive(manyToOneFilterFragment) == -1; if (joinClauseDoesNotContainsFilterAlready) { - // Ensure that the join condition is added to the join, not the where clause. - // Adding the condition to the where clause causes left joins to become inner joins. - outerjoin.AddFromFragmentString(new SqlString(manyToOneFilterFragment)); + filter = new SqlString(manyToOneFilterFragment); } } + + if (TableGroupJoinHelper.ProcessAsTableGroupJoin(new[] {oj}, new[] {oj.On, filter}, true, outerjoin, alias => true, factory)) + continue; + + oj.AddJoins(outerjoin); + + // Ensure that the join condition is added to the join, not the where clause. + // Adding the condition to the where clause causes left joins to become inner joins. + if (SqlStringHelper.IsNotEmpty(filter)) + outerjoin.AddFromFragmentString(filter); } last = oj; } diff --git a/src/NHibernate/Loader/OuterJoinableAssociation.cs b/src/NHibernate/Loader/OuterJoinableAssociation.cs index 8e3d680240f..ebdf393759f 100644 --- a/src/NHibernate/Loader/OuterJoinableAssociation.cs +++ b/src/NHibernate/Loader/OuterJoinableAssociation.cs @@ -9,7 +9,7 @@ namespace NHibernate.Loader { - public sealed class OuterJoinableAssociation + public sealed class OuterJoinableAssociation : IJoin { private readonly IAssociationType joinableType; private readonly IJoinable joinable; @@ -100,6 +100,10 @@ public SelectMode SelectMode public ISet EntityFetchLazyProperties { get; set; } + string[] IJoin.LHSColumns => lhsColumns; + string IJoin.Alias => RHSAlias; + IAssociationType IJoin.AssociationType => JoinableType; + public int GetOwner(IList associations) { if (IsEntityType || IsCollection) From 3cdb206a553002a20829e89c729dfe479f32d8b6 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Wed, 16 Sep 2020 23:28:19 +0300 Subject: [PATCH 2/6] Revert not needed change --- src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml index 70df70ffb99..9454287645a 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml @@ -20,7 +20,7 @@ - + From cee916374dc622b2ac2a0b056c3078953d119fdf Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Wed, 16 Sep 2020 23:49:04 +0300 Subject: [PATCH 3/6] Revert not needed change --- src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml index 9454287645a..d0ad311eea8 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/NH2049/Mappings.hbm.xml @@ -27,4 +27,4 @@ - + \ No newline at end of file From 44cf75cc5a7741217f80af23261e6f1fe87e3cc1 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Thu, 17 Sep 2020 10:31:32 +0300 Subject: [PATCH 4/6] Extract AppendWithClause --- src/NHibernate/Engine/TableGroupJoinHelper.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/NHibernate/Engine/TableGroupJoinHelper.cs b/src/NHibernate/Engine/TableGroupJoinHelper.cs index fc417529c80..f2ee03f650d 100644 --- a/src/NHibernate/Engine/TableGroupJoinHelper.cs +++ b/src/NHibernate/Engine/TableGroupJoinHelper.cs @@ -6,7 +6,7 @@ namespace NHibernate.Engine { - //Generates table group join if neccessary. Example of generated query wit table group join: + //Generates table group join if neccessary. Example of generated query with table group join: // SELECT * // FROM Person person0_ // INNER JOIN ( @@ -101,6 +101,13 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm } } + AppendWithClause(fromFragment, isAssociationJoin, withClauseFragments); + + return fromFragment.ToSqlString(); + } + + private static void AppendWithClause(SqlStringBuilder fromFragment, bool hasConditions, SqlString[] withClauseFragments) + { for (var i = 0; i < withClauseFragments.Length; i++) { var withClause = withClauseFragments[i]; @@ -109,20 +116,19 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm if (withClause.StartsWithCaseInsensitive(" and ")) { - if (!isAssociationJoin) + if (!hasConditions) { withClause = withClause.Substring(4); } } - else if (isAssociationJoin) + else if (hasConditions) { fromFragment.Add(" and "); } fromFragment.Add(withClause); + hasConditions = true; } - - return fromFragment.ToSqlString(); } } } From 2ad5e7ab0b4642581a150c42c578a92a4a9925fb Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Mon, 5 Oct 2020 10:36:57 +0300 Subject: [PATCH 5/6] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Frédéric Delaporte <12201973+fredericDelaporte@users.noreply.github.com> --- src/NHibernate/Engine/TableGroupJoinHelper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/NHibernate/Engine/TableGroupJoinHelper.cs b/src/NHibernate/Engine/TableGroupJoinHelper.cs index f2ee03f650d..19d9df8d56a 100644 --- a/src/NHibernate/Engine/TableGroupJoinHelper.cs +++ b/src/NHibernate/Engine/TableGroupJoinHelper.cs @@ -17,7 +17,7 @@ internal class TableGroupJoinHelper { internal static bool ProcessAsTableGroupJoin(IReadOnlyList tableGroupJoinables, SqlString[] withClauseFragments, bool includeAllSubclassJoins, JoinFragment joinFragment, Func isSubclassIncluded, ISessionFactoryImplementor sessionFactoryImplementor) { - if (!TableGroupJoinHelper.NeedsTableGroupJoin(tableGroupJoinables, withClauseFragments, includeAllSubclassJoins)) + if (!NeedsTableGroupJoin(tableGroupJoinables, withClauseFragments, includeAllSubclassJoins)) return false; var first = tableGroupJoinables[0]; @@ -51,15 +51,15 @@ internal static bool ProcessAsTableGroupJoin(IReadOnlyList tableGroupJoin join.Joinable.WhereJoinFragment(join.Alias, innerJoin, include)); } - var withClause = TableGroupJoinHelper.GetTableGroupJoinWithClause(withClauseFragments, first, sessionFactoryImplementor); + var withClause = GetTableGroupJoinWithClause(withClauseFragments, first, sessionFactoryImplementor); joinFragment.AddFromFragmentString(withClause); return true; } private static bool NeedsTableGroupJoin(IReadOnlyList joins, SqlString[] withClauseFragments, bool includeSubclasses) { - // If the rewrite is disabled or we don't have a with clause, we don't need a table group join - if ( /*!collectionJoinSubquery ||*/ withClauseFragments.All(x => SqlStringHelper.IsEmpty(x))) + // If we don't have a with clause, we don't need a table group join + if (withClauseFragments.All(x => SqlStringHelper.IsEmpty(x))) { return false; } From e3d19bfb1a07fcec681b27379fb92462c8ebdddc Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Mon, 5 Oct 2020 10:50:06 +0300 Subject: [PATCH 6/6] Small refactoring --- src/NHibernate/Engine/TableGroupJoinHelper.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/NHibernate/Engine/TableGroupJoinHelper.cs b/src/NHibernate/Engine/TableGroupJoinHelper.cs index 19d9df8d56a..f5d1f8e39c4 100644 --- a/src/NHibernate/Engine/TableGroupJoinHelper.cs +++ b/src/NHibernate/Engine/TableGroupJoinHelper.cs @@ -45,7 +45,7 @@ internal static bool ProcessAsTableGroupJoin(IReadOnlyList tableGroupJoin bool include = includeAllSubclassJoins && isSubclassIncluded(join.Alias); // TODO (from hibernate): Think about if this could be made always true // NH Specific: made always true (original check: join.JoinType == JoinType.InnerJoin) - var innerJoin = true; + const bool innerJoin = true; joinFragment.AddJoins( join.Joinable.FromJoinFragment(join.Alias, innerJoin, include), join.Joinable.WhereJoinFragment(join.Alias, innerJoin, include)); @@ -87,17 +87,10 @@ private static SqlString GetTableGroupJoinWithClause(SqlString[] withClauseFragm { string rhsAlias = first.Alias; string[] rhsColumns = JoinHelper.GetRHSColumnNames(first.AssociationType, factory); - for (int j = 0; j < lhsColumns.Length; j++) + fromFragment.Add(lhsColumns[0]).Add("=").Add(rhsAlias).Add(".").Add(rhsColumns[0]); + for (int j = 1; j < lhsColumns.Length; j++) { - fromFragment.Add(lhsColumns[j]); - fromFragment.Add("="); - fromFragment.Add(rhsAlias); - fromFragment.Add("."); - fromFragment.Add(rhsColumns[j]); - if (j < lhsColumns.Length - 1) - { - fromFragment.Add(" and "); - } + fromFragment.Add(" and ").Add(lhsColumns[j]).Add("=").Add(rhsAlias).Add(".").Add(rhsColumns[j]); } }