diff --git a/build-common/common.xml b/build-common/common.xml index c57395e8791..996dabb8d4d 100644 --- a/build-common/common.xml +++ b/build-common/common.xml @@ -52,7 +52,7 @@ effectively SP0). --> - + diff --git a/src/NHibernate/Dialect/Dialect.cs b/src/NHibernate/Dialect/Dialect.cs index c9240005f95..c3f471abc6c 100644 --- a/src/NHibernate/Dialect/Dialect.cs +++ b/src/NHibernate/Dialect/Dialect.cs @@ -1399,15 +1399,29 @@ public IList GetTokens() } } - #endregion + #endregion - #region Limit/offset support + #region Query Hint support - /// - /// Does this Dialect have some kind of LIMIT syntax? - /// - /// False, unless overridden. - public virtual bool SupportsLimit + public virtual bool SupportsOption + { + get { return false; } + } + + public virtual SqlString GetOptionString(SqlString queryString, string option) + { + throw new NotSupportedException("Dialect does not have support for query hints."); + } + + #endregion + + #region Limit/offset support + + /// + /// Does this Dialect have some kind of LIMIT syntax? + /// + /// False, unless overridden. + public virtual bool SupportsLimit { get { return false; } } diff --git a/src/NHibernate/Dialect/MsSql2008Dialect.cs b/src/NHibernate/Dialect/MsSql2008Dialect.cs index b1ac6b6e39f..bba1f706105 100644 --- a/src/NHibernate/Dialect/MsSql2008Dialect.cs +++ b/src/NHibernate/Dialect/MsSql2008Dialect.cs @@ -2,6 +2,7 @@ using NHibernate.Cfg; using NHibernate.Dialect.Function; using NHibernate.Driver; +using NHibernate.SqlCommand; namespace NHibernate.Dialect { @@ -38,5 +39,15 @@ protected override void RegisterDefaultProperties() base.RegisterDefaultProperties(); DefaultProperties[Environment.ConnectionDriver] = typeof(Sql2008ClientDriver).AssemblyQualifiedName; } - } + + public override bool SupportsOption + { + get { return false; } + } + + public override SqlString GetOptionString(SqlString queryString, string option) + { + return queryString.Append($" OPTION({option})"); + } + } } \ No newline at end of file diff --git a/src/NHibernate/Engine/Query/HQLQueryPlan.cs b/src/NHibernate/Engine/Query/HQLQueryPlan.cs index 7960125c306..57e92552a02 100644 --- a/src/NHibernate/Engine/Query/HQLQueryPlan.cs +++ b/src/NHibernate/Engine/Query/HQLQueryPlan.cs @@ -100,6 +100,7 @@ public void PerformList(QueryParameters queryParameters, ISessionImplementor ses RowSelection selection = new RowSelection(); selection.FetchSize = queryParameters.RowSelection.FetchSize; selection.Timeout = queryParameters.RowSelection.Timeout; + selection.Option = queryParameters.RowSelection.Option; queryParametersToUse = queryParameters.CreateCopyUsing(selection); } else diff --git a/src/NHibernate/Engine/RowSelection.cs b/src/NHibernate/Engine/RowSelection.cs index 334965d064d..70a0d828175 100644 --- a/src/NHibernate/Engine/RowSelection.cs +++ b/src/NHibernate/Engine/RowSelection.cs @@ -18,6 +18,7 @@ public sealed class RowSelection private int maxRows = NoValue; private int timeout = NoValue; private int fetchSize = NoValue; + private string option = null; /// /// Gets or Sets the Index of the First Row to Select @@ -62,5 +63,11 @@ public bool DefinesLimits { get { return maxRows != NoValue || firstRow > 0; } } + + public string Option + { + get { return option; } + set { option = value; } + } } } \ No newline at end of file diff --git a/src/NHibernate/IQuery.cs b/src/NHibernate/IQuery.cs index c4697cdaf37..91c7e43590f 100644 --- a/src/NHibernate/IQuery.cs +++ b/src/NHibernate/IQuery.cs @@ -216,6 +216,12 @@ public interface IQuery /// IQuery SetTimeout(int timeout); + /// + /// The query hint options specified for the query. + /// + /// The specified option + IQuery SetOption(string option); + /// Set a fetch size for the underlying ADO query. /// the fetch size IQuery SetFetchSize(int fetchSize); diff --git a/src/NHibernate/Impl/AbstractDetachedQuery.cs b/src/NHibernate/Impl/AbstractDetachedQuery.cs index 803f08c4268..e02223943eb 100644 --- a/src/NHibernate/Impl/AbstractDetachedQuery.cs +++ b/src/NHibernate/Impl/AbstractDetachedQuery.cs @@ -425,7 +425,8 @@ protected void SetQueryProperties(IQuery q) .SetReadOnly(readOnly) .SetTimeout(selection.Timeout) .SetFlushMode(flushMode) - .SetFetchSize(selection.FetchSize); + .SetFetchSize(selection.FetchSize) + .SetOption(selection.Option); if (!string.IsNullOrEmpty(comment)) q.SetComment(comment); if (!string.IsNullOrEmpty(cacheRegion)) diff --git a/src/NHibernate/Impl/AbstractQueryImpl.cs b/src/NHibernate/Impl/AbstractQueryImpl.cs index 729334b5c50..6ad26918e7f 100644 --- a/src/NHibernate/Impl/AbstractQueryImpl.cs +++ b/src/NHibernate/Impl/AbstractQueryImpl.cs @@ -785,6 +785,12 @@ public IQuery SetTimeout(int timeout) return this; } + public IQuery SetOption(string option) + { + selection.Option = option; + return this; + } + public IQuery SetFetchSize(int fetchSize) { selection.FetchSize = fetchSize; diff --git a/src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs b/src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs index 8150d0ae5df..e9e1c5169aa 100644 --- a/src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs +++ b/src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs @@ -40,7 +40,8 @@ public static class AggregatingGroupByRewriter typeof (AnyResultOperator), typeof (AllResultOperator), typeof (TimeoutResultOperator), - typeof (CacheableResultOperator) + typeof (CacheableResultOperator), + typeof (OptionResultOperator), }; public static void ReWrite(QueryModel queryModel) diff --git a/src/NHibernate/Linq/LinqExtensionMethods.cs b/src/NHibernate/Linq/LinqExtensionMethods.cs index dafee708975..d5d40ceee62 100755 --- a/src/NHibernate/Linq/LinqExtensionMethods.cs +++ b/src/NHibernate/Linq/LinqExtensionMethods.cs @@ -67,7 +67,16 @@ public static IQueryable Timeout(this IQueryable query, int timeout) return new NhQueryable(query.Provider, callExpression); } - public static IEnumerable ToFuture(this IQueryable query) + public static IQueryable WithOption(this IQueryable query, string option) + { + var method = ReflectionHelper.GetMethodDefinition(() => WithOption(null, null)).MakeGenericMethod(typeof(T)); + + var callExpression = Expression.Call(method, query.Expression, Expression.Constant(option)); + + return new NhQueryable(query.Provider, callExpression); + } + + public static IEnumerable ToFuture(this IQueryable query) { var nhQueryable = query as QueryableBase; if (nhQueryable == null) diff --git a/src/NHibernate/Linq/NhRelinqQueryParser.cs b/src/NHibernate/Linq/NhRelinqQueryParser.cs index 86c1410099e..5810cba1e06 100644 --- a/src/NHibernate/Linq/NhRelinqQueryParser.cs +++ b/src/NHibernate/Linq/NhRelinqQueryParser.cs @@ -78,7 +78,14 @@ public NHibernateNodeTypeProvider() }, typeof (TimeoutExpressionNode) ); - var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider(); + methodInfoRegistry.Register( + new[] + { + ReflectionHelper.GetMethodDefinition(() => LinqExtensionMethods.WithOption(null, null)), + }, typeof(OptionExpressionNode) + ); + + var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider(); nodeTypeProvider.InnerProviders.Add(methodInfoRegistry); defaultNodeTypeProvider = nodeTypeProvider; } @@ -168,8 +175,30 @@ public override void TransformExpressions(Func transform } } - - internal class TimeoutExpressionNode : ResultOperatorExpressionNodeBase + internal class OptionExpressionNode : ResultOperatorExpressionNodeBase + { + private readonly MethodCallExpressionParseInfo _parseInfo; + private readonly ConstantExpression _option; + + public OptionExpressionNode(MethodCallExpressionParseInfo parseInfo, ConstantExpression option) + : base(parseInfo, null, null) + { + _parseInfo = parseInfo; + _option = option; + } + + public override Expression Resolve(ParameterExpression inputParameter, Expression expressionToBeResolved, ClauseGenerationContext clauseGenerationContext) + { + return Source.Resolve(inputParameter, expressionToBeResolved, clauseGenerationContext); + } + + protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) + { + return new OptionResultOperator(_parseInfo, _option); + } + } + + internal class TimeoutExpressionNode : ResultOperatorExpressionNodeBase { private readonly MethodCallExpressionParseInfo _parseInfo; private readonly ConstantExpression _timeout; @@ -189,10 +218,41 @@ public override Expression Resolve(ParameterExpression inputParameter, Expressio protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) { return new TimeoutResultOperator(_parseInfo, _timeout); - } - } - - internal class TimeoutResultOperator : ResultOperatorBase + } + } + + internal class OptionResultOperator : ResultOperatorBase + { + public MethodCallExpressionParseInfo ParseInfo { get; private set; } + public ConstantExpression Option { get; private set; } + + public OptionResultOperator(MethodCallExpressionParseInfo parseInfo, ConstantExpression timeout) + { + ParseInfo = parseInfo; + Option = timeout; + } + + public override IStreamedData ExecuteInMemory(IStreamedData input) + { + throw new NotImplementedException(); + } + + public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo) + { + return inputInfo; + } + + public override ResultOperatorBase Clone(CloneContext cloneContext) + { + throw new NotImplementedException(); + } + + public override void TransformExpressions(Func transformation) + { + } + } + + internal class TimeoutResultOperator : ResultOperatorBase { public MethodCallExpressionParseInfo ParseInfo { get; private set; } public ConstantExpression Timeout { get; private set; } diff --git a/src/NHibernate/Linq/ReWriters/QueryReferenceExpressionFlattener.cs b/src/NHibernate/Linq/ReWriters/QueryReferenceExpressionFlattener.cs index 412f1c8fabe..914afe4b989 100644 --- a/src/NHibernate/Linq/ReWriters/QueryReferenceExpressionFlattener.cs +++ b/src/NHibernate/Linq/ReWriters/QueryReferenceExpressionFlattener.cs @@ -18,7 +18,8 @@ public class QueryReferenceExpressionFlattener : ExpressionTreeVisitor typeof (CacheableResultOperator), typeof (TimeoutResultOperator), typeof (FetchOneRequest), - typeof (FetchManyRequest) + typeof (FetchManyRequest), + typeof (OptionResultOperator) }; private QueryReferenceExpressionFlattener(QueryModel model) diff --git a/src/NHibernate/Linq/ReWriters/ResultOperatorRewriter.cs b/src/NHibernate/Linq/ReWriters/ResultOperatorRewriter.cs index fe1e50f7c40..60c28de2f00 100644 --- a/src/NHibernate/Linq/ReWriters/ResultOperatorRewriter.cs +++ b/src/NHibernate/Linq/ReWriters/ResultOperatorRewriter.cs @@ -68,6 +68,7 @@ private class ResultOperatorExpressionRewriter : ExpressionTreeVisitor typeof(CacheableResultOperator), typeof(TimeoutResultOperator), typeof(CastResultOperator), // see ProcessCast class + typeof(OptionResultOperator), }; private readonly List resultOperators = new List(); diff --git a/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs b/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs index a83d3911684..92b7693291a 100644 --- a/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs +++ b/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs @@ -126,7 +126,8 @@ static QueryModelVisitor() ResultOperatorMap.Add(); ResultOperatorMap.Add(); ResultOperatorMap.Add(); - } + ResultOperatorMap.Add(); + } private QueryModelVisitor(VisitorParameters visitorParameters, bool root, QueryModel queryModel) { diff --git a/src/NHibernate/Loader/Hql/QueryLoader.cs b/src/NHibernate/Loader/Hql/QueryLoader.cs index 2273716525e..3418ae705c6 100644 --- a/src/NHibernate/Loader/Hql/QueryLoader.cs +++ b/src/NHibernate/Loader/Hql/QueryLoader.cs @@ -59,7 +59,15 @@ public override bool IsSubselectLoadingEnabled get { return HasSubselectLoadableCollections(); } } - protected override SqlString ApplyLocks(SqlString sql, IDictionary lockModes, + protected override SqlString ApplyOptions(SqlString sql, string option, Dialect.Dialect dialect) + { + if (!dialect.SupportsOption && String.IsNullOrEmpty(option)) + return sql; + + return dialect.GetOptionString(sql, option); + } + + protected override SqlString ApplyLocks(SqlString sql, IDictionary lockModes, Dialect.Dialect dialect) { if (lockModes == null || lockModes.Count == 0) diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 77890dfa737..831730f21d7 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -177,6 +177,11 @@ protected virtual SqlString ApplyLocks(SqlString sql, IDictionary /// Does this query return objects that might be already cached by /// the session, whose lock mode may need upgrading. @@ -205,6 +210,11 @@ protected virtual SqlString PreprocessSQL(SqlString sql, QueryParameters paramet { sql = ApplyLocks(sql, parameters.LockModes, dialect); + RowSelection selection = parameters.RowSelection; + if (selection != null && !String.IsNullOrEmpty(selection.Option)) + sql = ApplyOptions(sql, selection.Option, dialect); + + return Factory.Settings.IsCommentsEnabled ? PrependComment(sql, parameters) : sql; } diff --git a/src/NHibernate/NHibernate.csproj b/src/NHibernate/NHibernate.csproj index adf988d2f7c..9bfbe7224f6 100644 --- a/src/NHibernate/NHibernate.csproj +++ b/src/NHibernate/NHibernate.csproj @@ -141,6 +141,7 @@ +