diff --git a/src/NHibernate.Test/NHSpecificTest/NH3951/Entity.cs b/src/NHibernate.Test/NHSpecificTest/NH3951/Entity.cs new file mode 100644 index 00000000000..569e049c35b --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3951/Entity.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.NH3951 +{ + class Entity + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + public virtual Guid? RelatedId { get; set; } + + public virtual ISet Related { get; set; } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH3951/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3951/Fixture.cs new file mode 100644 index 00000000000..175d73721b5 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3951/Fixture.cs @@ -0,0 +1,105 @@ +using System.Linq; +using NHibernate.Linq; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH3951 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnSetUp() + { + using (ISession session = OpenSession()) + using (ITransaction transaction = session.BeginTransaction()) + { + var e1 = new Entity {Name = "Bob"}; + session.Save(e1); + + var e2 = new Entity {Name = "Sally"}; + session.Save(e2); + + session.Flush(); + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (ISession session = OpenSession()) + using (ITransaction transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + session.Flush(); + transaction.Commit(); + } + } + + [Test] + public void AllNamedBob() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var result = session.Query() + .All(e => e.Name == "Bob"); + + Assert.AreEqual(false, result); + } + } + + [Test] + public void AllNamedWithAtLeast3Char() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var result = session.Query() + .All(e => e.Name.Length > 2); + + Assert.AreEqual(true, result); + } + } + + [Test] + public void AllNamedBobWorkaround() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var result = !session.Query() + .Any(e => e.Name != "Bob"); + + Assert.AreEqual(false, result); + } + } + + [Test] + public void AllNamedWithAtLeast3CharWorkaround() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var result = !session.Query() + .Any(e => e.Name.Length < 3); + + Assert.AreEqual(true, result); + } + } + + [Test] + public void AnyAndAllInSubQueries() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var result = session.Query() + .Select(e => new { e.Id, hasRelated = e.Related.Any(), allBobRelated = e.Related.All(r => r.Name == "Bob") }) + .ToList(); + + Assert.AreEqual(false, result[0].hasRelated); + Assert.AreEqual(true, result[0].allBobRelated); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH3951/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH3951/Mappings.hbm.xml new file mode 100644 index 00000000000..1e518fc13e5 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3951/Mappings.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index faa4c8486b9..bc3ee315644 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -749,6 +749,8 @@ + + @@ -3227,6 +3229,7 @@ + diff --git a/src/NHibernate/Linq/IntermediateHqlTree.cs b/src/NHibernate/Linq/IntermediateHqlTree.cs index 80278ea6eca..d20f3d33275 100644 --- a/src/NHibernate/Linq/IntermediateHqlTree.cs +++ b/src/NHibernate/Linq/IntermediateHqlTree.cs @@ -30,6 +30,14 @@ public class IntermediateHqlTree private HqlTreeNode _root; private HqlOrderBy _orderBy; + public bool IsRoot + { + get + { + return _isRoot; + } + } + public HqlTreeNode Root { get @@ -53,11 +61,6 @@ public IntermediateHqlTree(bool root) public ExpressionToHqlTranslationResults GetTranslation() { - if (_isRoot) - { - DetectOuterExists(); - } - return new ExpressionToHqlTranslationResults(Root, _itemTransformers, _listTransformers, @@ -201,19 +204,6 @@ public void AddHavingClause(HqlBooleanExpression where) } } - private void DetectOuterExists() - { - if (_root is HqlExists) - { - _takeCount = TreeBuilder.Constant(1); - _root = Root.Children.First(); - - Expression, bool>> x = l => l.Any(); - - _listTransformers.Add(x); - } - } - public void AddAdditionalCriteria(Action>> criteria) { _additionalCriteria.Add(criteria); diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAll.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAll.cs index 98bf8cd0288..84d62bb367f 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAll.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAll.cs @@ -1,17 +1,31 @@ -using NHibernate.Hql.Ast; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using NHibernate.Hql.Ast; using Remotion.Linq.Clauses.ResultOperators; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { - public class ProcessAll : IResultOperatorProcessor - { - public void Process(AllResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) - { - tree.AddWhereClause(tree.TreeBuilder.BooleanNot( - HqlGeneratorExpressionTreeVisitor.Visit(resultOperator.Predicate, queryModelVisitor.VisitorParameters). - ToBooleanExpression())); + public class ProcessAll : IResultOperatorProcessor + { + public void Process(AllResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) + { + tree.AddWhereClause(tree.TreeBuilder.BooleanNot( + HqlGeneratorExpressionTreeVisitor.Visit(resultOperator.Predicate, queryModelVisitor.VisitorParameters). + ToBooleanExpression())); - tree.SetRoot(tree.TreeBuilder.BooleanNot(tree.TreeBuilder.Exists((HqlQuery) tree.Root))); - } - } + if (tree.IsRoot) + { + tree.AddTakeClause(tree.TreeBuilder.Constant(1)); + + Expression, bool>> x = l => !l.Any(); + tree.AddListTransformer(x); + } + else + { + tree.SetRoot(tree.TreeBuilder.BooleanNot(tree.TreeBuilder.Exists((HqlQuery)tree.Root))); + } + } + } } \ No newline at end of file diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAny.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAny.cs index 7a8a91a8a0a..7831b9a2fa4 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAny.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAny.cs @@ -1,13 +1,27 @@ -using NHibernate.Hql.Ast; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using NHibernate.Hql.Ast; using Remotion.Linq.Clauses.ResultOperators; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { - public class ProcessAny : IResultOperatorProcessor - { - public void Process(AnyResultOperator anyOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) - { - tree.SetRoot(tree.TreeBuilder.Exists((HqlQuery) tree.Root)); - } - } + public class ProcessAny : IResultOperatorProcessor + { + public void Process(AnyResultOperator anyOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) + { + if (tree.IsRoot) + { + tree.AddTakeClause(tree.TreeBuilder.Constant(1)); + + Expression, bool>> x = l => l.Any(); + tree.AddListTransformer(x); + } + else + { + tree.SetRoot(tree.TreeBuilder.Exists((HqlQuery)tree.Root)); + } + } + } } \ No newline at end of file