diff --git a/src/NHibernate.Test/Linq/ByMethod/GroupByHavingTests.cs b/src/NHibernate.Test/Linq/ByMethod/GroupByHavingTests.cs
index 818d7593722..7b4e4cf3c50 100644
--- a/src/NHibernate.Test/Linq/ByMethod/GroupByHavingTests.cs
+++ b/src/NHibernate.Test/Linq/ByMethod/GroupByHavingTests.cs
@@ -135,5 +135,23 @@ public void SingleKeyGroupAndCountWithHavingClause()
var hornRow = orderCounts.Single(row => row.CompanyName == "Around the Horn");
Assert.That(hornRow.OrderCount, Is.EqualTo(13));
}
+
+ [Test, Explicit("Demonstrate an unsupported case for PagingRewriter")]
+ public void SingleKeyGroupAndCountWithHavingClausePagingAndOuterWhere()
+ {
+ var orderCounts = db.Orders
+ .GroupBy(o => o.Customer.CompanyName)
+ .Where(g => g.Count() > 10)
+ .Select(g => new { CompanyName = g.Key, OrderCount = g.Count() })
+ .OrderBy(oc => oc.CompanyName)
+ .Skip(5)
+ .Take(10)
+ .Where(oc => oc.CompanyName.Contains("F"))
+ .ToList();
+
+ Assert.That(orderCounts, Has.Count.EqualTo(3));
+ var frankRow = orderCounts.Single(row => row.CompanyName == "Frankenversand");
+ Assert.That(frankRow.OrderCount, Is.EqualTo(15));
+ }
}
}
diff --git a/src/NHibernate/Linq/Clauses/NhHavingClause.cs b/src/NHibernate/Linq/Clauses/NhHavingClause.cs
deleted file mode 100644
index 131043ecc92..00000000000
--- a/src/NHibernate/Linq/Clauses/NhHavingClause.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Remotion.Linq.Clauses;
-using Remotion.Linq.Clauses.ExpressionTreeVisitors;
-using System.Linq.Expressions;
-
-namespace NHibernate.Linq.Clauses
-{
- public class NhHavingClause : WhereClause
- {
- public NhHavingClause(Expression predicate)
- : base(predicate)
- {
- }
-
- public override string ToString()
- {
- return "having " + FormattingExpressionTreeVisitor.Format(Predicate);
- }
- }
-}
\ No newline at end of file
diff --git a/src/NHibernate/Linq/Clauses/NhJoinClause.cs b/src/NHibernate/Linq/Clauses/NhJoinClause.cs
deleted file mode 100644
index 944a8248d4d..00000000000
--- a/src/NHibernate/Linq/Clauses/NhJoinClause.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq.Expressions;
-using NHibernate.Linq.Visitors;
-using Remotion.Linq.Clauses;
-using Remotion.Linq.Clauses.Expressions;
-
-namespace NHibernate.Linq.Clauses
-{
- ///
- /// All joins are created as outer joins. An optimization in finds
- /// joins that may be inner joined and calls on them.
- /// 's will
- /// then emit the correct HQL join.
- ///
- public class NhJoinClause : AdditionalFromClause
- {
- public NhJoinClause(string itemName, System.Type itemType, Expression fromExpression)
- : this(itemName, itemType, fromExpression, new NhWithClause[0])
- {
- }
-
- public NhJoinClause(string itemName, System.Type itemType, Expression fromExpression, IEnumerable restrictions)
- : base(itemName, itemType, fromExpression)
- {
- Restrictions = new ObservableCollection();
- foreach (var withClause in restrictions)
- Restrictions.Add(withClause);
- IsInner = false;
- }
-
- public ObservableCollection Restrictions { get; private set; }
-
- public bool IsInner { get; private set; }
-
- public override AdditionalFromClause Clone(CloneContext cloneContext)
- {
- var joinClause = new NhJoinClause(ItemName, ItemType, FromExpression);
- foreach (var withClause in Restrictions)
- {
- var withClause2 = new NhWithClause(withClause.Predicate);
- joinClause.Restrictions.Add(withClause2);
- }
-
- cloneContext.QuerySourceMapping.AddMapping(this, new QuerySourceReferenceExpression(joinClause));
- return base.Clone(cloneContext);
- }
-
- public void MakeInner()
- {
- IsInner = true;
- }
-
- public override void TransformExpressions(Func transformation)
- {
- foreach (var withClause in Restrictions)
- withClause.TransformExpressions(transformation);
- base.TransformExpressions(transformation);
- }
- }
-}
\ No newline at end of file
diff --git a/src/NHibernate/Linq/Clauses/NhWithClause.cs b/src/NHibernate/Linq/Clauses/NhWithClause.cs
deleted file mode 100644
index ae21bc0f4a6..00000000000
--- a/src/NHibernate/Linq/Clauses/NhWithClause.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Linq.Expressions;
-using Remotion.Linq.Clauses;
-using Remotion.Linq.Clauses.ExpressionTreeVisitors;
-
-namespace NHibernate.Linq.Clauses
-{
- public class NhWithClause : WhereClause
- {
- public NhWithClause(Expression predicate)
- : base(predicate)
- {
- }
-
- public override string ToString()
- {
- return "with " + FormattingExpressionTreeVisitor.Format(Predicate);
- }
- }
-}
diff --git a/src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs b/src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs
index 8150d0ae5df..316ba170add 100644
--- a/src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs
+++ b/src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
-using NHibernate.Linq.Clauses;
using NHibernate.Linq.ReWriters;
using NHibernate.Linq.Visitors;
using Remotion.Linq;
@@ -43,11 +42,9 @@ public static class AggregatingGroupByRewriter
typeof (CacheableResultOperator)
};
- public static void ReWrite(QueryModel queryModel)
+ public static void ReWrite(QueryModel queryModel, VisitorParameters parameters)
{
- var subQueryExpression = queryModel.MainFromClause.FromExpression as SubQueryExpression;
-
- if (subQueryExpression != null)
+ if (queryModel.MainFromClause.FromExpression is SubQueryExpression subQueryExpression)
{
var operators = subQueryExpression.QueryModel.ResultOperators
.Where(x => !QueryReferenceExpressionFlattener.FlattenableResultOperators.Contains(x.GetType()))
@@ -55,17 +52,16 @@ public static void ReWrite(QueryModel queryModel)
if (operators.Length == 1)
{
- var groupBy = operators[0] as GroupResultOperator;
- if (groupBy != null)
+ if (operators[0] is GroupResultOperator groupBy)
{
- FlattenSubQuery(queryModel, subQueryExpression.QueryModel, groupBy);
+ FlattenSubQuery(queryModel, subQueryExpression.QueryModel, groupBy, parameters);
RemoveCostantGroupByKeys(queryModel, groupBy);
}
}
}
}
- private static void FlattenSubQuery(QueryModel queryModel, QueryModel subQueryModel, GroupResultOperator groupBy)
+ private static void FlattenSubQuery(QueryModel queryModel, QueryModel subQueryModel, GroupResultOperator groupBy, VisitorParameters parameters)
{
foreach (var resultOperator in queryModel.ResultOperators.Where(resultOperator => !AcceptableOuterResultOperators.Contains(resultOperator.GetType())))
{
@@ -81,11 +77,9 @@ private static void FlattenSubQuery(QueryModel queryModel, QueryModel subQueryMo
clause.TransformExpressions(s => GroupBySelectClauseRewriter.ReWrite(s, groupBy, subQueryModel));
//all outer where clauses actually are having clauses
- var whereClause = clause as WhereClause;
- if (whereClause != null)
+ if (clause is WhereClause whereClause)
{
- queryModel.BodyClauses.RemoveAt(i);
- queryModel.BodyClauses.Insert(i, new NhHavingClause(whereClause.Predicate));
+ parameters.AddHavingClause(whereClause);
}
}
diff --git a/src/NHibernate/Linq/GroupBy/PagingRewriter.cs b/src/NHibernate/Linq/GroupBy/PagingRewriter.cs
index 922f1790a10..87d89e4f2ee 100644
--- a/src/NHibernate/Linq/GroupBy/PagingRewriter.cs
+++ b/src/NHibernate/Linq/GroupBy/PagingRewriter.cs
@@ -9,17 +9,16 @@ namespace NHibernate.Linq.GroupBy
{
internal static class PagingRewriter
{
- private static readonly System.Type[] PagingResultOperators = new[]
- {
- typeof (SkipResultOperator),
- typeof (TakeResultOperator),
- };
+ private static readonly System.Type[] PagingResultOperators =
+ new[]
+ {
+ typeof (SkipResultOperator),
+ typeof (TakeResultOperator),
+ };
public static void ReWrite(QueryModel queryModel)
{
- var subQueryExpression = queryModel.MainFromClause.FromExpression as SubQueryExpression;
-
- if (subQueryExpression != null &&
+ if (queryModel.MainFromClause.FromExpression is SubQueryExpression subQueryExpression &&
subQueryExpression.QueryModel.ResultOperators.All(x => PagingResultOperators.Contains(x.GetType())))
{
FlattenSubQuery(subQueryExpression, queryModel);
@@ -28,7 +27,7 @@ public static void ReWrite(QueryModel queryModel)
private static void FlattenSubQuery(SubQueryExpression subQueryExpression, QueryModel queryModel)
{
- // we can not flattern subquery if outer query has body clauses.
+ // we can not flatten subquery if outer query has body clauses.
var subQueryModel = subQueryExpression.QueryModel;
var subQueryMainFromClause = subQueryModel.MainFromClause;
if (queryModel.BodyClauses.Count == 0)
@@ -46,9 +45,13 @@ private static void FlattenSubQuery(SubQueryExpression subQueryExpression, Query
{
var cro = new ContainsResultOperator(new QuerySourceReferenceExpression(subQueryMainFromClause));
+ // Cloning may cause having/join/with clauses listed in VisitorParameters to no more be matched.
+ // Not a problem for now, because those clauses imply a projection, which is not supported
+ // by the "new WhereClause(new SubQueryExpression(newSubQueryModel))" below. See
+ // SingleKeyGroupAndCountWithHavingClausePagingAndOuterWhere test by example.
var newSubQueryModel = subQueryModel.Clone();
newSubQueryModel.ResultOperators.Add(cro);
- newSubQueryModel.ResultTypeOverride = typeof (bool);
+ newSubQueryModel.ResultTypeOverride = typeof(bool);
var where = new WhereClause(new SubQueryExpression(newSubQueryModel));
queryModel.BodyClauses.Add(where);
diff --git a/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs b/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs
index 37ab62de54b..61a057d97e6 100644
--- a/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs
+++ b/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs
@@ -3,9 +3,9 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
-using NHibernate.Linq.Clauses;
using NHibernate.Linq.GroupBy;
using NHibernate.Linq.Visitors;
+using NHibernate.Type;
using NHibernate.Util;
using Remotion.Linq;
using Remotion.Linq.Clauses;
@@ -26,20 +26,20 @@ static class NestedSelectRewriter
private static readonly PropertyInfo IGroupingKeyProperty = (PropertyInfo)
ReflectHelper.GetProperty, Tuple>(g => g.Key);
- public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory)
+ public static void ReWrite(QueryModel queryModel, VisitorParameters parameters)
{
- var nsqmv = new NestedSelectDetector(sessionFactory);
+ var nsqmv = new NestedSelectDetector(parameters.SessionFactory);
nsqmv.VisitExpression(queryModel.SelectClause.Selector);
if (!nsqmv.HasSubqueries)
return;
var elementExpression = new List();
- var group = Expression.Parameter(typeof (IGrouping), "g");
-
+ var group = Expression.Parameter(typeof(IGrouping), "g");
+
var replacements = new Dictionary();
foreach (var expression in nsqmv.Expressions)
{
- var processed = ProcessExpression(queryModel, sessionFactory, expression, elementExpression, group);
+ var processed = ProcessExpression(queryModel, expression, elementExpression, group, parameters);
if (processed != null)
replacements.Add(expression, processed);
}
@@ -48,7 +48,7 @@ public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory
var expressions = new List();
- var identifier = GetIdentifier(sessionFactory, new QuerySourceReferenceExpression(queryModel.MainFromClause));
+ var identifier = GetIdentifier(parameters.SessionFactory, new QuerySourceReferenceExpression(queryModel.MainFromClause));
var rewriter = new SelectClauseRewriter(key, expressions, identifier, replacements);
@@ -59,98 +59,104 @@ public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory
var keySelector = CreateSelector(elementExpression, 0);
var elementSelector = CreateSelector(elementExpression, 1);
-
- var input = Expression.Parameter(typeof (IEnumerable