Skip to content

Commit 30d9a2c

Browse files
authored
Skip transparent cast for unknown ident types in hql conditional expression (#2713)
1 parent 19ddf5c commit 30d9a2c

File tree

4 files changed

+272
-1
lines changed

4 files changed

+272
-1
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Linq;
12+
using NHibernate.Cfg.MappingSchema;
13+
using NHibernate.Mapping.ByCode;
14+
using NUnit.Framework;
15+
using NHibernate.Linq;
16+
17+
namespace NHibernate.Test.NHSpecificTest.GH2707
18+
{
19+
using System.Threading.Tasks;
20+
[TestFixture]
21+
public class ConditionalFixtureAsync : TestCaseMappingByCode
22+
{
23+
protected override HbmMapping GetMappings()
24+
{
25+
var mapper = new ModelMapper();
26+
27+
mapper.AddMapping<Entity1Map>();
28+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
29+
}
30+
31+
protected override void OnSetUp()
32+
{
33+
using (var session = OpenSession())
34+
using (var transaction = session.BeginTransaction())
35+
{
36+
var e1 = new Entity1() {Id = "id1", IsChiusa = true};
37+
e1.CustomType = new MyType() {ToPersist = 1};
38+
session.Save(e1);
39+
var e2 = new Entity1() {Id = "id2", IsChiusa = false};
40+
session.Save(e2);
41+
e1.Parent = e1;
42+
transaction.Commit();
43+
}
44+
}
45+
46+
protected override void OnTearDown()
47+
{
48+
using (var session = OpenSession())
49+
using (var transaction = session.BeginTransaction())
50+
{
51+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
52+
transaction.Commit();
53+
}
54+
}
55+
56+
[Test]
57+
public async Task EntityAndCustomTypeInConditionalResultAsync()
58+
{
59+
using (var s = OpenSession())
60+
await ((from x in s.Query<Entity1>()
61+
let parent = x.Parent
62+
//NH-3005 - Contditional on custom type
63+
where (parent.IsChiusa ? x.CustomType : parent.CustomType) == x.CustomType
64+
select new
65+
{
66+
ParentIsChiusa = (((x == null) ? null : x.Parent) == null)
67+
? (bool?) null
68+
: x.Parent.IsChiusa,
69+
}).ToListAsync());
70+
}
71+
}
72+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
using System;
2+
using System.Data.Common;
3+
using NHibernate.Engine;
4+
using NHibernate.Mapping.ByCode.Conformist;
5+
using NHibernate.SqlTypes;
6+
using NHibernate.UserTypes;
7+
8+
namespace NHibernate.Test.NHSpecificTest.GH2707
9+
{
10+
public class Entity1
11+
{
12+
public virtual string Id { get; set; }
13+
public virtual bool IsChiusa { get; set; }
14+
public virtual MyType CustomType { get; set; }
15+
public virtual Entity1 Parent { get; set; }
16+
}
17+
18+
class Entity1Map : ClassMapping<Entity1>
19+
{
20+
public Entity1Map()
21+
{
22+
Table("TA");
23+
24+
Id(x => x.Id);
25+
Property(x => x.IsChiusa);
26+
Property(x => x.CustomType, m => m.Type<SimpleCustomType>());
27+
ManyToOne(x => x.Parent, x => x.ForeignKey("none"));
28+
}
29+
}
30+
31+
public class MyType
32+
{
33+
public int ToPersist { get; set; }
34+
35+
public override bool Equals(object obj)
36+
{
37+
if (obj == null || GetType() != obj.GetType())
38+
{
39+
return false;
40+
}
41+
42+
var other = (MyType) obj;
43+
return ToPersist == other.ToPersist;
44+
}
45+
46+
public override int GetHashCode()
47+
{
48+
return ToPersist.GetHashCode();
49+
}
50+
}
51+
52+
public class SimpleCustomType : IUserType
53+
{
54+
private static readonly SqlType[] ReturnSqlTypes = {SqlTypeFactory.Int32};
55+
56+
#region IUserType Members
57+
58+
public new bool Equals(object x, object y)
59+
{
60+
if (ReferenceEquals(x, y))
61+
{
62+
return true;
63+
}
64+
65+
if (ReferenceEquals(null, x) || ReferenceEquals(null, y))
66+
{
67+
return false;
68+
}
69+
70+
return x.Equals(y);
71+
}
72+
73+
public int GetHashCode(object x)
74+
{
75+
return (x == null) ? 0 : x.GetHashCode();
76+
}
77+
78+
public SqlType[] SqlTypes
79+
{
80+
get { return ReturnSqlTypes; }
81+
}
82+
83+
public object DeepCopy(object value)
84+
{
85+
return value;
86+
}
87+
88+
public void NullSafeSet(DbCommand cmd, object value, int index, ISessionImplementor session)
89+
{
90+
if (value == null)
91+
{
92+
cmd.Parameters[index].Value = DBNull.Value;
93+
}
94+
else
95+
{
96+
cmd.Parameters[index].Value = ((MyType) value).ToPersist;
97+
}
98+
}
99+
100+
public System.Type ReturnedType
101+
{
102+
get { return typeof(Int32); }
103+
}
104+
105+
public object NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner)
106+
{
107+
int index0 = rs.GetOrdinal(names[0]);
108+
if (rs.IsDBNull(index0))
109+
{
110+
return null;
111+
}
112+
113+
int value = rs.GetInt32(index0);
114+
return new MyType {ToPersist = value};
115+
}
116+
117+
public bool IsMutable
118+
{
119+
get { return false; }
120+
}
121+
122+
public object Replace(object original, object target, object owner)
123+
{
124+
return original;
125+
}
126+
127+
public object Assemble(object cached, object owner)
128+
{
129+
return cached;
130+
}
131+
132+
public object Disassemble(object value)
133+
{
134+
return value;
135+
}
136+
137+
#endregion
138+
}
139+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.Linq;
2+
using NHibernate.Cfg.MappingSchema;
3+
using NHibernate.Mapping.ByCode;
4+
using NUnit.Framework;
5+
6+
namespace NHibernate.Test.NHSpecificTest.GH2707
7+
{
8+
[TestFixture]
9+
public class ConditionalFixture : TestCaseMappingByCode
10+
{
11+
protected override HbmMapping GetMappings()
12+
{
13+
var mapper = new ModelMapper();
14+
15+
mapper.AddMapping<Entity1Map>();
16+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
17+
}
18+
19+
protected override void OnSetUp()
20+
{
21+
using (var session = OpenSession())
22+
using (var transaction = session.BeginTransaction())
23+
{
24+
var e1 = new Entity1() {Id = "id1", IsChiusa = true};
25+
e1.CustomType = new MyType() {ToPersist = 1};
26+
session.Save(e1);
27+
var e2 = new Entity1() {Id = "id2", IsChiusa = false};
28+
session.Save(e2);
29+
e1.Parent = e1;
30+
transaction.Commit();
31+
}
32+
}
33+
34+
protected override void OnTearDown()
35+
{
36+
using (var session = OpenSession())
37+
using (var transaction = session.BeginTransaction())
38+
{
39+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
40+
transaction.Commit();
41+
}
42+
}
43+
44+
[Test]
45+
public void EntityAndCustomTypeInConditionalResult()
46+
{
47+
using (var s = OpenSession())
48+
(from x in s.Query<Entity1>()
49+
let parent = x.Parent
50+
//NH-3005 - Conditional with custom type
51+
where (parent.IsChiusa ? x.CustomType : parent.CustomType) == x.CustomType
52+
select new
53+
{
54+
ParentIsChiusa = (((x == null) ? null : x.Parent) == null)
55+
? (bool?) null
56+
: x.Parent.IsChiusa,
57+
}).ToList();
58+
}
59+
}
60+
}

src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ protected HqlTreeNode VisitConditionalExpression(ConditionalExpression expressio
622622
// If both operands are parameters, HQL will not be able to determine the resulting type before
623623
// parameters binding. But it has to compute result set columns type before parameters are bound,
624624
// so an artificial cast is introduced to hint HQL at the resulting type.
625-
return expression.Type == typeof(bool) || expression.Type == typeof(bool?)
625+
return expression.Type == typeof(bool) || expression.Type == typeof(bool?) || !HqlIdent.SupportsType(expression.Type)
626626
? @case
627627
: _hqlTreeBuilder.TransparentCast(@case, expression.Type);
628628
}

0 commit comments

Comments
 (0)