Skip to content

Commit f35874a

Browse files
bahusoidfredericDelaporte
authored andcommitted
Fix QueryOver Coalesce projection in Select (#2246)
1 parent 891c632 commit f35874a

File tree

5 files changed

+99
-8
lines changed

5 files changed

+99
-8
lines changed

src/NHibernate.Test/Async/Criteria/Lambda/ProjectIntegrationFixture.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,43 @@ var actual
134134
Assert.That((int) actual[1], Is.EqualTo(2), "distinct count by name");
135135
}
136136
}
137+
138+
[Test]
139+
public async Task ProjectionCanCoalesceInSelectAsync()
140+
{
141+
using (var s = OpenSession())
142+
using (s.BeginTransaction())
143+
{
144+
var actual
145+
= (await (s.QueryOver<Person>()
146+
.Select(x => x.Age.Coalesce(0))
147+
.Where(x => x.Age == 20)
148+
.ListAsync<int>())).FirstOrDefault();
149+
150+
Assert.That(actual, Is.EqualTo(20));
151+
}
152+
}
153+
154+
//NH-2983
155+
[Test]
156+
public async Task ProjectionSelectSumOnCoalesceAsync()
157+
{
158+
using (var s = OpenSession())
159+
using (s.BeginTransaction())
160+
{
161+
var actual
162+
= (await (s.QueryOver<Person>()
163+
.SelectList(
164+
l =>
165+
l
166+
.SelectSum(xx => xx.Age.Coalesce(0))
167+
.SelectSum(xx => xx.Age.Coalesce(1)))
168+
.Where(x => x.Age == 20)
169+
.ListAsync<object[]>())).FirstOrDefault();
170+
171+
Assert.That(actual[0], Is.EqualTo(20));
172+
Assert.That(actual[1], Is.EqualTo(20));
173+
}
174+
}
137175
}
138176
}

src/NHibernate.Test/Criteria/Lambda/ProjectIntegrationFixture.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,43 @@ var actual
123123
Assert.That((int) actual[1], Is.EqualTo(2), "distinct count by name");
124124
}
125125
}
126+
127+
[Test]
128+
public void ProjectionCanCoalesceInSelect()
129+
{
130+
using (var s = OpenSession())
131+
using (s.BeginTransaction())
132+
{
133+
var actual
134+
= s.QueryOver<Person>()
135+
.Select(x => x.Age.Coalesce(0))
136+
.Where(x => x.Age == 20)
137+
.List<int>().FirstOrDefault();
138+
139+
Assert.That(actual, Is.EqualTo(20));
140+
}
141+
}
142+
143+
//NH-2983
144+
[Test]
145+
public void ProjectionSelectSumOnCoalesce()
146+
{
147+
using (var s = OpenSession())
148+
using (s.BeginTransaction())
149+
{
150+
var actual
151+
= s.QueryOver<Person>()
152+
.SelectList(
153+
l =>
154+
l
155+
.SelectSum(xx => xx.Age.Coalesce(0))
156+
.SelectSum(xx => xx.Age.Coalesce(1)))
157+
.Where(x => x.Age == 20)
158+
.List<object[]>().FirstOrDefault();
159+
160+
Assert.That(actual[0], Is.EqualTo(20));
161+
Assert.That(actual[1], Is.EqualTo(20));
162+
}
163+
}
126164
}
127165
}

src/NHibernate.Test/Criteria/Lambda/RestrictionsFixture.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,9 @@ public void FunctionExtensions()
287287
.Add(Restrictions.Eq(Projections.SqlFunction("substring", NHibernateUtil.String, Projections.Property("Name"), Projections.Property("Age"), Projections.Constant(2)), "te"))
288288
.Add(Restrictions.Eq(Projections.SqlFunction("locate", NHibernateUtil.String, Projections.Constant("e"), Projections.Property("Name"), Projections.Constant(1)), 2))
289289
.Add(Restrictions.Eq(Projections.SqlFunction("locate", NHibernateUtil.String, Projections.Constant("e"), Projections.Property("Name"), Projections.Property("Age")), 2))
290-
.Add(Restrictions.Eq(Projections.SqlFunction("coalesce", NHibernateUtil.Object, Projections.Property("Name"), Projections.Constant("not-null-val")), "test"))
291-
.Add(Restrictions.Eq(Projections.SqlFunction("coalesce", NHibernateUtil.Object, Projections.Property("Name"), Projections.Property("Nickname")), "test"))
292-
.Add(Restrictions.Eq(Projections.SqlFunction("coalesce", NHibernateUtil.Object, Projections.Property("NullableIsParent"), Projections.Constant(true)), true))
290+
.Add(Restrictions.Eq(new SqlFunctionProjection("coalesce", Projections.Property("Name"), Projections.Property("Name"), Projections.Constant("not-null-val")), "test"))
291+
.Add(Restrictions.Eq(new SqlFunctionProjection("coalesce", Projections.Property("Name"), Projections.Property("Name"), Projections.Property("Nickname")), "test"))
292+
.Add(Restrictions.Eq(new SqlFunctionProjection("coalesce", Projections.Property("NullableIsParent"), Projections.Property("NullableIsParent"), Projections.Constant(true)), true))
293293
.Add(Restrictions.Eq(Projections.SqlFunction("concat", NHibernateUtil.String, Projections.Property("Name"), Projections.Constant(", "), Projections.Property("Name")), "test, test"))
294294
.Add(Restrictions.Eq(Projections.SqlFunction("mod", NHibernateUtil.Int32, Projections.Property("Height"), Projections.Constant(10)), 0))
295295
.Add(Restrictions.Eq(Projections.SqlFunction("mod", NHibernateUtil.Int32, Projections.Property("Height"), Projections.Property("Age")), 0));

src/NHibernate/Criterion/ProjectionsExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ internal static IProjection ProcessCoalesce(MethodCallExpression methodCallExpre
316316
{
317317
IProjection property = ExpressionProcessor.FindMemberProjection(methodCallExpression.Arguments[0]).AsProjection();
318318
var replaceValueIfIsNull = ExpressionProcessor.FindMemberProjection(methodCallExpression.Arguments[1]);
319-
return Projections.SqlFunction("coalesce", NHibernateUtil.Object, property, replaceValueIfIsNull.AsProjection());
319+
return new SqlFunctionProjection("coalesce", returnTypeProjection: property, property, replaceValueIfIsNull.AsProjection());
320320
}
321321

322322
/// <summary>

src/NHibernate/Criterion/SqlFunctionProjection.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
using System;
2-
using System.Collections;
32
using System.Collections.Generic;
3+
using System.Linq;
44
using NHibernate.Dialect.Function;
55
using NHibernate.Engine;
66
using NHibernate.SqlCommand;
77
using NHibernate.Type;
8-
using NHibernate.Util;
98

109
namespace NHibernate.Criterion
1110
{
@@ -16,6 +15,7 @@ public class SqlFunctionProjection : SimpleProjection
1615
private readonly ISQLFunction function;
1716
private readonly string functionName;
1817
private readonly IType returnType;
18+
private readonly IProjection returnTypeProjection;
1919

2020
public SqlFunctionProjection(string functionName, IType returnType, params IProjection[] args)
2121
{
@@ -31,6 +31,13 @@ public SqlFunctionProjection(ISQLFunction function, IType returnType, params IPr
3131
this.args = args;
3232
}
3333

34+
public SqlFunctionProjection(string functionName, IProjection returnTypeProjection, params IProjection[] args)
35+
{
36+
this.functionName = functionName;
37+
this.returnTypeProjection = returnTypeProjection;
38+
this.args = args;
39+
}
40+
3441
public override bool IsAggregate
3542
{
3643
get { return false; }
@@ -107,10 +114,18 @@ private static SqlString GetProjectionArgument(ICriteriaQuery criteriaQuery, ICr
107114
}
108115

109116
public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
117+
{
118+
var type = GetReturnType(criteria, criteriaQuery);
119+
return type != null ? new[] {type} : Array.Empty<IType>();
120+
}
121+
122+
private IType GetReturnType(ICriteria criteria, ICriteriaQuery criteriaQuery)
110123
{
111124
ISQLFunction sqlFunction = GetFunction(criteriaQuery);
112-
IType type = sqlFunction.ReturnType(returnType, criteriaQuery.Factory);
113-
return new IType[] {type};
125+
126+
var resultType = returnType ?? returnTypeProjection?.GetTypes(criteria, criteriaQuery).FirstOrDefault();
127+
128+
return sqlFunction.ReturnType(resultType, criteriaQuery.Factory);
114129
}
115130

116131
public override TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)

0 commit comments

Comments
 (0)