From b4fb681d106e0a3f6ff0b725a9d40b528dac10ba Mon Sep 17 00:00:00 2001 From: csharper2010 Date: Tue, 24 Nov 2020 07:57:42 +0100 Subject: [PATCH 01/12] Fixes #2614 Results from the query should be skipped if they are already in the ISet distinction thus if distinction.Add is false. Currently, they are only added if they have been there before. Signed-off-by: csharper2010 --- src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs | 2 +- src/NHibernate/Engine/Query/HQLQueryPlan.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs b/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs index f0e5674ea69..a5ced8d10d7 100644 --- a/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs +++ b/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs @@ -78,7 +78,7 @@ public async Task PerformListAsync(QueryParameters queryParameters, ISessionImpl for (int x = 0; x < size; x++) { object result = tmp[x]; - if (distinction.Add(result)) + if (!distinction.Add(result)) { continue; } diff --git a/src/NHibernate/Engine/Query/HQLQueryPlan.cs b/src/NHibernate/Engine/Query/HQLQueryPlan.cs index eb02c296cea..fd6717a3d33 100644 --- a/src/NHibernate/Engine/Query/HQLQueryPlan.cs +++ b/src/NHibernate/Engine/Query/HQLQueryPlan.cs @@ -128,7 +128,7 @@ public void PerformList(QueryParameters queryParameters, ISessionImplementor ses for (int x = 0; x < size; x++) { object result = tmp[x]; - if (distinction.Add(result)) + if (!distinction.Add(result)) { continue; } From ef533ff3687a51be70376c10f12a7c363d5b3f35 Mon Sep 17 00:00:00 2001 From: csharper2010 Date: Tue, 24 Nov 2020 07:57:42 +0100 Subject: [PATCH 02/12] Fixes #2614 Results from the query should be skipped if they are already in the ISet distinction thus if distinction.Add is false. Currently, they are only added if they have been there before. Signed-off-by: csharper2010 --- src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs | 2 +- src/NHibernate/Engine/Query/HQLQueryPlan.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs b/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs index f0e5674ea69..a5ced8d10d7 100644 --- a/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs +++ b/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs @@ -78,7 +78,7 @@ public async Task PerformListAsync(QueryParameters queryParameters, ISessionImpl for (int x = 0; x < size; x++) { object result = tmp[x]; - if (distinction.Add(result)) + if (!distinction.Add(result)) { continue; } diff --git a/src/NHibernate/Engine/Query/HQLQueryPlan.cs b/src/NHibernate/Engine/Query/HQLQueryPlan.cs index eb02c296cea..fd6717a3d33 100644 --- a/src/NHibernate/Engine/Query/HQLQueryPlan.cs +++ b/src/NHibernate/Engine/Query/HQLQueryPlan.cs @@ -128,7 +128,7 @@ public void PerformList(QueryParameters queryParameters, ISessionImplementor ses for (int x = 0; x < size; x++) { object result = tmp[x]; - if (distinction.Add(result)) + if (!distinction.Add(result)) { continue; } From bd592e1f51a593c5602fec181d11cd0013330a8b Mon Sep 17 00:00:00 2001 From: csharper2010 Date: Tue, 24 Nov 2020 07:57:42 +0100 Subject: [PATCH 03/12] Fixes #2614 Results from the query should be skipped if they are already in the ISet distinction thus if distinction.Add is false. Currently, they are only added if they have been there before. Signed-off-by: csharper2010 --- src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs | 2 +- src/NHibernate/Engine/Query/HQLQueryPlan.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs b/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs index f0e5674ea69..a5ced8d10d7 100644 --- a/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs +++ b/src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs @@ -78,7 +78,7 @@ public async Task PerformListAsync(QueryParameters queryParameters, ISessionImpl for (int x = 0; x < size; x++) { object result = tmp[x]; - if (distinction.Add(result)) + if (!distinction.Add(result)) { continue; } diff --git a/src/NHibernate/Engine/Query/HQLQueryPlan.cs b/src/NHibernate/Engine/Query/HQLQueryPlan.cs index eb02c296cea..fd6717a3d33 100644 --- a/src/NHibernate/Engine/Query/HQLQueryPlan.cs +++ b/src/NHibernate/Engine/Query/HQLQueryPlan.cs @@ -128,7 +128,7 @@ public void PerformList(QueryParameters queryParameters, ISessionImplementor ses for (int x = 0; x < size; x++) { object result = tmp[x]; - if (distinction.Add(result)) + if (!distinction.Add(result)) { continue; } From 62f4331c3e34cbc2ba84bf335cf619c38bd11cd6 Mon Sep 17 00:00:00 2001 From: CSharper2010 Date: Mon, 26 Jun 2023 09:08:45 +0200 Subject: [PATCH 04/12] Fixes #3334 Convert EntityJoin to FROM_FRAGMENT only for first element Adjust comment in HqlSqlWalker before adding an element without Parent to the tree and use AppendFromElement instead of direct AddChild --- .../NHSpecificTest/GH3334/Entity.cs | 33 ++++ .../NHSpecificTest/GH3334/Fixture.cs | 174 ++++++++++++++++++ .../NHSpecificTest/GH3334/Mappings.hbm.xml | 36 ++++ src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs | 8 +- .../Ast/ANTLR/Tree/EntityJoinFromElement.cs | 2 +- .../Hql/Ast/ANTLR/Tree/FromClause.cs | 3 +- .../Hql/Ast/ANTLR/Tree/FromElement.cs | 16 +- .../Hql/Ast/ANTLR/Tree/FromElementFactory.cs | 36 ++-- 8 files changed, 279 insertions(+), 29 deletions(-) create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3334/Entity.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3334/Mappings.hbm.xml diff --git a/src/NHibernate.Test/NHSpecificTest/GH3334/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH3334/Entity.cs new file mode 100644 index 00000000000..1203e6d8eb9 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3334/Entity.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.GH3334 +{ + public class Entity + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + public virtual ISet Children { get; set; } = new HashSet(); + public virtual OtherEntity OtherEntity { get; set; } + } + + public class ChildEntity + { + public virtual int Id { get; set; } + public virtual Entity Parent { get; set; } + public virtual string Name { get; set; } + public virtual GrandChildEntity Child { get; set; } + } + + public class GrandChildEntity + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + } + + public class OtherEntity + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + public virtual ISet Entities { get; set; } = new HashSet(); + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs new file mode 100644 index 00000000000..deda4cc87d9 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs @@ -0,0 +1,174 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH3334 +{ + [TestFixture] + public class Fixture : BugTestCase + { + [OneTimeSetUp] + public void OneTimeSetUp() + { + using (var session = OpenSession()) + using (var t = session.BeginTransaction()) + { + var parent = new Entity + { + Name = "Parent1", + Children = { new ChildEntity { Name = "Child", Child = new GrandChildEntity { Name = "GrandChild" } } } + }; + session.Save(parent); + parent = new Entity + { + Name = "Parent2", + Children = { new ChildEntity { Name = "Child", Child = new GrandChildEntity { Name = "XGrandChild" } } } + }; + var other = new OtherEntity { Name = "ABC", Entities = {parent}}; + parent.OtherEntity = other; + session.Save(parent); + session.Save(other); + t.Commit(); + } + + Sfi.Statistics.IsStatisticsEnabled = true; + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + Sfi.Statistics.IsStatisticsEnabled = false; + + using var session = OpenSession(); + using var transaction = session.BeginTransaction(); + + session.CreateQuery("delete from ChildEntity").ExecuteUpdate(); + session.CreateQuery("delete from GrandChildEntity").ExecuteUpdate(); + session.CreateQuery("delete from Entity").ExecuteUpdate(); + session.CreateQuery("delete from OtherEntity").ExecuteUpdate(); + + transaction.Commit(); + } + + public class TestCase + { + public string Name { get; } + public string Hql { get; } + public int LineNumber { get; } + + internal TestCase(string name, string hql, [CallerLineNumber] int lineNumber = 0) + { + Name = name; + Hql = hql; + LineNumber = lineNumber; + } + + public override string ToString() => $"{LineNumber:0000}: {Name}"; + } + + public static IEnumerable GetNoExceptionOnExecuteQueryTestCases() + { + /* does not work because of inner join or theta join created for many-to-one + @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.Children) AS child + WHERE + child.Child.Name like 'G%' + OR ROOT.OtherEntity.Name like 'A%' + )");*/ + + yield return new("Basic Elements case 1 FoundViaGrandChildG", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.Children) AS child + LEFT JOIN child.Child AS grandChild + WHERE + grandChild.Name like 'G%' + )"); + yield return new("Basic Elements case 2 FoundViaOtherEntityA", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.OtherEntity) AS otherEntity + WHERE + otherEntity.Name like 'A%' + )"); + yield return new("HQL Elements FoundViaGrandChildG", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.Children) AS child + LEFT JOIN child.Child AS grandChild + LEFT JOIN ROOT.OtherEntity AS otherEntity + WHERE + grandChild.Name like 'G%' + OR otherEntity.Name like 'G%' + )"); + yield return new("HQL Elements FoundViaOtherEntityA", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.Children) AS child + LEFT JOIN child.Child AS grandChild + LEFT JOIN ROOT.OtherEntity AS otherEntity + WHERE + grandChild.Name like 'A%' + OR otherEntity.Name like 'A%' + )"); + yield return new("HQL Entity FoundViaGrandChildG", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ChildEntity AS child + LEFT JOIN child.Child AS grandChild + LEFT JOIN ROOT.OtherEntity AS otherEntity + WHERE + child.Parent = ROOT + AND ( + grandChild.Name like 'G%' + OR otherEntity.Name like 'G%' + ) + )"); + yield return new("HQL Entity FoundViaOtherEntityA", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ChildEntity AS child + LEFT JOIN child.Child AS grandChild + LEFT JOIN ROOT.OtherEntity AS otherEntity + WHERE + child.Parent = ROOT + AND ( + grandChild.Name like 'A%' + OR otherEntity.Name like 'A%' + ) + )"); + } + + [Test, TestCaseSource(nameof(GetNoExceptionOnExecuteQueryTestCases))] + public void NoExceptionOnExecuteQuery(TestCase testCase) + { + using var session = OpenSession(); + using var _ = session.BeginTransaction(); + + var q = session.CreateQuery(testCase.Hql); + Assert.AreEqual(1, q.List().Count); + } + + protected override bool CheckDatabaseWasCleaned() + { + // same set of objects for each test + return true; + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3334/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH3334/Mappings.hbm.xml new file mode 100644 index 00000000000..1f5bcdfe8e6 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3334/Mappings.hbm.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs index 7cc0bcae9c6..3ff11867273 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs @@ -835,10 +835,10 @@ void CreateFromJoinElement( if (fromElement.Parent == null) { - // Most likely means association join is used in invalid context - // I.e. in subquery: from EntityA a where exists (from EntityB join a.Assocation) - // Maybe we should throw exception instead - fromElement.FromClause.AddChild(fromElement); + // happens e.g. on ToMany association join with "EXISTS(FROM ELEMENTS(ROOT.Children))" + // or on an association join in subquery from a parent entity in parent from clause + // like in "from EntityA a where exists (from EntityB join a.Assocation)" + fromElement.FromClause.AppendFromElement(fromElement); if (fromElement.IsImplied) fromElement.JoinSequence.SetUseThetaStyle(true); } diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs index 8711100412a..c5ab7635f88 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs @@ -15,7 +15,7 @@ public EntityJoinFromElement(FromClause fromClause, IQueryable entityPersister, string tableAlias = fromClause.AliasGenerator.CreateName(entityPersister.EntityName); EntityType entityType = (EntityType) entityPersister.Type; - InitializeEntity(fromClause, entityPersister.EntityName, entityPersister, entityType, alias, tableAlias); + InitializeEntity(fromClause, entityPersister.EntityName, entityPersister, entityType, alias, tableAlias, out _); //NH Specific: hibernate uses special class EntityJoinJoinSequenceImpl JoinSequence = new JoinSequence(SessionFactoryHelper.Factory) {ForceFilter = true} diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs index 39179f1419a..1da84825bcf 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs @@ -373,8 +373,9 @@ private FromElement FindIntendedAliasedFromElementBasedOnCrazyJPARequirements(st return null; } - public void RegisterFromElement(FromElement element) + public void RegisterFromElement(FromElement element, out bool isFirst) { + isFirst = !_fromElements.Any(); _fromElements.Add(element); string classAlias = element.ClassAlias; if (classAlias != null) diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs index 79d42962748..b5064366e0a 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs @@ -10,7 +10,6 @@ using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Type; -using NHibernate.Util; using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Hql.Ast.ANTLR.Tree @@ -67,7 +66,7 @@ protected FromElement(FromClause fromClause,FromElement origin,string alias):thi protected void InitializeComponentJoin(FromElementType elementType) { _elementType = elementType; - _fromClause.RegisterFromElement(this); + _fromClause.RegisterFromElement(this, out _); _initialized = true; } @@ -739,7 +738,7 @@ public virtual string GetDisplayText() public void InitializeCollection(FromClause fromClause, string classAlias, string tableAlias) { - DoInitialize(fromClause, tableAlias, null, classAlias, null, null, null); + DoInitialize(fromClause, tableAlias, null, classAlias, null, null, null, out _); _initialized = true; } @@ -748,9 +747,10 @@ public void InitializeEntity(FromClause fromClause, IEntityPersister persister, EntityType type, string classAlias, - string tableAlias) + string tableAlias, + out bool isFirstElement) { - DoInitialize(fromClause, tableAlias, className, classAlias, persister, type, null); + DoInitialize(fromClause, tableAlias, className, classAlias, persister, type, null, out isFirstElement); _initialized = true; } @@ -762,7 +762,7 @@ internal void Initialize( string tableAlias, IEntityPersister persister) { - DoInitialize(fromClause, tableAlias, null, classAlias, persister, type, propertyMapping); + DoInitialize(fromClause, tableAlias, null, classAlias, persister, type, propertyMapping, out _); _initialized = true; } @@ -810,7 +810,7 @@ private void AddDestination(FromElement fromElement) } private void DoInitialize(FromClause fromClause, string tableAlias, string className, string classAlias, - IEntityPersister persister, IType type, IPropertyMapping propertyMapping) + IEntityPersister persister, IType type, IPropertyMapping propertyMapping, out bool isFirstElement) { if (_initialized) { @@ -827,7 +827,7 @@ private void DoInitialize(FromClause fromClause, string tableAlias, string class } // Register the FromElement with the FROM clause, now that we have the names and aliases. - fromClause.RegisterFromElement(this); + fromClause.RegisterFromElement(this, out isFirstElement); if (Log.IsDebugEnabled()) { diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs index 4f80d774f43..84167b59588 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs @@ -84,7 +84,8 @@ public FromElement AddFromElement() _classAlias, entityPersister, (EntityType)((IQueryable)entityPersister).Type, - null); + null, + out _); // Add to the query spaces. _fromClause.Walker.AddQuerySpaces(entityPersister); @@ -141,7 +142,8 @@ private FromElement CreateFromElementInSubselect( classAlias, entityPersister, (EntityType)((IQueryable)entityPersister).Type, - tableAlias + tableAlias, + out _ ); } if (Log.IsDebugEnabled()) @@ -254,8 +256,8 @@ public FromElement CreateElementJoin(IQueryableCollection queryableCollection) _classAlias, targetEntityPersister, (EntityType)queryableCollection.ElementType, - tableAlias - ); + tableAlias, + out _); // If the join is implied, then don't include sub-classes on the element. if (_implied) @@ -291,7 +293,7 @@ public FromElement CreateEntityJoin( bool inFrom, EntityType type) { - FromElement elem = CreateJoin(entityClass, tableAlias, joinSequence, type, false); + FromElement elem = CreateJoin(entityClass, tableAlias, joinSequence, type, false, out bool isFirstElement); elem.Fetch = fetchFlag; //if (numberOfTables > 1 && _implied && !elem.UseFromFragment) @@ -321,7 +323,7 @@ public FromElement CreateEntityJoin( // 1) 'elem' is the "root from-element" in correlated subqueries // 2) The DotNode.useThetaStyleImplicitJoins has been set to true // and 'elem' represents an implicit join - if (elem.FromClause != elem.Origin.FromClause || DotNode.UseThetaStyleImplicitJoins) + if (isFirstElement && elem.FromClause != elem.Origin.FromClause || DotNode.UseThetaStyleImplicitJoins) { // the "root from-element" in correlated subqueries do need this piece elem.Type = HqlSqlWalker.FROM_FRAGMENT; @@ -353,7 +355,7 @@ private FromElement CreateEntityAssociation( var joinSequence = CreateJoinSequence(roleAlias, joinType, implicitJoin); - elem = CreateJoin(associatedEntityName, roleAlias, joinSequence, (EntityType) _queryableCollection.ElementType, false); + elem = CreateJoin(associatedEntityName, roleAlias, joinSequence, (EntityType) _queryableCollection.ElementType, false, out _); elem.UseFromFragment |= elem.IsImplied && elem.Walker.IsSubQuery; } else @@ -416,7 +418,7 @@ private FromElement CreateManyToMany( { // For implied many-to-many, just add the end join. JoinSequence joinSequence = CreateJoinSequence(roleAlias, joinType, implicitJoin); - elem = CreateJoin(associatedEntityName, roleAlias, joinSequence, type, true); + elem = CreateJoin(associatedEntityName, roleAlias, joinSequence, type, true, out _); } else { @@ -429,7 +431,7 @@ private FromElement CreateManyToMany( // Add the second join, the one that ends in the destination table. JoinSequence joinSequence = CreateJoinSequence(roleAlias, joinType, implicitJoin); joinSequence.AddJoin(sfh.GetElementAssociationType(_collectionType), tableAlias, joinType, secondJoinColumns); - elem = CreateJoin(associatedEntityName, tableAlias, joinSequence, type, false); + elem = CreateJoin(associatedEntityName, tableAlias, joinSequence, type, false, out _); elem.UseFromFragment = true; } return elem; @@ -452,7 +454,8 @@ private FromElement CreateJoin( string tableAlias, JoinSequence joinSequence, EntityType type, - bool manyToMany) + bool manyToMany, + out bool isFirstElement) { // origin, path, implied, columns, classAlias, IEntityPersister entityPersister = _fromClause.SessionFactoryHelper.RequireClassPersister(entityClass); @@ -460,7 +463,8 @@ private FromElement CreateJoin( _classAlias, entityPersister, type, - tableAlias); + tableAlias, + out isFirstElement); return InitializeJoin(_path, destination, joinSequence, Columns, _origin, manyToMany); } @@ -528,14 +532,15 @@ private void InitializeAndAddFromElement(FromElement element, string classAlias, IEntityPersister entityPersister, EntityType type, - string tableAlias) + string tableAlias, + out bool isFirstElement) { if (tableAlias == null) { AliasGenerator aliasGenerator = _fromClause.AliasGenerator; tableAlias = aliasGenerator.CreateName(entityPersister.EntityName); } - element.InitializeEntity(_fromClause, className, entityPersister, type, classAlias, tableAlias); + element.InitializeEntity(_fromClause, className, entityPersister, type, classAlias, tableAlias, out isFirstElement); } private FromElement CreateAndAddFromElement( @@ -543,7 +548,8 @@ private FromElement CreateAndAddFromElement( string classAlias, IEntityPersister entityPersister, EntityType type, - string tableAlias) + string tableAlias, + out bool isFirstElement) { if (!(entityPersister is IJoinable)) { @@ -551,7 +557,7 @@ private FromElement CreateAndAddFromElement( } FromElement element = CreateFromElement(entityPersister); - InitializeAndAddFromElement(element, className, classAlias, entityPersister, type, tableAlias); + InitializeAndAddFromElement(element, className, classAlias, entityPersister, type, tableAlias, out isFirstElement); return element; } From def6ef838fb73e1e6118435c6f116a604c105902 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 26 Jun 2023 07:17:36 +0000 Subject: [PATCH 05/12] Generate async files --- .../Async/NHSpecificTest/GH3334/Fixture.cs | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/GH3334/Fixture.cs diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3334/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3334/Fixture.cs new file mode 100644 index 00000000000..059ef6350dd --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3334/Fixture.cs @@ -0,0 +1,169 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH3334 +{ + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { + [OneTimeSetUp] + public void OneTimeSetUp() + { + using (var session = OpenSession()) + using (var t = session.BeginTransaction()) + { + var parent = new Entity + { + Name = "Parent1", + Children = { new ChildEntity { Name = "Child", Child = new GrandChildEntity { Name = "GrandChild" } } } + }; + session.Save(parent); + parent = new Entity + { + Name = "Parent2", + Children = { new ChildEntity { Name = "Child", Child = new GrandChildEntity { Name = "XGrandChild" } } } + }; + var other = new OtherEntity { Name = "ABC", Entities = {parent}}; + parent.OtherEntity = other; + session.Save(parent); + session.Save(other); + t.Commit(); + } + + Sfi.Statistics.IsStatisticsEnabled = true; + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + Sfi.Statistics.IsStatisticsEnabled = false; + + using var session = OpenSession(); + using var transaction = session.BeginTransaction(); + + session.CreateQuery("delete from ChildEntity").ExecuteUpdate(); + session.CreateQuery("delete from GrandChildEntity").ExecuteUpdate(); + session.CreateQuery("delete from Entity").ExecuteUpdate(); + session.CreateQuery("delete from OtherEntity").ExecuteUpdate(); + + transaction.Commit(); + } + + public static IEnumerable GetNoExceptionOnExecuteQueryTestCases() + { + /* does not work because of inner join or theta join created for many-to-one + @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.Children) AS child + WHERE + child.Child.Name like 'G%' + OR ROOT.OtherEntity.Name like 'A%' + )");*/ + + yield return new("Basic Elements case 1 FoundViaGrandChildG", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.Children) AS child + LEFT JOIN child.Child AS grandChild + WHERE + grandChild.Name like 'G%' + )"); + yield return new("Basic Elements case 2 FoundViaOtherEntityA", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.OtherEntity) AS otherEntity + WHERE + otherEntity.Name like 'A%' + )"); + yield return new("HQL Elements FoundViaGrandChildG", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.Children) AS child + LEFT JOIN child.Child AS grandChild + LEFT JOIN ROOT.OtherEntity AS otherEntity + WHERE + grandChild.Name like 'G%' + OR otherEntity.Name like 'G%' + )"); + yield return new("HQL Elements FoundViaOtherEntityA", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ELEMENTS(ROOT.Children) AS child + LEFT JOIN child.Child AS grandChild + LEFT JOIN ROOT.OtherEntity AS otherEntity + WHERE + grandChild.Name like 'A%' + OR otherEntity.Name like 'A%' + )"); + yield return new("HQL Entity FoundViaGrandChildG", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ChildEntity AS child + LEFT JOIN child.Child AS grandChild + LEFT JOIN ROOT.OtherEntity AS otherEntity + WHERE + child.Parent = ROOT + AND ( + grandChild.Name like 'G%' + OR otherEntity.Name like 'G%' + ) + )"); + yield return new("HQL Entity FoundViaOtherEntityA", @" + SELECT ROOT + FROM Entity AS ROOT + WHERE + EXISTS + (FROM ChildEntity AS child + LEFT JOIN child.Child AS grandChild + LEFT JOIN ROOT.OtherEntity AS otherEntity + WHERE + child.Parent = ROOT + AND ( + grandChild.Name like 'A%' + OR otherEntity.Name like 'A%' + ) + )"); + } + + [Test, TestCaseSource(nameof(GetNoExceptionOnExecuteQueryTestCases))] + public async Task NoExceptionOnExecuteQueryAsync(TestCase testCase) + { + using var session = OpenSession(); + using var _ = session.BeginTransaction(); + + var q = session.CreateQuery(testCase.Hql); + Assert.AreEqual(1, (await (q.ListAsync())).Count); + } + + protected override bool CheckDatabaseWasCleaned() + { + // same set of objects for each test + return true; + } + } +} From 5d11263fc568c8fa2dcd13bd5b90ffa1deb1cbbd Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Tue, 27 Jun 2023 20:56:38 +1000 Subject: [PATCH 06/12] Fix build --- src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs index deda4cc87d9..035f8c9755c 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs @@ -56,7 +56,7 @@ public class TestCase public string Hql { get; } public int LineNumber { get; } - internal TestCase(string name, string hql, [CallerLineNumber] int lineNumber = 0) + public TestCase(string name, string hql, [CallerLineNumber] int lineNumber = 0) { Name = name; Hql = hql; From 7fd5d5b25d7283b7543b42da8999fe9cf8c281f4 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Tue, 27 Jun 2023 21:21:33 +1000 Subject: [PATCH 07/12] Remove breaking changes --- .../Ast/ANTLR/Tree/EntityJoinFromElement.cs | 2 +- .../Hql/Ast/ANTLR/Tree/FromClause.cs | 12 ++++- .../Hql/Ast/ANTLR/Tree/FromElement.cs | 15 +++--- .../Hql/Ast/ANTLR/Tree/FromElementFactory.cs | 54 +++++++++---------- 4 files changed, 43 insertions(+), 40 deletions(-) diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs index c5ab7635f88..8711100412a 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/EntityJoinFromElement.cs @@ -15,7 +15,7 @@ public EntityJoinFromElement(FromClause fromClause, IQueryable entityPersister, string tableAlias = fromClause.AliasGenerator.CreateName(entityPersister.EntityName); EntityType entityType = (EntityType) entityPersister.Type; - InitializeEntity(fromClause, entityPersister.EntityName, entityPersister, entityType, alias, tableAlias, out _); + InitializeEntity(fromClause, entityPersister.EntityName, entityPersister, entityType, alias, tableAlias); //NH Specific: hibernate uses special class EntityJoinJoinSequenceImpl JoinSequence = new JoinSequence(SessionFactoryHelper.Factory) {ForceFilter = true} diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs index 1da84825bcf..7b5d165486d 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs @@ -288,6 +288,8 @@ public bool IsSubQuery internal bool IsJoinSubQuery { get; set; } + internal bool HasRegisteredFromElements => _fromElements.Any(); + public string GetDisplayText() { return "FromClause{" + @@ -375,7 +377,12 @@ private FromElement FindIntendedAliasedFromElementBasedOnCrazyJPARequirements(st public void RegisterFromElement(FromElement element, out bool isFirst) { - isFirst = !_fromElements.Any(); + isFirst = !HasRegisteredFromElements; + RegisterFromElement(element); + } + + public void RegisterFromElement(FromElement element) + { _fromElements.Add(element); string classAlias = element.ClassAlias; if (classAlias != null) @@ -383,6 +390,7 @@ public void RegisterFromElement(FromElement element, out bool isFirst) // The HQL class alias refers to the class name. _fromElementByClassAlias.Add(classAlias, element); } + // Associate the table alias with the element. string tableAlias = element.TableAlias; if (tableAlias != null) @@ -392,7 +400,7 @@ public void RegisterFromElement(FromElement element, out bool isFirst) if (element.IsEntityJoin()) { - _appendFromElements.Add((EntityJoinFromElement) element); + _appendFromElements.Add((EntityJoinFromElement)element); } } diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs index b5064366e0a..fea5d1dd6d7 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs @@ -66,7 +66,7 @@ protected FromElement(FromClause fromClause,FromElement origin,string alias):thi protected void InitializeComponentJoin(FromElementType elementType) { _elementType = elementType; - _fromClause.RegisterFromElement(this, out _); + _fromClause.RegisterFromElement(this); _initialized = true; } @@ -738,7 +738,7 @@ public virtual string GetDisplayText() public void InitializeCollection(FromClause fromClause, string classAlias, string tableAlias) { - DoInitialize(fromClause, tableAlias, null, classAlias, null, null, null, out _); + DoInitialize(fromClause, tableAlias, null, classAlias, null, null, null); _initialized = true; } @@ -747,10 +747,9 @@ public void InitializeEntity(FromClause fromClause, IEntityPersister persister, EntityType type, string classAlias, - string tableAlias, - out bool isFirstElement) + string tableAlias) { - DoInitialize(fromClause, tableAlias, className, classAlias, persister, type, null, out isFirstElement); + DoInitialize(fromClause, tableAlias, className, classAlias, persister, type, null); _initialized = true; } @@ -762,7 +761,7 @@ internal void Initialize( string tableAlias, IEntityPersister persister) { - DoInitialize(fromClause, tableAlias, null, classAlias, persister, type, propertyMapping, out _); + DoInitialize(fromClause, tableAlias, null, classAlias, persister, type, propertyMapping); _initialized = true; } @@ -810,7 +809,7 @@ private void AddDestination(FromElement fromElement) } private void DoInitialize(FromClause fromClause, string tableAlias, string className, string classAlias, - IEntityPersister persister, IType type, IPropertyMapping propertyMapping, out bool isFirstElement) + IEntityPersister persister, IType type, IPropertyMapping propertyMapping) { if (_initialized) { @@ -827,7 +826,7 @@ private void DoInitialize(FromClause fromClause, string tableAlias, string class } // Register the FromElement with the FROM clause, now that we have the names and aliases. - fromClause.RegisterFromElement(this, out isFirstElement); + fromClause.RegisterFromElement(this); if (Log.IsDebugEnabled()) { diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs index 84167b59588..7f5e8334daa 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs @@ -84,8 +84,7 @@ public FromElement AddFromElement() _classAlias, entityPersister, (EntityType)((IQueryable)entityPersister).Type, - null, - out _); + null); // Add to the query spaces. _fromClause.Walker.AddQuerySpaces(entityPersister); @@ -142,8 +141,7 @@ private FromElement CreateFromElementInSubselect( classAlias, entityPersister, (EntityType)((IQueryable)entityPersister).Type, - tableAlias, - out _ + tableAlias ); } if (Log.IsDebugEnabled()) @@ -256,8 +254,7 @@ public FromElement CreateElementJoin(IQueryableCollection queryableCollection) _classAlias, targetEntityPersister, (EntityType)queryableCollection.ElementType, - tableAlias, - out _); + tableAlias); // If the join is implied, then don't include sub-classes on the element. if (_implied) @@ -293,7 +290,8 @@ public FromElement CreateEntityJoin( bool inFrom, EntityType type) { - FromElement elem = CreateJoin(entityClass, tableAlias, joinSequence, type, false, out bool isFirstElement); + var isFirstFromElement = !_fromClause.HasRegisteredFromElements; + FromElement elem = CreateJoin(entityClass, tableAlias, joinSequence, type, false); elem.Fetch = fetchFlag; //if (numberOfTables > 1 && _implied && !elem.UseFromFragment) @@ -323,7 +321,7 @@ public FromElement CreateEntityJoin( // 1) 'elem' is the "root from-element" in correlated subqueries // 2) The DotNode.useThetaStyleImplicitJoins has been set to true // and 'elem' represents an implicit join - if (isFirstElement && elem.FromClause != elem.Origin.FromClause || DotNode.UseThetaStyleImplicitJoins) + if (isFirstFromElement && elem.FromClause != elem.Origin.FromClause || DotNode.UseThetaStyleImplicitJoins) { // the "root from-element" in correlated subqueries do need this piece elem.Type = HqlSqlWalker.FROM_FRAGMENT; @@ -355,7 +353,7 @@ private FromElement CreateEntityAssociation( var joinSequence = CreateJoinSequence(roleAlias, joinType, implicitJoin); - elem = CreateJoin(associatedEntityName, roleAlias, joinSequence, (EntityType) _queryableCollection.ElementType, false, out _); + elem = CreateJoin(associatedEntityName, roleAlias, joinSequence, (EntityType) _queryableCollection.ElementType, false); elem.UseFromFragment |= elem.IsImplied && elem.Walker.IsSubQuery; } else @@ -418,7 +416,7 @@ private FromElement CreateManyToMany( { // For implied many-to-many, just add the end join. JoinSequence joinSequence = CreateJoinSequence(roleAlias, joinType, implicitJoin); - elem = CreateJoin(associatedEntityName, roleAlias, joinSequence, type, true, out _); + elem = CreateJoin(associatedEntityName, roleAlias, joinSequence, type, true); } else { @@ -431,7 +429,7 @@ private FromElement CreateManyToMany( // Add the second join, the one that ends in the destination table. JoinSequence joinSequence = CreateJoinSequence(roleAlias, joinType, implicitJoin); joinSequence.AddJoin(sfh.GetElementAssociationType(_collectionType), tableAlias, joinType, secondJoinColumns); - elem = CreateJoin(associatedEntityName, tableAlias, joinSequence, type, false, out _); + elem = CreateJoin(associatedEntityName, tableAlias, joinSequence, type, false); elem.UseFromFragment = true; } return elem; @@ -454,17 +452,15 @@ private FromElement CreateJoin( string tableAlias, JoinSequence joinSequence, EntityType type, - bool manyToMany, - out bool isFirstElement) + bool manyToMany) { // origin, path, implied, columns, classAlias, IEntityPersister entityPersister = _fromClause.SessionFactoryHelper.RequireClassPersister(entityClass); FromElement destination = CreateAndAddFromElement(entityClass, - _classAlias, - entityPersister, - type, - tableAlias, - out isFirstElement); + _classAlias, + entityPersister, + type, + tableAlias); return InitializeJoin(_path, destination, joinSequence, Columns, _origin, manyToMany); } @@ -527,20 +523,21 @@ private IASTNode CreateFromElement(string text) return ast; } - private void InitializeAndAddFromElement(FromElement element, - string className, - string classAlias, - IEntityPersister entityPersister, - EntityType type, - string tableAlias, - out bool isFirstElement) + private void InitializeAndAddFromElement( + FromElement element, + string className, + string classAlias, + IEntityPersister entityPersister, + EntityType type, + string tableAlias) { if (tableAlias == null) { AliasGenerator aliasGenerator = _fromClause.AliasGenerator; tableAlias = aliasGenerator.CreateName(entityPersister.EntityName); } - element.InitializeEntity(_fromClause, className, entityPersister, type, classAlias, tableAlias, out isFirstElement); + + element.InitializeEntity(_fromClause, className, entityPersister, type, classAlias, tableAlias); } private FromElement CreateAndAddFromElement( @@ -548,8 +545,7 @@ private FromElement CreateAndAddFromElement( string classAlias, IEntityPersister entityPersister, EntityType type, - string tableAlias, - out bool isFirstElement) + string tableAlias) { if (!(entityPersister is IJoinable)) { @@ -557,7 +553,7 @@ private FromElement CreateAndAddFromElement( } FromElement element = CreateFromElement(entityPersister); - InitializeAndAddFromElement(element, className, classAlias, entityPersister, type, tableAlias, out isFirstElement); + InitializeAndAddFromElement(element, className, classAlias, entityPersister, type, tableAlias); return element; } From 07b44a2cf703b28edb7a45eaddf47efe19c160bd Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Tue, 27 Jun 2023 21:26:32 +1000 Subject: [PATCH 08/12] Revert unnecessary changes --- .../Hql/Ast/ANTLR/Tree/FromClause.cs | 9 +------ .../Hql/Ast/ANTLR/Tree/FromElement.cs | 1 + .../Hql/Ast/ANTLR/Tree/FromElementFactory.cs | 25 +++++++++---------- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs index 7b5d165486d..1fbc18a3d32 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs @@ -375,12 +375,6 @@ private FromElement FindIntendedAliasedFromElementBasedOnCrazyJPARequirements(st return null; } - public void RegisterFromElement(FromElement element, out bool isFirst) - { - isFirst = !HasRegisteredFromElements; - RegisterFromElement(element); - } - public void RegisterFromElement(FromElement element) { _fromElements.Add(element); @@ -390,7 +384,6 @@ public void RegisterFromElement(FromElement element) // The HQL class alias refers to the class name. _fromElementByClassAlias.Add(classAlias, element); } - // Associate the table alias with the element. string tableAlias = element.TableAlias; if (tableAlias != null) @@ -400,7 +393,7 @@ public void RegisterFromElement(FromElement element) if (element.IsEntityJoin()) { - _appendFromElements.Add((EntityJoinFromElement)element); + _appendFromElements.Add((EntityJoinFromElement) element); } } diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs index fea5d1dd6d7..79d42962748 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs @@ -10,6 +10,7 @@ using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Type; +using NHibernate.Util; using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Hql.Ast.ANTLR.Tree diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs index 7f5e8334daa..3077d5e3af9 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs @@ -254,7 +254,8 @@ public FromElement CreateElementJoin(IQueryableCollection queryableCollection) _classAlias, targetEntityPersister, (EntityType)queryableCollection.ElementType, - tableAlias); + tableAlias + ); // If the join is implied, then don't include sub-classes on the element. if (_implied) @@ -457,10 +458,10 @@ private FromElement CreateJoin( // origin, path, implied, columns, classAlias, IEntityPersister entityPersister = _fromClause.SessionFactoryHelper.RequireClassPersister(entityClass); FromElement destination = CreateAndAddFromElement(entityClass, - _classAlias, - entityPersister, - type, - tableAlias); + _classAlias, + entityPersister, + type, + tableAlias); return InitializeJoin(_path, destination, joinSequence, Columns, _origin, manyToMany); } @@ -523,20 +524,18 @@ private IASTNode CreateFromElement(string text) return ast; } - private void InitializeAndAddFromElement( - FromElement element, - string className, - string classAlias, - IEntityPersister entityPersister, - EntityType type, - string tableAlias) + private void InitializeAndAddFromElement(FromElement element, + string className, + string classAlias, + IEntityPersister entityPersister, + EntityType type, + string tableAlias) { if (tableAlias == null) { AliasGenerator aliasGenerator = _fromClause.AliasGenerator; tableAlias = aliasGenerator.CreateName(entityPersister.EntityName); } - element.InitializeEntity(_fromClause, className, entityPersister, type, classAlias, tableAlias); } From 00d56062538dd640574b8b283bf9d43ce4c61894 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Tue, 27 Jun 2023 21:30:58 +1000 Subject: [PATCH 09/12] Rename TestCase to avoid name conflicts in async --- src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs index 035f8c9755c..aed5ab062ef 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH3334/Fixture.cs @@ -50,13 +50,13 @@ public void OneTimeTearDown() transaction.Commit(); } - public class TestCase + public class TestCaseItem { public string Name { get; } public string Hql { get; } public int LineNumber { get; } - public TestCase(string name, string hql, [CallerLineNumber] int lineNumber = 0) + public TestCaseItem(string name, string hql, [CallerLineNumber] int lineNumber = 0) { Name = name; Hql = hql; @@ -66,7 +66,7 @@ public TestCase(string name, string hql, [CallerLineNumber] int lineNumber = 0) public override string ToString() => $"{LineNumber:0000}: {Name}"; } - public static IEnumerable GetNoExceptionOnExecuteQueryTestCases() + public static IEnumerable GetNoExceptionOnExecuteQueryTestCases() { /* does not work because of inner join or theta join created for many-to-one @" @@ -156,13 +156,13 @@ OR otherEntity.Name like 'A%' } [Test, TestCaseSource(nameof(GetNoExceptionOnExecuteQueryTestCases))] - public void NoExceptionOnExecuteQuery(TestCase testCase) + public void NoExceptionOnExecuteQuery(TestCaseItem testCase) { using var session = OpenSession(); using var _ = session.BeginTransaction(); var q = session.CreateQuery(testCase.Hql); - Assert.AreEqual(1, q.List().Count); + Assert.That(q.List(), Has.Count.EqualTo(1)); } protected override bool CheckDatabaseWasCleaned() From f7ca5c3cf600f92e1c6f11bcac429b970f191301 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Jun 2023 11:33:38 +0000 Subject: [PATCH 10/12] Generate async files --- .../Async/NHSpecificTest/GH3334/Fixture.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3334/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3334/Fixture.cs index 059ef6350dd..8978ae20a39 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH3334/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3334/Fixture.cs @@ -61,7 +61,23 @@ public void OneTimeTearDown() transaction.Commit(); } - public static IEnumerable GetNoExceptionOnExecuteQueryTestCases() + public class TestCaseItem + { + public string Name { get; } + public string Hql { get; } + public int LineNumber { get; } + + public TestCaseItem(string name, string hql, [CallerLineNumber] int lineNumber = 0) + { + Name = name; + Hql = hql; + LineNumber = lineNumber; + } + + public override string ToString() => $"{LineNumber:0000}: {Name}"; + } + + public static IEnumerable GetNoExceptionOnExecuteQueryTestCases() { /* does not work because of inner join or theta join created for many-to-one @" @@ -151,13 +167,13 @@ OR otherEntity.Name like 'A%' } [Test, TestCaseSource(nameof(GetNoExceptionOnExecuteQueryTestCases))] - public async Task NoExceptionOnExecuteQueryAsync(TestCase testCase) + public async Task NoExceptionOnExecuteQueryAsync(TestCaseItem testCase) { using var session = OpenSession(); using var _ = session.BeginTransaction(); var q = session.CreateQuery(testCase.Hql); - Assert.AreEqual(1, (await (q.ListAsync())).Count); + Assert.That(await (q.ListAsync()), Has.Count.EqualTo(1)); } protected override bool CheckDatabaseWasCleaned() From df7333154d0ae59e4a1ca3d7806d0766ac7a018d Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Tue, 27 Jun 2023 15:28:00 +0300 Subject: [PATCH 11/12] Try removing hack added in #2990 (see 32fdfec) --- src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs index 3ff11867273..fc909aedf65 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs @@ -839,8 +839,6 @@ void CreateFromJoinElement( // or on an association join in subquery from a parent entity in parent from clause // like in "from EntityA a where exists (from EntityB join a.Assocation)" fromElement.FromClause.AppendFromElement(fromElement); - if (fromElement.IsImplied) - fromElement.JoinSequence.SetUseThetaStyle(true); } } From 4c543803614a6428c49acbbd0d7066b82bdd9ca4 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Tue, 27 Jun 2023 16:16:30 +0300 Subject: [PATCH 12/12] Revert "Try removing hack added in #2990 (see 32fdfec)" This reverts commit df7333154d0ae59e4a1ca3d7806d0766ac7a018d. --- src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs index fc909aedf65..3ff11867273 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs @@ -839,6 +839,8 @@ void CreateFromJoinElement( // or on an association join in subquery from a parent entity in parent from clause // like in "from EntityA a where exists (from EntityB join a.Assocation)" fromElement.FromClause.AppendFromElement(fromElement); + if (fromElement.IsImplied) + fromElement.JoinSequence.SetUseThetaStyle(true); } }