Skip to content

* Basic support for T-SQL query hints. #3008

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
1 change: 1 addition & 0 deletions src/NHibernate/Async/Engine/Query/HQLQueryPlan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public async Task PerformListAsync(QueryParameters queryParameters, ISessionImpl
RowSelection selection = new RowSelection();
selection.FetchSize = queryParameters.RowSelection.FetchSize;
selection.Timeout = queryParameters.RowSelection.Timeout;
selection.Hint = queryParameters.RowSelection.Hint;
queryParametersToUse = queryParameters.CreateCopyUsing(selection);
}
else
Expand Down
20 changes: 20 additions & 0 deletions src/NHibernate/Dialect/Dialect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2408,6 +2408,11 @@ public virtual bool SupportsSubSelects
/// </summary>
public virtual bool SupportsDateTimeScale => false;

/// <summary>
/// Does this dialect support T-SQL query hints
/// </summary>
public virtual bool SupportsQueryHints => false;

#endregion

/// <summary>
Expand Down Expand Up @@ -2675,5 +2680,20 @@ public virtual ISQLExceptionConverter BuildSQLExceptionConverter()
// may override to return whatever is most appropriate for that vendor.
return new SQLStateConverter(ViolatedConstraintNameExtracter);
}

/// <summary>
/// If the dialect supports T-SQL query hints, this method returns the corresponding SQL.
/// </summary>
/// <param name="queryString">The input query string to transform</param>
/// <param name="hint">The query hint string</param>
/// <returns>Modified query string with the given T-SQL query hint.</returns>
/// <exception cref="NotSupportedException">
/// This exception is thrown if the dialect does state it supports query hints, but the current dialect
/// has no implementation for it.
/// </exception>
public virtual SqlString GetQueryHintString(SqlString queryString, string hint)
{
throw new NotSupportedException("Dialect does not have support for query hints.");
}
}
}
16 changes: 15 additions & 1 deletion src/NHibernate/Dialect/MsSql2008Dialect.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Data;
using NHibernate.Dialect.Function;
using NHibernate.Driver;
using NHibernate.SqlCommand;
using NHibernate.SqlTypes;
using NHibernate.Util;
using Environment = NHibernate.Cfg.Environment;
Expand Down Expand Up @@ -107,5 +109,17 @@ public override SqlType OverrideSqlType(SqlType type)

/// <inheritdoc />
public override bool SupportsDateTimeScale => !KeepDateTime;

/// <inheritdoc />
public override bool SupportsQueryHints => true;

/// <inheritdoc />
public override SqlString GetQueryHintString(SqlString queryString, string hint)
{
if (String.IsNullOrEmpty(hint))
return queryString;

return queryString.Append($" OPTION({hint})");
}
}
}
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 hint = 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 Hint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a strange place to add query hint...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be a better place?

{
get { return hint; }
set { hint = value; }
}
}
}
5 changes: 5 additions & 0 deletions src/NHibernate/IQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,11 @@ public partial interface IQuery
/// </summary>
IQuery SetResultTransformer(IResultTransformer resultTransformer);

/// <summary>
/// Provide a query hint. Needs support of the underlying dialect.
/// </summary>
IQuery SetHint(string hint);

/// <summary>
/// Get a enumerable that when enumerated will execute
/// a batch of queries in a single database roundtrip
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 @@ -443,7 +443,8 @@ protected void SetQueryProperties(IQuery q)
.SetReadOnly(readOnly)
.SetTimeout(selection.Timeout)
.SetFlushMode(flushMode)
.SetFetchSize(selection.FetchSize);
.SetFetchSize(selection.FetchSize)
.SetHint(selection.Hint);
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 @@ -903,6 +903,12 @@ public IQuery SetFlushMode(FlushMode flushMode)
return this;
}

public IQuery SetHint(string hint)
{
selection.Hint = hint;
return this;
}

public IQuery SetCollectionKey(object collectionKey)
{
this.collectionKey = collectionKey;
Expand Down
7 changes: 7 additions & 0 deletions src/NHibernate/Linq/IQueryableOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,12 @@ public interface IQueryableOptions
/// <param name="timeout">The timeout in seconds.</param>
/// <returns><see langword="this" /> (for method chaining).</returns>
IQueryableOptions SetTimeout(int timeout);

/// <summary>
/// Set a T-SQL Query hint as an Option to the query
/// </summary>
/// <param name="hint">The t-sql query hint</param>
/// <returns><see langword="this" /> (for method chaining).</returns>
IQueryableOptions SetHint(string hint);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface should not be changed. It is obsolete.

}
}
24 changes: 23 additions & 1 deletion src/NHibernate/Linq/NhQueryableOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class NhQueryableOptions
protected bool? ReadOnly { get; private set; }
protected string Comment { get; private set; }
protected FlushMode? FlushMode { get; private set; }
protected string Hint { get; private set; }

#pragma warning disable 618
/// <inheritdoc />
Expand All @@ -28,6 +29,9 @@ public class NhQueryableOptions

/// <inheritdoc />
IQueryableOptions IQueryableOptions.SetTimeout(int timeout) => SetTimeout(timeout);

/// <inheritdoc />
IQueryableOptions IQueryableOptions.SetHint(string hint) => SetHint(hint);
#pragma warning restore 618

/// <summary>
Expand Down Expand Up @@ -125,6 +129,20 @@ public NhQueryableOptions SetFlushMode(FlushMode flushMode)
return this;
}

/// <summary>
/// Set a T-SQL query hint which is appended to the query.
/// </summary>
/// <remarks>
/// The underlying dialect needs support for query hints.
/// </remarks>
/// <param name="hint">The query hint which should be appended to the query.</param>
/// <returns><see langword="this"/> (for method chaining).</returns>
public NhQueryableOptions SetHint(string hint)
{
Hint = hint;
return this;
}

protected internal NhQueryableOptions Clone()
{
return new NhQueryableOptions
Expand All @@ -135,7 +153,8 @@ protected internal NhQueryableOptions Clone()
Timeout = Timeout,
ReadOnly = ReadOnly,
Comment = Comment,
FlushMode = FlushMode
FlushMode = FlushMode,
Hint = Hint
};
}

Expand All @@ -161,6 +180,9 @@ protected internal void Apply(IQuery query)

if (FlushMode.HasValue)
query.SetFlushMode(FlushMode.Value);

if (!string.IsNullOrEmpty(Hint))
query.SetHint(Hint);
}
}
}
16 changes: 12 additions & 4 deletions src/NHibernate/Loader/Hql/QueryLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ protected override SqlString ApplyLocks(SqlString sql, IDictionary<string, LockM
return dialect.ApplyLocksToSql(sql, aliasedLockModes, keyColumnNames);
}

protected override SqlString ApplyHint(SqlString sql, string hint, Dialect.Dialect dialect)
{
if (dialect.SupportsQueryHints)
return dialect.GetQueryHintString(sql, hint);

return base.ApplyHint(sql, hint, dialect);
}

protected override string[] Aliases
{
get { return _sqlAliases; }
Expand Down Expand Up @@ -343,9 +351,9 @@ protected override object GetResultColumnOrRow(object[] row, IResultTransformer
Object[] resultRow = GetResultRow(row, rs, session);
bool hasTransform = HasSelectNew || resultTransformer != null;
return (!hasTransform && resultRow.Length == 1
? resultRow[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert these changes.

: resultRow
);
? resultRow[0]
: resultRow
);
}

protected override object[] GetResultRow(object[] row, DbDataReader rs, ISessionImplementor session)
Expand Down Expand Up @@ -441,7 +449,7 @@ internal IEnumerable GetEnumerable(QueryParameters queryParameters, IEventSource
var rs = GetResultSet(cmd, queryParameters, session, null);

var resultTransformer = _selectNewTransformer ?? queryParameters.ResultTransformer;
IEnumerable result =
IEnumerable result =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this changes

new EnumerableImpl(rs, cmd, session, queryParameters.IsReadOnly(session), _queryTranslator.ReturnTypes, _queryTranslator.GetColumnNames(), queryParameters.RowSelection, resultTransformer, _queryReturnAliases);

if (stopWatch != null)
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 @@ -227,6 +227,16 @@ protected virtual SqlString ApplyLocks(SqlString sql, IDictionary<string, LockMo
return sql;
}

/// <summary>
/// Append <c> OPTION(...hints...)</c> clause, if necessary. This
/// empty superclass implementation merely returns its first
/// argument.
/// </summary>
protected virtual SqlString ApplyHint(SqlString sql, string hints, Dialect.Dialect dialect)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not used anywhere.

{
return sql;
}

/// <summary>
/// Does this query return objects that might be already cached by
/// the session, whose lock mode may need upgrading.
Expand Down