Skip to content

Commit 1b2df17

Browse files
committed
NH-3474 - Fix support of GroupBy with constant keys
* Remove the GROUP BY clause entirely if all keys are constant * Re-write the GROUP BY clause using only non-constant keys
1 parent f20a5e3 commit 1b2df17

File tree

3 files changed

+65
-7
lines changed

3 files changed

+65
-7
lines changed

src/NHibernate.Test/Linq/ByMethod/GroupByTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,46 @@ public void GroupByComputedValueInObjectArray()
549549
Assert.AreEqual(830, orderGroups.Sum(g => g.Count));
550550
}
551551

552+
[Test(Description = "NH-3474")]
553+
public void GroupByConstant()
554+
{
555+
var totals = db.Orders.GroupBy(o => 1).Select(g => new { Key = g.Key, Count = g.Count(), Sum = g.Sum(x => x.Freight) }).ToList();
556+
Assert.That(totals.Count, Is.EqualTo(1));
557+
Assert.That(totals, Has.All.With.Property("Key").EqualTo(1));
558+
}
559+
560+
[Test(Description = "NH-3474")]
561+
public void GroupByConstantAnonymousType()
562+
{
563+
var totals = db.Orders.GroupBy(o => new { A = 1 }).Select(g => new { Key = g.Key, Count = g.Count(), Sum = g.Sum(x => x.Freight) }).ToList();
564+
Assert.That(totals.Count, Is.EqualTo(1));
565+
Assert.That(totals, Has.All.With.Property("Key").With.Property("A").EqualTo(1));
566+
}
567+
568+
[Test(Description = "NH-3474")]
569+
public void GroupByConstantArray()
570+
{
571+
var totals = db.Orders.GroupBy(o => new object[] { 1 }).Select(g => new { Key = g.Key, Count = g.Count(), Sum = g.Sum(x => x.Freight) }).ToList();
572+
Assert.That(totals.Count, Is.EqualTo(1));
573+
Assert.That(totals, Has.All.With.Property("Key").EqualTo(new object[] { 1 }));
574+
}
575+
576+
[Test(Description = "NH-3474")]
577+
public void GroupByKeyWithConstantInAnonymousType()
578+
{
579+
var totals = db.Orders.GroupBy(o => new { A = 1, B = o.Shipper.ShipperId }).Select(g => new { Key = g.Key, Count = g.Count(), Sum = g.Sum(x => x.Freight) }).ToList();
580+
Assert.That(totals.Count, Is.EqualTo(3));
581+
Assert.That(totals, Has.All.With.Property("Key").With.Property("A").EqualTo(1));
582+
}
583+
584+
[Test(Description = "NH-3474")]
585+
public void GroupByKeyWithConstantInArray()
586+
{
587+
var totals = db.Orders.GroupBy(o => new[] { 1, o.Shipper.ShipperId }).Select(g => new { Key = g.Key, Count = g.Count(), Sum = g.Sum(x => x.Freight) }).ToList();
588+
Assert.That(totals.Count, Is.EqualTo(3));
589+
Assert.That(totals, Has.All.With.Property("Key").Contains(1));
590+
}
591+
552592
private static void CheckGrouping<TKey, TElement>(IEnumerable<IGrouping<TKey, TElement>> groupedItems, Func<TElement, TKey> groupBy)
553593
{
554594
var used = new HashSet<object>();

src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ public static void ReWrite(QueryModel queryModel, IList<Expression> groupByKeys)
5959
var groupBy = operators[0] as GroupResultOperator;
6060
if (groupBy != null)
6161
{
62-
groupBy.ExtractKeyExpressions(groupByKeys);
6362
FlattenSubQuery(queryModel, subQueryExpression.QueryModel, groupBy);
63+
RemoveCostantGroupByKeys(queryModel, groupBy).ForEach(groupByKeys.Add);
6464
}
6565
}
6666
}
@@ -104,5 +104,24 @@ private static void FlattenSubQuery(QueryModel queryModel, QueryModel subQueryMo
104104
// Replace the outer query source
105105
queryModel.MainFromClause = subQueryModel.MainFromClause;
106106
}
107+
108+
private static IEnumerable<Expression> RemoveCostantGroupByKeys(QueryModel queryModel, GroupResultOperator groupBy)
109+
{
110+
var keys = groupBy.ExtractKeyExpressions().Where(x => !(x is ConstantExpression)).ToList();
111+
112+
if (!keys.Any())
113+
{
114+
// Remove the Group By clause completely if all the keys are constant (redundant)
115+
queryModel.ResultOperators.Remove(groupBy);
116+
}
117+
else
118+
{
119+
// Re-write the KeySelector as an object array of the non-constant keys
120+
// This should be safe because we've already re-written the select clause using the original keys
121+
groupBy.KeySelector = Expression.NewArrayInit(typeof (object), keys.Select(x => x.Type.IsValueType ? Expression.Convert(x, typeof(object)) : x));
122+
}
123+
124+
return keys;
125+
}
107126
}
108127
}

src/NHibernate/Linq/GroupResultOperatorExtensions.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@ namespace NHibernate.Linq
1010
{
1111
internal static class GroupResultOperatorExtensions
1212
{
13-
public static void ExtractKeyExpressions(this GroupResultOperator groupResult, IList<Expression> groupByKeys)
13+
public static IEnumerable<Expression> ExtractKeyExpressions(this GroupResultOperator groupResult)
1414
{
1515
if (groupResult.KeySelector is NewExpression)
16-
(groupResult.KeySelector as NewExpression).Arguments.ForEach(groupByKeys.Add);
17-
else if (groupResult.KeySelector is NewArrayExpression)
18-
(groupResult.KeySelector as NewArrayExpression).Expressions.ForEach(groupByKeys.Add);
19-
else
20-
groupByKeys.Add(groupResult.KeySelector);
16+
return (groupResult.KeySelector as NewExpression).Arguments;
17+
if (groupResult.KeySelector is NewArrayExpression)
18+
return (groupResult.KeySelector as NewArrayExpression).Expressions;
19+
return new [] { groupResult.KeySelector };
2120
}
2221
}
2322
}

0 commit comments

Comments
 (0)