From 533922d4a9433b73e1fa1fe5e1aea403d6e2da04 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Sat, 12 Jun 2021 09:57:12 +0300 Subject: [PATCH] Fix null check for component with 4+ properties in hql --- .../NHSpecificTest/NH3634/FixtureByCode.cs | 62 ++++++++++++++++++ .../NHSpecificTest/NH3634/Connection.cs | 3 +- .../NHSpecificTest/NH3634/FixtureByCode.cs | 63 ++++++++++++++++++- .../NHSpecificTest/NH3634/PersonMapper.cs | 3 +- .../ANTLR/Tree/AbstractNullnessCheckNode.cs | 28 +++++---- 5 files changed, 145 insertions(+), 14 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3634/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3634/FixtureByCode.cs index 2c4cfc882d2..95fdf541a2d 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3634/FixtureByCode.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3634/FixtureByCode.cs @@ -8,10 +8,12 @@ //------------------------------------------------------------------------------ +using System.Linq; using NHibernate.Cfg.MappingSchema; using NHibernate.Criterion; using NHibernate.Mapping.ByCode; using NUnit.Framework; +using NHibernate.Linq; namespace NHibernate.Test.NHSpecificTest.NH3634 { @@ -316,6 +318,66 @@ public async Task CachedQueryAgainstComponentWithANullPropertyUsingCriteriaAsync } } + [Test] + public async Task QueryOverComponentIsNullAsync() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var person = await (session.QueryOver() + .Where(p => p.Connection == null) + .SingleOrDefaultAsync()); + Assert.That(person, Is.Null); + } + } + + [Test(Description = "GH-2822")] + public async Task LinqComponentIsNullAsync() + { + using (ISession session = OpenSession()) + { + var person = await (session.Query() + .Where(p => p.Name != null && p.Connection == null && p.Name != null) + .FirstOrDefaultAsync()); + Assert.That(person, Is.Null); + } + } + + [Test] + public async Task LinqComponentIsNotNullAsync() + { + using (ISession session = OpenSession()) + { + var person = await (session.Query() + .Where(p => p.Name != null && p.Connection != null && p.Name != null) + .FirstOrDefaultAsync()); + Assert.That(person, Is.Not.Null); + } + } + + [Test] + public async Task HqlComponentIsNullAsync() + { + using(new SqlLogSpy()) + using (ISession session = OpenSession()) + { + var p = await (session.CreateQuery("from Person where Connection is null").UniqueResultAsync()); + + Assert.That(p, Is.Null); + } + } + + [Test] + public async Task HqlComponentIsNotNullAsync() + { + using (ISession session = OpenSession()) + { + var p = await (session.CreateQuery("from Person where Connection is not null").SetMaxResults(1).UniqueResultAsync()); + + Assert.That(p, Is.Not.Null); + } + } + [Test] public async Task QueryOverANullComponentPropertyAsync() { diff --git a/src/NHibernate.Test/NHSpecificTest/NH3634/Connection.cs b/src/NHibernate.Test/NHSpecificTest/NH3634/Connection.cs index 93ed1522daf..a1111f20df5 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3634/Connection.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3634/Connection.cs @@ -5,5 +5,6 @@ class Connection public virtual string ConnectionType { get; set; } public virtual string Address { get; set; } public virtual string PortName { get; set; } + public virtual string PortName2 { get; set; } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3634/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/NH3634/FixtureByCode.cs index ef28a6f3e83..339f9fedbd7 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3634/FixtureByCode.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3634/FixtureByCode.cs @@ -1,4 +1,5 @@ -using NHibernate.Cfg.MappingSchema; +using System.Linq; +using NHibernate.Cfg.MappingSchema; using NHibernate.Criterion; using NHibernate.Mapping.ByCode; using NUnit.Framework; @@ -305,6 +306,66 @@ public void CachedQueryAgainstComponentWithANullPropertyUsingCriteria() } } + [Test] + public void QueryOverComponentIsNull() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var person = session.QueryOver() + .Where(p => p.Connection == null) + .SingleOrDefault(); + Assert.That(person, Is.Null); + } + } + + [Test(Description = "GH-2822")] + public void LinqComponentIsNull() + { + using (ISession session = OpenSession()) + { + var person = session.Query() + .Where(p => p.Name != null && p.Connection == null && p.Name != null) + .FirstOrDefault(); + Assert.That(person, Is.Null); + } + } + + [Test] + public void LinqComponentIsNotNull() + { + using (ISession session = OpenSession()) + { + var person = session.Query() + .Where(p => p.Name != null && p.Connection != null && p.Name != null) + .FirstOrDefault(); + Assert.That(person, Is.Not.Null); + } + } + + [Test] + public void HqlComponentIsNull() + { + using(new SqlLogSpy()) + using (ISession session = OpenSession()) + { + var p = session.CreateQuery("from Person where Connection is null").UniqueResult(); + + Assert.That(p, Is.Null); + } + } + + [Test] + public void HqlComponentIsNotNull() + { + using (ISession session = OpenSession()) + { + var p = session.CreateQuery("from Person where Connection is not null").SetMaxResults(1).UniqueResult(); + + Assert.That(p, Is.Not.Null); + } + } + [Test] public void QueryOverANullComponentProperty() { diff --git a/src/NHibernate.Test/NHSpecificTest/NH3634/PersonMapper.cs b/src/NHibernate.Test/NHSpecificTest/NH3634/PersonMapper.cs index 6e7844db955..01c72c9a6a5 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3634/PersonMapper.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3634/PersonMapper.cs @@ -19,7 +19,8 @@ public PersonMapper() m.Property(c => c.ConnectionType, mapper => mapper.NotNullable(true)); m.Property(c => c.Address, mapper => mapper.NotNullable(false)); m.Property(c => c.PortName, mapper => mapper.NotNullable(false)); + m.Property(c => c.PortName2, mapper => mapper.NotNullable(false)); }); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/AbstractNullnessCheckNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/AbstractNullnessCheckNode.cs index c836269f0e3..e3fd2efa7dd 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/AbstractNullnessCheckNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/AbstractNullnessCheckNode.cs @@ -75,20 +75,26 @@ private void MutateRowValueConstructorSyntax(int operandColumnSpan) { if ( i == 1 ) { - container.AddChildren( - ASTFactory.CreateNode(comparisonType, comparisonText, - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, mutationTexts[0])), - ASTFactory.CreateNode(comparisonType, comparisonText, - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, mutationTexts[1]))); + var op1 = ASTFactory.CreateNode(comparisonType, comparisonText); + var operand1 = ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, mutationTexts[0]); + op1.SetFirstChild(operand1); + container.SetFirstChild(op1); + + var op2 = ASTFactory.CreateNode(comparisonType, comparisonText); + var operand2 = ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, mutationTexts[1]); + op2.SetFirstChild(operand2); + op1.AddSibling(op2); } else { - container.AddChildren( - ASTFactory.CreateNode(expansionConnectorType, expansionConnectorText), - ASTFactory.CreateNode(comparisonType, comparisonText, - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, mutationTexts[i]))); - - container = GetChild(0); + var operand = ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, mutationTexts[i]); + var op = ASTFactory.CreateNode(comparisonType, comparisonText); + op.SetFirstChild(operand); + + var newContainer = ASTFactory.CreateNode(expansionConnectorType, expansionConnectorText); + container.SetFirstChild(newContainer); + newContainer.AddSibling(op); + container = newContainer; } } }