Skip to content

NH-3820 - Basic support for query hints. #678

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build-common/common.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
effectively SP0).
-->

<property name="project.version" value="4.1.1.GA" overwrite="false" />
<property name="project.version" value="4.1.1.SP1" overwrite="false" />

<!-- This version number should be changed if, but only if, there are incompatible
changes compared to the previous version. -->
Expand Down
28 changes: 21 additions & 7 deletions src/NHibernate/Dialect/Dialect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1399,15 +1399,29 @@ public IList<SqlString> GetTokens()
}
}

#endregion
#endregion

#region Limit/offset support
#region Query Hint support

/// <summary>
/// Does this Dialect have some kind of <c>LIMIT</c> syntax?
/// </summary>
/// <value>False, unless overridden.</value>
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

/// <summary>
/// Does this Dialect have some kind of <c>LIMIT</c> syntax?
/// </summary>
/// <value>False, unless overridden.</value>
public virtual bool SupportsLimit
{
get { return false; }
}
Expand Down
13 changes: 12 additions & 1 deletion src/NHibernate/Dialect/MsSql2008Dialect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using NHibernate.Cfg;
using NHibernate.Dialect.Function;
using NHibernate.Driver;
using NHibernate.SqlCommand;

namespace NHibernate.Dialect
{
Expand Down Expand Up @@ -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})");
}
}
}
1 change: 1 addition & 0 deletions src/NHibernate/Engine/Query/HQLQueryPlan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/NHibernate/Engine/RowSelection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public sealed class RowSelection
private int maxRows = NoValue;
private int timeout = NoValue;
private int fetchSize = NoValue;
private string option = null;

/// <summary>
/// Gets or Sets the Index of the First Row to Select
Expand Down Expand Up @@ -62,5 +63,11 @@ public bool DefinesLimits
{
get { return maxRows != NoValue || firstRow > 0; }
}

public string Option
{
get { return option; }
set { option = value; }
}
}
}
6 changes: 6 additions & 0 deletions src/NHibernate/IQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ public interface IQuery
/// <param name="timeout"></param>
IQuery SetTimeout(int timeout);

/// <summary>
/// The query hint options specified for the query.
/// </summary>
/// <param name="option">The specified option</param>
IQuery SetOption(string option);

/// <summary> Set a fetch size for the underlying ADO query.</summary>
/// <param name="fetchSize">the fetch size </param>
IQuery SetFetchSize(int fetchSize);
Expand Down
3 changes: 2 additions & 1 deletion src/NHibernate/Impl/AbstractDetachedQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
6 changes: 6 additions & 0 deletions src/NHibernate/Impl/AbstractQueryImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 10 additions & 1 deletion src/NHibernate/Linq/LinqExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,16 @@ public static IQueryable<T> Timeout<T>(this IQueryable<T> query, int timeout)
return new NhQueryable<T>(query.Provider, callExpression);
}

public static IEnumerable<T> ToFuture<T>(this IQueryable<T> query)
public static IQueryable<T> WithOption<T>(this IQueryable<T> query, string option)
{
var method = ReflectionHelper.GetMethodDefinition(() => WithOption<object>(null, null)).MakeGenericMethod(typeof(T));

var callExpression = Expression.Call(method, query.Expression, Expression.Constant(option));

return new NhQueryable<T>(query.Provider, callExpression);
}

public static IEnumerable<T> ToFuture<T>(this IQueryable<T> query)
{
var nhQueryable = query as QueryableBase<T>;
if (nhQueryable == null)
Expand Down
74 changes: 67 additions & 7 deletions src/NHibernate/Linq/NhRelinqQueryParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,14 @@ public NHibernateNodeTypeProvider()
}, typeof (TimeoutExpressionNode)
);

var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider();
methodInfoRegistry.Register(
new[]
{
ReflectionHelper.GetMethodDefinition(() => LinqExtensionMethods.WithOption<object>(null, null)),
}, typeof(OptionExpressionNode)
);

var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider();
nodeTypeProvider.InnerProviders.Add(methodInfoRegistry);
defaultNodeTypeProvider = nodeTypeProvider;
}
Expand Down Expand Up @@ -168,8 +175,30 @@ public override void TransformExpressions(Func<Expression, Expression> 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;
Expand All @@ -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<Expression, Expression> transformation)
{
}
}

internal class TimeoutResultOperator : ResultOperatorBase
{
public MethodCallExpressionParseInfo ParseInfo { get; private set; }
public ConstantExpression Timeout { get; private set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public class QueryReferenceExpressionFlattener : ExpressionTreeVisitor
typeof (CacheableResultOperator),
typeof (TimeoutResultOperator),
typeof (FetchOneRequest),
typeof (FetchManyRequest)
typeof (FetchManyRequest),
typeof (OptionResultOperator)
};

private QueryReferenceExpressionFlattener(QueryModel model)
Expand Down
1 change: 1 addition & 0 deletions src/NHibernate/Linq/ReWriters/ResultOperatorRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ private class ResultOperatorExpressionRewriter : ExpressionTreeVisitor
typeof(CacheableResultOperator),
typeof(TimeoutResultOperator),
typeof(CastResultOperator), // see ProcessCast class
typeof(OptionResultOperator),
};

private readonly List<ResultOperatorBase> resultOperators = new List<ResultOperatorBase>();
Expand Down
3 changes: 2 additions & 1 deletion src/NHibernate/Linq/Visitors/QueryModelVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ static QueryModelVisitor()
ResultOperatorMap.Add<TimeoutResultOperator, ProcessTimeout>();
ResultOperatorMap.Add<OfTypeResultOperator, ProcessOfType>();
ResultOperatorMap.Add<CastResultOperator, ProcessCast>();
}
ResultOperatorMap.Add<OptionResultOperator, ProcessOption>();
}

private QueryModelVisitor(VisitorParameters visitorParameters, bool root, QueryModel queryModel)
{
Expand Down
10 changes: 9 additions & 1 deletion src/NHibernate/Loader/Hql/QueryLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,15 @@ public override bool IsSubselectLoadingEnabled
get { return HasSubselectLoadableCollections(); }
}

protected override SqlString ApplyLocks(SqlString sql, IDictionary<string, LockMode> 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<string, LockMode> lockModes,
Dialect.Dialect dialect)
{
if (lockModes == null || lockModes.Count == 0)
Expand Down
10 changes: 10 additions & 0 deletions src/NHibernate/Loader/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ protected virtual SqlString ApplyLocks(SqlString sql, IDictionary<string, LockMo
return sql;
}

protected virtual SqlString ApplyOptions(SqlString sql, string option, Dialect.Dialect dialect)
{
return sql;
}

/// <summary>
/// Does this query return objects that might be already cached by
/// the session, whose lock mode may need upgrading.
Expand Down Expand Up @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions src/NHibernate/NHibernate.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
<Compile Include="Connection\DriverConnectionProvider.cs" />
<Compile Include="Connection\IConnectionProvider.cs" />
<Compile Include="Connection\UserSuppliedConnectionProvider.cs" />
<Compile Include="Linq\Visitors\ResultOperatorProcessors\ProcessOption.cs" />
<Compile Include="Util\DelegateHelper.cs" />
<Compile Include="Dialect\BitwiseFunctionOperation.cs" />
<Compile Include="Dialect\DB2Dialect.cs" />
Expand Down