From 92585543ddc20adc536806f21c0a4cb69f621464 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Sun, 28 Mar 2021 23:40:24 +0300 Subject: [PATCH 1/2] Skip transparent cast for unknown types in hql conditional expression --- .../NHSpecificTest/GH2707/FixtureByCode.cs | 72 +++++++++ .../NHSpecificTest/GH2707/Entity.cs | 140 ++++++++++++++++++ .../NHSpecificTest/GH2707/FixtureByCode.cs | 60 ++++++++ .../Visitors/HqlGeneratorExpressionVisitor.cs | 2 +- 4 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2707/FixtureByCode.cs diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs new file mode 100644 index 00000000000..ed8a151bd42 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// 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.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; +using NHibernate.Linq; + +namespace NHibernate.Test.NHSpecificTest.GH2707 +{ + using System.Threading.Tasks; + [TestFixture] + public class ConditionalFixtureAsync : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + + mapper.AddMapping(); + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var e1 = new Entity1() {Id = "id1", IsChiusa = true}; + e1.CustomType = new MyType() {ToPersist = 1}; + session.Save(e1); + var e2 = new Entity1() {Id = "id2", IsChiusa = false}; + session.Save(e2); + e1.Parent = e1; + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + transaction.Commit(); + } + } + + [Test] + public async Task EntityAndCustomTypeInConditionalResultAsync() + { + using (var s = OpenSession()) + await ((from x in s.Query() + let parent = x.Parent + //NH-3005 - Contditional on custom type + where (parent.IsChiusa ? x.CustomType : parent.CustomType) == x.CustomType + select new + { + ParentIsChiusa = (((x == null) ? null : x.Parent) == null) + ? (bool?) null + : x.Parent.IsChiusa, + }).ToListAsync()); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs new file mode 100644 index 00000000000..ea6ff4c263c --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs @@ -0,0 +1,140 @@ +using System; +using System.Data.Common; +using NHibernate.Engine; +using NHibernate.Mapping.ByCode.Conformist; +using NHibernate.SqlTypes; +using NHibernate.UserTypes; + +namespace NHibernate.Test.NHSpecificTest.GH2707 +{ + public class Entity1 + { + public virtual string Id { get; set; } + public virtual bool IsChiusa { get; set; } + public virtual MyType CustomType { get; set; } + public virtual Entity1 Parent { get; set; } + } + + class Entity1Map : ClassMapping + { + public Entity1Map() + { + Table("TA"); + + Id(x => x.Id); + Property(x => x.IsChiusa); + Property(x => x.CustomType, m => m.Type()); + ManyToOne(x => x.Parent, x => x.ForeignKey("none")); + + } + } + + public class MyType + { + public int ToPersist { get; set; } + + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + var other = (MyType) obj; + return ToPersist == other.ToPersist; + } + + public override int GetHashCode() + { + return ToPersist.GetHashCode(); + } + } + + public class SimpleCustomType : IUserType + { + private static readonly SqlType[] ReturnSqlTypes = {SqlTypeFactory.Int32}; + + #region IUserType Members + + public new bool Equals(object x, object y) + { + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(null, x) || ReferenceEquals(null, y)) + { + return false; + } + + return x.Equals(y); + } + + public int GetHashCode(object x) + { + return (x == null) ? 0 : x.GetHashCode(); + } + + public SqlType[] SqlTypes + { + get { return ReturnSqlTypes; } + } + + public object DeepCopy(object value) + { + return value; + } + + public void NullSafeSet(DbCommand cmd, object value, int index, ISessionImplementor session) + { + if (value == null) + { + cmd.Parameters[index].Value = DBNull.Value; + } + else + { + cmd.Parameters[index].Value = ((MyType) value).ToPersist; + } + } + + public System.Type ReturnedType + { + get { return typeof(Int32); } + } + + public object NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner) + { + int index0 = rs.GetOrdinal(names[0]); + if (rs.IsDBNull(index0)) + { + return null; + } + + int value = rs.GetInt32(index0); + return new MyType {ToPersist = value}; + } + + public bool IsMutable + { + get { return false; } + } + + public object Replace(object original, object target, object owner) + { + return original; + } + + public object Assemble(object cached, object owner) + { + return cached; + } + + public object Disassemble(object value) + { + return value; + } + + #endregion + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2707/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/GH2707/FixtureByCode.cs new file mode 100644 index 00000000000..36809f21220 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2707/FixtureByCode.cs @@ -0,0 +1,60 @@ +using System.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH2707 +{ + [TestFixture] + public class ConditionalFixture : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + + mapper.AddMapping(); + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var e1 = new Entity1() {Id = "id1", IsChiusa = true}; + e1.CustomType = new MyType() {ToPersist = 1}; + session.Save(e1); + var e2 = new Entity1() {Id = "id2", IsChiusa = false}; + session.Save(e2); + e1.Parent = e1; + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + transaction.Commit(); + } + } + + [Test] + public void EntityAndCustomTypeInConditionalResult() + { + using (var s = OpenSession()) + (from x in s.Query() + let parent = x.Parent + //NH-3005 - Conditional with custom type + where (parent.IsChiusa ? x.CustomType : parent.CustomType) == x.CustomType + select new + { + ParentIsChiusa = (((x == null) ? null : x.Parent) == null) + ? (bool?) null + : x.Parent.IsChiusa, + }).ToList(); + } + } +} diff --git a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs index 2a285ff5311..63d9d3e2a1e 100644 --- a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs +++ b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs @@ -622,7 +622,7 @@ protected HqlTreeNode VisitConditionalExpression(ConditionalExpression expressio // If both operands are parameters, HQL will not be able to determine the resulting type before // parameters binding. But it has to compute result set columns type before parameters are bound, // so an artificial cast is introduced to hint HQL at the resulting type. - return expression.Type == typeof(bool) || expression.Type == typeof(bool?) + return expression.Type == typeof(bool) || expression.Type == typeof(bool?) || !HqlIdent.SupportsType(expression.Type) ? @case : _hqlTreeBuilder.TransparentCast(@case, expression.Type); } From 25cb78c800f1890d7b2019f2ca4f4207525b9335 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Mon, 29 Mar 2021 00:01:23 +0300 Subject: [PATCH 2/2] formatting --- src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs index ea6ff4c263c..46d557151d7 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH2707/Entity.cs @@ -25,7 +25,6 @@ public Entity1Map() Property(x => x.IsChiusa); Property(x => x.CustomType, m => m.Type()); ManyToOne(x => x.Parent, x => x.ForeignKey("none")); - } }