From: <fab...@us...> - 2011-05-23 12:59:39
|
Revision: 5860 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5860&view=rev Author: fabiomaulo Date: 2011-05-23 12:59:32 +0000 (Mon, 23 May 2011) Log Message: ----------- LINQ provider using HQL skip/take Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Hql/Ast/HqlTreeBuilder.cs trunk/nhibernate/src/NHibernate/Hql/Ast/HqlTreeNode.cs trunk/nhibernate/src/NHibernate/Linq/IntermediateHqlTree.cs trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFirst.cs trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessSkip.cs trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessTake.cs Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/HqlTreeBuilder.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/HqlTreeBuilder.cs 2011-05-23 12:28:49 UTC (rev 5859) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/HqlTreeBuilder.cs 2011-05-23 12:59:32 UTC (rev 5860) @@ -176,6 +176,16 @@ return new HqlOrderBy(_factory); } + public HqlSkip Skip(HqlExpression parameter) + { + return new HqlSkip(_factory, parameter); + } + + public HqlTake Take(HqlExpression parameter) + { + return new HqlTake(_factory, parameter); + } + public HqlSelect Select(HqlExpression expression) { return new HqlSelect(_factory, expression); Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/HqlTreeNode.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/HqlTreeNode.cs 2011-05-23 12:28:49 UTC (rev 5859) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/HqlTreeNode.cs 2011-05-23 12:59:32 UTC (rev 5860) @@ -363,6 +363,18 @@ } } + public class HqlSkip : HqlStatement + { + public HqlSkip(IASTFactory factory, HqlExpression parameter) + : base(HqlSqlWalker.SKIP, "skip", factory, parameter) { } + } + + public class HqlTake : HqlStatement + { + public HqlTake(IASTFactory factory, HqlExpression parameter) + : base(HqlSqlWalker.TAKE, "take", factory, parameter) {} + } + public class HqlConstant : HqlExpression { public HqlConstant(IASTFactory factory, int type, string value) Modified: trunk/nhibernate/src/NHibernate/Linq/IntermediateHqlTree.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Linq/IntermediateHqlTree.cs 2011-05-23 12:28:49 UTC (rev 5859) +++ trunk/nhibernate/src/NHibernate/Linq/IntermediateHqlTree.cs 2011-05-23 12:59:32 UTC (rev 5860) @@ -11,26 +11,45 @@ { public class IntermediateHqlTree { - private readonly bool _root; + /* NOTE: + * Because common understanding of our users, we are flatting the behavior of Skip and Take methods. + * In RAM a query like primeNumbers.Skip(2).Take(6).Where(x=> x > 10) has a completely different results than primeNumbers.Where(x=> x > 10).Skip(2).Take(6) that has + * different results than primeNumbers.Take(6).Where(x=> x > 10).Skip(2) and so on. + * We are flatting/override even the double-usage of Skip and Take in the same query as: primeNumbers.Skip(2).Where(x=> x > 10).Take(6).Skip(3) + * We ***shouldn't*** change the behavior of the query just because we are translating it in SQL. + */ + private readonly bool isRoot; private readonly List<Action<IQuery, IDictionary<string, Tuple<object, IType>>>> _additionalCriteria = new List<Action<IQuery, IDictionary<string, Tuple<object, IType>>>>(); private readonly List<LambdaExpression> _listTransformers = new List<LambdaExpression>(); private readonly List<LambdaExpression> _itemTransformers = new List<LambdaExpression>(); private readonly List<LambdaExpression> _postExecuteTransformers = new List<LambdaExpression>(); private bool _hasDistinctRootOperator; + private HqlExpression skipCount; + private HqlExpression takeCount; - public HqlTreeNode Root { get; private set; } - public HqlTreeBuilder TreeBuilder { get; private set; } + private HqlTreeNode root; + public HqlTreeNode Root + { + get + { + ExecuteAddSkipClause(skipCount); + ExecuteAddTakeClause(takeCount); + return root; + } + } + public HqlTreeBuilder TreeBuilder { get; private set; } + public IntermediateHqlTree(bool root) { - _root = root; + isRoot = root; TreeBuilder = new HqlTreeBuilder(); - Root = TreeBuilder.Query(TreeBuilder.SelectFrom(TreeBuilder.From())); + this.root = TreeBuilder.Query(TreeBuilder.SelectFrom(TreeBuilder.From())); } public ExpressionToHqlTranslationResults GetTranslation() { - if (_root) + if (isRoot) { DetectOuterExists(); } @@ -62,41 +81,86 @@ public void AddFromClause(HqlTreeNode from) { - Root.NodesPreOrder.Where(n => n is HqlFrom).First().AddChild(from); + root.NodesPreOrder.Where(n => n is HqlFrom).First().AddChild(from); } public void AddSelectClause(HqlTreeNode select) { - Root.NodesPreOrder.Where(n => n is HqlSelectFrom).First().AddChild(select); + root.NodesPreOrder.Where(n => n is HqlSelectFrom).First().AddChild(select); } public void AddGroupByClause(HqlGroupBy groupBy) { - Root.As<HqlQuery>().AddChild(groupBy); + root.As<HqlQuery>().AddChild(groupBy); } public void AddOrderByClause(HqlExpression orderBy, HqlDirectionStatement direction) { - var orderByRoot = Root.NodesPreOrder.Where(n => n is HqlOrderBy).FirstOrDefault(); + var orderByRoot = root.NodesPreOrder.Where(n => n is HqlOrderBy).FirstOrDefault(); if (orderByRoot == null) { orderByRoot = TreeBuilder.OrderBy(); - Root.As<HqlQuery>().AddChild(orderByRoot); + root.As<HqlQuery>().AddChild(orderByRoot); } orderByRoot.AddChild(orderBy); orderByRoot.AddChild(direction); } - public void AddWhereClause(HqlBooleanExpression where) + public void AddSkipClause(HqlExpression toSkip) + { + skipCount = toSkip; + } + + public void AddTakeClause(HqlExpression toTake) + { + takeCount = toTake; + } + + private void ExecuteAddTakeClause(HqlExpression toTake) + { + if(toTake == null) + { + return; + } + + HqlQuery hqlQuery = root.NodesPreOrder.OfType<HqlQuery>().First(); + HqlTreeNode takeRoot = hqlQuery.Children.FirstOrDefault(n => n is HqlTake); + + // were present we ignore the new value + if (takeRoot == null) + { + //We should check the value instead delegate the behavior to the result SQL-> MSDN: If count is less than or equal to zero, source is not enumerated and an empty IEnumerable<T> is returned. + takeRoot = TreeBuilder.Take(toTake); + hqlQuery.AddChild(takeRoot); + } + } + + private void ExecuteAddSkipClause(HqlExpression toSkip) + { + if (toSkip == null) + { + return; + } + // We should check the value instead delegate the behavior to the result SQL-> MSDN: If count is less than or equal to zero, all elements of source are yielded. + HqlQuery hqlQuery = root.NodesPreOrder.OfType<HqlQuery>().First(); + HqlTreeNode skipRoot = hqlQuery.Children.FirstOrDefault(n => n is HqlSkip); + if (skipRoot == null) + { + skipRoot = TreeBuilder.Skip(toSkip); + hqlQuery.AddChild(skipRoot); + } + } + + public void AddWhereClause(HqlBooleanExpression where) { - var currentWhere = Root.NodesPreOrder.Where(n => n is HqlWhere).FirstOrDefault(); + var currentWhere = root.NodesPreOrder.Where(n => n is HqlWhere).FirstOrDefault(); if (currentWhere == null) { currentWhere = TreeBuilder.Where(where); - Root.As<HqlQuery>().AddChild(currentWhere); + root.As<HqlQuery>().AddChild(currentWhere); } else { @@ -109,16 +173,15 @@ private void DetectOuterExists() { - if (Root is HqlExists) - { - Root = Root.Children.First(); + if (root is HqlExists) + { + AddTakeClause(TreeBuilder.Constant(1)); + root = Root.Children.First(); - _additionalCriteria.Add((q, p) => q.SetMaxResults(1)); + Expression<Func<IEnumerable<object>, bool>> x = l => l.Any(); - Expression<Func<IEnumerable<object>, bool>> x = l => l.Any(); - - _listTransformers.Add(x); - } + _listTransformers.Add(x); + } } @@ -139,7 +202,7 @@ public void SetRoot(HqlTreeNode newRoot) { - Root = newRoot; + root = newRoot; } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFirst.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFirst.cs 2011-05-23 12:28:49 UTC (rev 5859) +++ trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFirst.cs 2011-05-23 12:59:32 UTC (rev 5860) @@ -13,7 +13,7 @@ AddClientSideEval(firstMethod, queryModelVisitor, tree); - tree.AddAdditionalCriteria((q, p) => q.SetMaxResults(1)); + tree.AddTakeClause(tree.TreeBuilder.Constant(1)); } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessSkip.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessSkip.cs 2011-05-23 12:28:49 UTC (rev 5859) +++ trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessSkip.cs 2011-05-23 12:59:32 UTC (rev 5860) @@ -1,22 +1,29 @@ using System.Linq.Expressions; +using NHibernate.Engine.Query; using Remotion.Linq.Clauses.ResultOperators; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { - public class ProcessSkip : IResultOperatorProcessor<SkipResultOperator> - { - public void Process(SkipResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) - { - NamedParameter parameterName; + public class ProcessSkip : IResultOperatorProcessor<SkipResultOperator> + { + #region IResultOperatorProcessor<SkipResultOperator> Members - if (queryModelVisitor.VisitorParameters.ConstantToParameterMap.TryGetValue(resultOperator.Count as ConstantExpression, out parameterName)) - { - tree.AddAdditionalCriteria((q, p) => q.SetFirstResult((int)p[parameterName.Name].First)); - } - else - { - tree.AddAdditionalCriteria((q, p) => q.SetFirstResult(resultOperator.GetConstantCount())); - } - } - } + public void Process(SkipResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) + { + VisitorParameters parameters = queryModelVisitor.VisitorParameters; + NamedParameter namedParameter; + + if (parameters.ConstantToParameterMap.TryGetValue(resultOperator.Count as ConstantExpression, out namedParameter)) + { + parameters.RequiredHqlParameters.Add(new NamedParameterDescriptor(namedParameter.Name, null, new[] {parameters.RequiredHqlParameters.Count + 1}, false)); + tree.AddSkipClause(tree.TreeBuilder.Parameter(namedParameter.Name)); + } + else + { + tree.AddSkipClause(tree.TreeBuilder.Constant(resultOperator.GetConstantCount())); + } + } + + #endregion + } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessTake.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessTake.cs 2011-05-23 12:28:49 UTC (rev 5859) +++ trunk/nhibernate/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessTake.cs 2011-05-23 12:59:32 UTC (rev 5860) @@ -1,24 +1,29 @@ using System.Linq.Expressions; +using NHibernate.Engine.Query; using Remotion.Linq.Clauses.ResultOperators; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { - public class ProcessTake : IResultOperatorProcessor<TakeResultOperator> - { - public void Process(TakeResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) - { - NamedParameter parameterName; + public class ProcessTake : IResultOperatorProcessor<TakeResultOperator> + { + #region IResultOperatorProcessor<TakeResultOperator> Members - // TODO - very similar to ProcessSkip, plus want to investigate the scenario in the "else" - // clause to see if it is valid - if (queryModelVisitor.VisitorParameters.ConstantToParameterMap.TryGetValue(resultOperator.Count as ConstantExpression, out parameterName)) - { - tree.AddAdditionalCriteria((q, p) => q.SetMaxResults((int)p[parameterName.Name].First)); - } - else - { - tree.AddAdditionalCriteria((q, p) => q.SetMaxResults(resultOperator.GetConstantCount())); - } - } - } + public void Process(TakeResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) + { + VisitorParameters parameters = queryModelVisitor.VisitorParameters; + NamedParameter namedParameter; + + if (parameters.ConstantToParameterMap.TryGetValue(resultOperator.Count as ConstantExpression, out namedParameter)) + { + parameters.RequiredHqlParameters.Add(new NamedParameterDescriptor(namedParameter.Name, null, new[] {parameters.RequiredHqlParameters.Count + 1}, false)); + tree.AddTakeClause(tree.TreeBuilder.Parameter(namedParameter.Name)); + } + else + { + tree.AddTakeClause(tree.TreeBuilder.Constant(resultOperator.GetConstantCount())); + } + } + + #endregion + } } \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |