Skip to content

Commit

Permalink
Add test and fix for NH-2915 and NH-3056
Browse files Browse the repository at this point in the history
Where clause before fetch request was suppressed when additional clauses were used after fetch request
  • Loading branch information
hazzik committed Jun 4, 2012
1 parent ae2b296 commit 6195424
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 1 deletion.
165 changes: 165 additions & 0 deletions src/NHibernate.Test/Linq/EagerLoadTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,170 @@ in session.Query<Supplier>().FetchMany(a => a.Products).ThenFetchMany(a => a.Ord
where s.SupplierId == 1
select s).ToList();
}

[Test]
public void WhereBeforeFetchAndOrderBy()
{
//NH-2915
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.Fetch(x => x.Customer)
.OrderBy(x => x.OrderId)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].Customer));
}

[Test]
public void WhereBeforeFetchManyAndOrderBy()
{
//NH-2915
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.FetchMany(x => x.OrderLines)
.OrderBy(x => x.OrderId)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines));
}

[Test]
public void WhereBeforeFetchManyThenFetchAndOrderBy()
{
//NH-2915
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.FetchMany(x => x.OrderLines)
.ThenFetch(x => x.Product)
.OrderBy(x => x.OrderId)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines));
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines.First().Product));
}

[Test]
public void WhereBeforeFetchAndSelect()
{
//NH-3056
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.Fetch(x => x.Customer)
.Select(x => x)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].Customer));
}

[Test]
public void WhereBeforeFetchManyAndSelect()
{
//NH-3056
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.FetchMany(x => x.OrderLines)
.Select(x => x)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines));
}

[Test]
public void WhereBeforeFetchManyThenFetchAndSelect()
{
//NH-3056
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.FetchMany(x => x.OrderLines)
.ThenFetch(x => x.Product)
.Select(x => x)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines));
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines.First().Product));
}

[Test]
public void WhereBeforeFetchAndWhere()
{
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.Fetch(x => x.Customer)
.Where(x => true)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].Customer));
}

[Test]
public void WhereBeforeFetchManyAndWhere()
{
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.FetchMany(x => x.OrderLines)
.Where(x => true)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines));
}

[Test]
public void WhereBeforeFetchManyThenFetchAndWhere()
{
var firstOrderId = db.Orders.OrderBy(x => x.OrderId)
.Select(x => x.OrderId)
.First();

var orders = db.Orders
.Where(x => x.OrderId != firstOrderId)
.FetchMany(x => x.OrderLines)
.ThenFetch(x => x.Product)
.Where(x => true)
.ToList();

Assert.AreEqual(829, orders.Count);
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines));
Assert.IsTrue(NHibernateUtil.IsInitialized(orders[0].OrderLines.First().Product));
}
}
}
4 changes: 3 additions & 1 deletion src/NHibernate.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/EXPLICIT_INTERNAL_MODIFIER/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/EXPLICIT_PRIVATE_MODIFIER/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">False</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String></wpf:ResourceDictionary>
2 changes: 2 additions & 0 deletions src/NHibernate/Linq/Visitors/QueryModelVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class QueryModelVisitor : QueryModelVisitorBase
{
public static ExpressionToHqlTranslationResults GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, bool root)
{
SubQueryFromClauseFlattener.ReWrite(queryModel);

NestedSelectRewriter.ReWrite(queryModel, parameters.SessionFactory);

// Remove unnecessary body operators
Expand Down
98 changes: 98 additions & 0 deletions src/NHibernate/Linq/Visitors/SubQueryFromClauseFlattener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System.Collections.Generic;
using System.Linq;
using Remotion.Linq;
using Remotion.Linq.Clauses;
using Remotion.Linq.Clauses.ExpressionTreeVisitors;
using Remotion.Linq.Clauses.Expressions;
using Remotion.Linq.EagerFetching;

namespace NHibernate.Linq.Visitors
{
public class SubQueryFromClauseFlattener : QueryModelVisitorBase
{
private static readonly System.Type[] FlattenableResultOperators = new[]
{
typeof (FetchOneRequest),
typeof (FetchManyRequest),
};

public static void ReWrite(QueryModel queryModel)
{
new SubQueryFromClauseFlattener().VisitQueryModel(queryModel);
}

public override void VisitAdditionalFromClause(AdditionalFromClause fromClause, QueryModel queryModel, int index)
{
var subQueryExpression = fromClause.FromExpression as SubQueryExpression;
if (subQueryExpression != null)
FlattenSubQuery(subQueryExpression, fromClause, queryModel, index + 1);
base.VisitAdditionalFromClause(fromClause, queryModel, index);
}

public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
{
var subQueryExpression = fromClause.FromExpression as SubQueryExpression;
if (subQueryExpression != null)
FlattenSubQuery(subQueryExpression, fromClause, queryModel, 0);
base.VisitMainFromClause(fromClause, queryModel);
}

private static bool CheckFlattenable(QueryModel subQueryModel)
{
if (subQueryModel.BodyClauses.OfType<OrderByClause>().Any())
return false;

if (subQueryModel.ResultOperators.Count == 0)
return true;

return HasJustAllFlattenableOperator(subQueryModel.ResultOperators);
}

private static bool HasJustAllFlattenableOperator(IEnumerable<ResultOperatorBase> resultOperators)
{
return resultOperators.All(x => FlattenableResultOperators.Contains(x.GetType()));
}

private static void CopyFromClauseData(FromClauseBase source, FromClauseBase destination)
{
destination.FromExpression = source.FromExpression;
destination.ItemName = source.ItemName;
destination.ItemType = source.ItemType;
}

private static void CopyResultOperators(IEnumerable<ResultOperatorBase> resultOperators, QueryModel queryModel)
{
foreach (var bodyClause in resultOperators)
queryModel.ResultOperators.Add(bodyClause);
}

private static void FlattenSubQuery(SubQueryExpression subQueryExpression, FromClauseBase fromClause, QueryModel queryModel, int destinationIndex)
{
if (!CheckFlattenable(subQueryExpression.QueryModel))
return;

var mainFromClause = subQueryExpression.QueryModel.MainFromClause;
CopyFromClauseData(mainFromClause, fromClause);

var innerSelectorMapping = new QuerySourceMapping();
innerSelectorMapping.AddMapping(fromClause, subQueryExpression.QueryModel.SelectClause.Selector);
queryModel.TransformExpressions((ex => ReferenceReplacingExpressionTreeVisitor.ReplaceClauseReferences(ex, innerSelectorMapping, false)));

InsertBodyClauses(subQueryExpression.QueryModel.BodyClauses, queryModel, destinationIndex);
CopyResultOperators(subQueryExpression.QueryModel.ResultOperators, queryModel);

var innerBodyClauseMapping = new QuerySourceMapping();
innerBodyClauseMapping.AddMapping(mainFromClause, new QuerySourceReferenceExpression(fromClause));
queryModel.TransformExpressions((ex => ReferenceReplacingExpressionTreeVisitor.ReplaceClauseReferences(ex, innerBodyClauseMapping, false)));
}

private static void InsertBodyClauses(IEnumerable<IBodyClause> bodyClauses, QueryModel queryModel, int destinationIndex)
{
foreach (var bodyClause in bodyClauses)
{
queryModel.BodyClauses.Insert(destinationIndex, bodyClause);
++destinationIndex;
}
}
}
}
1 change: 1 addition & 0 deletions src/NHibernate/NHibernate.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@
<Compile Include="Linq\NestedSelects\NestedSelectRewriter.cs" />
<Compile Include="Linq\Visitors\SelectJoinDetector.cs" />
<Compile Include="Linq\Visitors\SelectClauseNominator.cs" />
<Compile Include="Linq\Visitors\SubQueryFromClauseFlattener.cs" />
<Compile Include="Linq\Visitors\VisitorUtil.cs" />
<Compile Include="Linq\Visitors\WhereJoinDetector.cs" />
<Compile Include="Loader\TopologicalSorter.cs" />
Expand Down

0 comments on commit 6195424

Please sign in to comment.