From: <pa...@us...> - 2011-05-29 18:37:03
|
Revision: 5880 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5880&view=rev Author: patearl Date: 2011-05-29 18:36:56 +0000 (Sun, 29 May 2011) Log Message: ----------- Documentation and consistency improvements in limit support. Includes a fix for a bug recently introduced in the MsSql2005Dialect. Modified Paths: -------------- trunk/nhibernate/releasenotes.txt trunk/nhibernate/src/NHibernate/Criterion/SubqueryExpression.cs trunk/nhibernate/src/NHibernate/Dialect/Dialect.cs trunk/nhibernate/src/NHibernate/Dialect/MsSql2005Dialect.cs trunk/nhibernate/src/NHibernate/Loader/Loader.cs Modified: trunk/nhibernate/releasenotes.txt =================================================================== --- trunk/nhibernate/releasenotes.txt 2011-05-29 18:06:21 UTC (rev 5879) +++ trunk/nhibernate/releasenotes.txt 2011-05-29 18:36:56 UTC (rev 5880) @@ -4,6 +4,8 @@ ##### Possible Breaking Changes ##### * All Dialect.GetLimitString() methods replaced with a single GetLimitString method with a new signature. + For dialects the developers don't perform routine tests on, efforts were made to ensure the new limit string + method conforms to the database documentation. Please report any limit-related bugs discovered at runtime. * [NH-2550] - Allow public access to FieldInterceptor Session (IFieldInterceptor changed) * [NH-2593] - For Microsoft SQL Server the default batch-size (adonet.batch_size) is set to 20 where not explicit defined in the session-factory configuration * - ICollectionPersister added property to fix [NH-2489] Modified: trunk/nhibernate/src/NHibernate/Criterion/SubqueryExpression.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Criterion/SubqueryExpression.cs 2011-05-29 18:06:21 UTC (rev 5879) +++ trunk/nhibernate/src/NHibernate/Criterion/SubqueryExpression.cs 2011-05-29 18:36:56 UTC (rev 5880) @@ -66,11 +66,13 @@ if (criteriaImpl.FirstResult != 0 || criteriaImpl.MaxResults != RowSelection.NoValue) { - int firstResults = factory.Dialect.GetOffsetValue(Loader.Loader.GetFirstRow(parameters.RowSelection)); - int maxResults = Loader.Loader.GetMaxOrLimit(factory.Dialect, parameters.RowSelection); - int? offsetParameterIndex = criteriaQuery.CreatePagingParameter(firstResults); - int? limitParameterIndex = criteriaQuery.CreatePagingParameter(maxResults); - sql = factory.Dialect.GetLimitString(sql, firstResults, maxResults, offsetParameterIndex, limitParameterIndex); + int? offset = Loader.Loader.GetOffsetUsingDialect(parameters.RowSelection, factory.Dialect); + int? limit = Loader.Loader.GetLimitUsingDialect(parameters.RowSelection, factory.Dialect); + int? offsetParameterIndex = offset.HasValue ? criteriaQuery.CreatePagingParameter(offset.Value) : null; + int? limitParameterIndex = limit.HasValue ? criteriaQuery.CreatePagingParameter(limit.Value) : null; + Parameter offsetParameter = offsetParameterIndex.HasValue ? Parameter.WithIndex(offsetParameterIndex.Value) : null; + Parameter limitParameter = limitParameterIndex.HasValue ? Parameter.WithIndex(limitParameterIndex.Value) : null; + sql = factory.Dialect.GetLimitString(sql, offset, limit, offsetParameter, limitParameter); } if (op != null) Modified: trunk/nhibernate/src/NHibernate/Dialect/Dialect.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Dialect/Dialect.cs 2011-05-29 18:06:21 UTC (rev 5879) +++ trunk/nhibernate/src/NHibernate/Dialect/Dialect.cs 2011-05-29 18:36:56 UTC (rev 5880) @@ -1702,28 +1702,34 @@ } /// <summary> + /// Generates a string to limit the result set to a number of maximum results with a specified offset into the results. /// Expects any database-specific offset and limit adjustments to have already been performed (ex. UseMaxForLimit, OffsetStartsAtOne). + /// Performs error checking based on the various dialect limit support options. If both parameters and fixed valeus are + /// specified, this will use the parameter option if possible. Otherwise, it will fall back to a fixed string. /// </summary> - internal SqlString GetLimitString(SqlString queryString, int? offset, int? limit, int? offsetParameterIndex, int? limitParameterIndex) + /// <param name="queryString"></param> + /// <param name="offset"></param> + /// <param name="limit"></param> + /// <param name="offsetParameter"></param> + /// <param name="limitParameter"></param> + /// <returns></returns> + public SqlString GetLimitString(SqlString queryString, int? offset, int? limit, Parameter offsetParameter, Parameter limitParameter) { - SqlString offsetParameter = - SupportsVariableLimit && offsetParameterIndex.HasValue ? new SqlString(Parameter.WithIndex(offsetParameterIndex.Value)) : - offset.HasValue ? new SqlString(offset.ToString()) : - null; + if (offset == null && limit == null && offsetParameter == null && limitParameter == null) + return queryString; - SqlString limitParameter = - SupportsVariableLimit && limitParameterIndex.HasValue ? new SqlString(Parameter.WithIndex(limitParameterIndex.Value)) : - limit.HasValue ? new SqlString(limit.ToString()) : - null; + if (!SupportsLimit) + throw new NotSupportedException("Dialect does not support limits."); - return GetLimitString(queryString, offsetParameter, limitParameter); - } + if (!SupportsVariableLimit && offsetParameter != null && offset == null) + throw new NotSupportedException("Dialect does not support variable limits."); - /// <summary> - /// Expects any database-specific offset and limit adjustments to have already been performed (ex. UseMaxForLimit, OffsetStartsAtOne). - /// </summary> - internal SqlString GetLimitString(SqlString queryString, int? offset, int? limit, Parameter offsetParameter, Parameter limitParameter) - { + if (!SupportsVariableLimit && limitParameter != null && limit == null) + throw new NotSupportedException("Dialect does not support variable limits."); + + if (!SupportsLimitOffset && (offset != null || offsetParameter != null)) + throw new NotSupportedException("Dialect does not support limits with offsets."); + SqlString o = SupportsVariableLimit && offsetParameter != null ? new SqlString(offsetParameter) : offset.HasValue ? new SqlString(offset.ToString()) : @@ -1737,7 +1743,15 @@ return GetLimitString(queryString, o, l); } - internal int GetLimitValue(int offset, int limit) + /// <summary> + /// Some databases require that a limit statement contain the maximum row number + /// instead of the number of rows to retrieve. This method adjusts source + /// limit and offset values to account for this. + /// </summary> + /// <param name="offset"></param> + /// <param name="limit"></param> + /// <returns></returns> + public int GetLimitValue(int offset, int limit) { if (limit == int.MaxValue) return int.MaxValue; @@ -1748,7 +1762,13 @@ return limit; } - internal int GetOffsetValue(int offset) + /// <summary> + /// Some databases use limit row offsets that start at one instead of zero. + /// This method adjusts a desired offset using the OffsetStartsAtOne flag. + /// </summary> + /// <param name="offset"></param> + /// <returns></returns> + public int GetOffsetValue(int offset) { if (OffsetStartsAtOne) return offset + 1; Modified: trunk/nhibernate/src/NHibernate/Dialect/MsSql2005Dialect.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Dialect/MsSql2005Dialect.cs 2011-05-29 18:06:21 UTC (rev 5879) +++ trunk/nhibernate/src/NHibernate/Dialect/MsSql2005Dialect.cs 2011-05-29 18:36:56 UTC (rev 5880) @@ -76,6 +76,9 @@ if (limit != null) result.Add("TOP (").Add(limit).Add(") "); + else + // ORDER BY can only be used in subqueries if TOP is also specified. + result.Add("TOP (" + int.MaxValue + ") "); result .Add(StringHelper.Join(", ", columnsOrAliases)) Modified: trunk/nhibernate/src/NHibernate/Loader/Loader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2011-05-29 18:06:21 UTC (rev 5879) +++ trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2011-05-29 18:36:56 UTC (rev 5880) @@ -1098,6 +1098,29 @@ return dialect.SupportsLimit && (HasMaxRows(selection) || HasOffset(selection)); } + /// <summary> + /// Performs dialect-specific manipulations on the offset value before returning it. + /// This method is applicable for use in limit statements only. + /// </summary> + internal static int? GetOffsetUsingDialect(RowSelection selection, Dialect.Dialect dialect) + { + int firstRow = GetFirstRow(selection); + if (firstRow == 0) + return null; + return dialect.GetOffsetValue(firstRow); + } + + /// <summary> + /// Performs dialect-specific manipulations on the limit value before returning it. + /// This method is applicable for use in limit statements only. + /// </summary> + internal static int? GetLimitUsingDialect(RowSelection selection, Dialect.Dialect dialect) + { + if (selection == null || selection.MaxRows == RowSelection.NoValue) + return null; + return dialect.GetLimitValue(GetFirstRow(selection), selection.MaxRows); + } + /// <summary> /// Obtain an <c>IDbCommand</c> with all parameters pre-bound. Bind positional parameters, /// named parameters, and limit parameters. @@ -1127,14 +1150,17 @@ if (useLimit) { - int max = GetMaxOrLimit(dialect, selection); + int? offset = GetOffsetUsingDialect(selection, dialect); + int? limit = GetLimitUsingDialect(selection, dialect); + Parameter offsetParameter = queryParameters.OffsetParameterIndex.HasValue ? Parameter.WithIndex(queryParameters.OffsetParameterIndex.Value) : null; + Parameter limitParameter = queryParameters.LimitParameterIndex.HasValue ? Parameter.WithIndex(queryParameters.LimitParameterIndex.Value) : null; sqlString = dialect.GetLimitString( sqlString.Trim(), - useOffset ? (int?)dialect.GetOffsetValue(GetFirstRow(selection)) : null, - max != int.MaxValue ? (int?)max : null, - queryParameters.OffsetParameterIndex, - queryParameters.LimitParameterIndex); + useOffset ? offset : null, + limit, + useOffset ? offsetParameter : null, + limitParameter); } sqlString = PreprocessSQL(sqlString, queryParameters, dialect); @@ -1742,15 +1768,18 @@ if (useLimit) { - int max = GetMaxOrLimit(dialect, selection); - sqlString = - dialect.GetLimitString( - sqlString.Trim(), - useOffset ? (int?)dialect.GetOffsetValue(GetFirstRow(selection)) : null, - max != int.MaxValue ? (int?)max : null, - parameters.OffsetParameterIndex, - parameters.LimitParameterIndex); - } + int? offset = GetOffsetUsingDialect(selection, dialect); + int? limit = GetLimitUsingDialect(selection, dialect); + Parameter offsetParameter = parameters.OffsetParameterIndex.HasValue ? Parameter.WithIndex(parameters.OffsetParameterIndex.Value) : null; + Parameter limitParameter = parameters.LimitParameterIndex.HasValue ? Parameter.WithIndex(parameters.LimitParameterIndex.Value) : null; + sqlString = + dialect.GetLimitString( + sqlString.Trim(), + useOffset ? offset : null, + limit, + useOffset ? offsetParameter : null, + limitParameter); + } sqlString = PreprocessSQL(sqlString, parameters, dialect); return new SqlCommandInfo(sqlString, sqlTypes); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |