From: <fab...@us...> - 2011-05-30 13:58:33
|
Revision: 5881 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5881&view=rev Author: fabiomaulo Date: 2011-05-30 13:58:24 +0000 (Mon, 30 May 2011) Log Message: ----------- - Fix NH-2736 - refactoring of sql-parameters managements for HQL-LINQ Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Engine/JoinSequence.cs trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableUpdateExecutor.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/BinaryLogicOperatorNode.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/SqlFragment.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/JoinProcessor.cs trunk/nhibernate/src/NHibernate/NHibernate.csproj trunk/nhibernate/src/NHibernate/Param/AbstractExplicitParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/AggregatedIndexCollectionSelectorParameterSpecifications.cs trunk/nhibernate/src/NHibernate/Param/CollectionFilterKeyParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/DynamicFilterParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/IExplicitParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/IParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/NamedParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/PositionalParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs trunk/nhibernate/src/NHibernate/SqlCommand/Parameter.cs trunk/nhibernate/src/NHibernate/SqlCommand/SqlString.cs trunk/nhibernate/src/NHibernate.Test/Linq/FunctionTests.cs trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj trunk/nhibernate/src/NHibernate.Test/SqlCommandTest/SqlStringFixture.cs Added Paths: ----------- trunk/nhibernate/src/NHibernate/Param/ParametersBackTrackExtensions.cs trunk/nhibernate/src/NHibernate/Param/QuerySkipParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/QueryTakeParameterSpecification.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2736/ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2736/Domain.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2736/Fixture.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2736/Mappings.hbm.xml Modified: trunk/nhibernate/src/NHibernate/Engine/JoinSequence.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/JoinSequence.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Engine/JoinSequence.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -139,7 +139,7 @@ public JoinFragment ToJoinFragment( IDictionary<string, IFilter> enabledFilters, bool includeExtraJoins, - string withClauseFragment, + SqlString withClauseFragment, string withClauseJoinAlias) { QueryJoinFragment joinFragment = new QueryJoinFragment(factory.Dialect, useThetaStyle); @@ -164,7 +164,7 @@ { Join join = joins[i]; string on = join.AssociationType.GetOnCondition(join.Alias, factory, enabledFilters); - string condition; + SqlString condition = new SqlString(); if (last != null && IsManyToManyRoot(last) && ((IQueryableCollection)last).ElementType == join.AssociationType) @@ -174,36 +174,38 @@ // defined specifically on the many-to-many string manyToManyFilter = ((IQueryableCollection)last) .GetManyToManyFilterFragment(join.Alias, enabledFilters); - condition = "".Equals(manyToManyFilter) + condition = new SqlString("".Equals(manyToManyFilter) ? on : "".Equals(on) ? manyToManyFilter - : on + " and " + manyToManyFilter; + : on + " and " + manyToManyFilter); } else { // NH Different behavior : NH1179 and NH1293 // Apply filters in Many-To-One association var enabledForManyToOne = FilterHelper.GetEnabledForManyToOne(enabledFilters); - condition = string.IsNullOrEmpty(on) && enabledForManyToOne.Count > 0 + condition = new SqlString(string.IsNullOrEmpty(on) && enabledForManyToOne.Count > 0 ? join.Joinable.FilterFragment(join.Alias, enabledForManyToOne) - : on; + : on); } if (withClauseFragment != null) { if (join.Alias.Equals(withClauseJoinAlias)) { - condition += " and " + withClauseFragment; + condition = condition.Append(" and ").Append(withClauseFragment); } } + + // NH: the variable "condition" have to be a SqlString because it may contains Parameter instances with BackTrack joinFragment.AddJoin( join.Joinable.TableName, join.Alias, join.LHSColumns, JoinHelper.GetRHSColumnNames(join.AssociationType, factory), join.JoinType, - new SqlString(condition) + condition ); if (includeExtraJoins) { Modified: trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -324,7 +324,7 @@ { if (part is Parameter) { - result.AddParameter(); + result.Add(((Parameter)part).Clone()); // (?) can be a position parameter or a named parameter (already substituted by (?), // but only the positional parameters are available at this point. Adding them in the @@ -487,7 +487,7 @@ int span = typedval.Type.GetColumnSpan(factory); string name = namedParameter.Key; - int[] locs = GetEffectiveNamedParameterLocations(sqlParameters, name) ?? getNamedParameterLocations(name); + int[] locs = getNamedParameterLocations(name); for (int i = 0; i < locs.Length; i++) { int location = locs[i]; @@ -567,23 +567,6 @@ return ConvertITypesToSqlTypes(paramTypeList, factory, totalSpan); } - private int[] GetEffectiveNamedParameterLocations(IList<Parameter> sqlParameters, object backTrackId) - { - var locations = new List<int>(5); - for (int i = 0; i < sqlParameters.Count; i++) - { - if (backTrackId.Equals(sqlParameters[i].BackTrack)) - { - locations.Add(i); - } - } - if(locations.Count == 0) - { - return null; - } - return locations.ToArray(); - } - public int BindParameters(IDbCommand command, int start, ISessionImplementor session) { int location = start; @@ -651,6 +634,12 @@ return span; } + internal SqlString ProcessedSql + { + get { return processedSQL; } + set { processedSQL = value; } + } + public SqlString FilteredSQL { get { return processedSQL; } @@ -659,16 +648,19 @@ public IList<IType> FilteredParameterTypes { get { return filteredParameterTypes; } + internal set { filteredParameterTypes = value; } } public IList<object> FilteredParameterValues { get { return filteredParameterValues; } + internal set { filteredParameterValues = value; } } public IList<int> FilteredParameterLocations { get { return filteredParameterLocations; } + internal set { filteredParameterLocations = value; } } public bool NaturalKeyLookup { get; set; } Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Linq; using Antlr.Runtime; using Antlr.Runtime.Tree; @@ -9,9 +10,9 @@ using NHibernate.Exceptions; using NHibernate.Hql.Ast.ANTLR.Tree; using NHibernate.Param; -using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.SqlTypes; +using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Hql.Ast.ANTLR.Exec { @@ -59,24 +60,16 @@ try { CheckParametersExpectedType(parameters); // NH Different behavior (NH-1898) - var parameterTypes = new List<SqlType>(Parameters.Count); + + var sqlQueryParametersList = sql.GetParameters().ToList(); + SqlType[] parameterTypes = Parameters.GetQueryParameterTypes(sqlQueryParametersList, session.Factory); + + st = session.Batcher.PrepareCommand(CommandType.Text, sql, parameterTypes); foreach (var parameterSpecification in Parameters) { - if (parameterSpecification.ExpectedType == null) - { - throw new QuerySyntaxException("Can't determine SqlType of parameter " + parameterSpecification.RenderDisplayInfo()+"\n Possible cause: wrong case-sensitive property-name."); - } - parameterTypes.AddRange(parameterSpecification.ExpectedType.SqlTypes(Factory)); + parameterSpecification.Bind(st, sqlQueryParametersList, parameters, session); } - st = session.Batcher.PrepareCommand(CommandType.Text, sql, parameterTypes.ToArray()); - IEnumerator<IParameterSpecification> paramSpecifications = Parameters.GetEnumerator(); - // NH Different behavior: The inital value is 0 (initialized to 1 in JAVA) - int pos = 0; - while (paramSpecifications.MoveNext()) - { - var paramSpec = paramSpecifications.Current; - pos += paramSpec.Bind(st, parameters, session, pos); - } + if (selection != null) { if (selection.Timeout != RowSelection.NoValue) Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableDeleteExecutor.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,16 +1,15 @@ using System; -using System.Collections.Generic; using System.Data; using System.Data.Common; - +using System.Linq; using NHibernate.Engine; using NHibernate.Exceptions; using NHibernate.Hql.Ast.ANTLR.Tree; using NHibernate.Param; -using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.SqlTypes; using NHibernate.Util; +using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Hql.Ast.ANTLR.Exec { @@ -83,19 +82,15 @@ try { var paramsSpec = Walker.Parameters; - var parameterTypes = new List<SqlType>(paramsSpec.Count); + var sqlQueryParametersList = idInsertSelect.GetParameters().ToList(); + SqlType[] parameterTypes = paramsSpec.GetQueryParameterTypes(sqlQueryParametersList, session.Factory); + + ps = session.Batcher.PrepareCommand(CommandType.Text, idInsertSelect, parameterTypes); foreach (var parameterSpecification in paramsSpec) { - parameterTypes.AddRange(parameterSpecification.ExpectedType.SqlTypes(Factory)); + parameterSpecification.Bind(ps, sqlQueryParametersList, parameters, session); } - ps = session.Batcher.PrepareCommand(CommandType.Text, idInsertSelect, parameterTypes.ToArray()); - // NH Different behavior: The inital value is 0 (initialized to 1 in JAVA) - int pos = 0; - foreach (var specification in paramsSpec) - { - pos += specification.Bind(ps, parameters, session, pos); - } resultCount = session.Batcher.ExecuteNonQuery(ps); } finally Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableUpdateExecutor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableUpdateExecutor.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableUpdateExecutor.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; - +using System.Linq; using NHibernate.Engine; using NHibernate.Exceptions; using NHibernate.Hql.Ast.ANTLR.Tree; @@ -11,6 +11,7 @@ using NHibernate.SqlCommand; using NHibernate.SqlTypes; using NHibernate.Util; +using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Hql.Ast.ANTLR.Exec { @@ -109,9 +110,15 @@ List<IParameterSpecification> whereParams = (new List<IParameterSpecification>(allParams)).GetRange( parameterStart, allParams.Count - parameterStart); - ps = session.Batcher.PrepareCommand(CommandType.Text, idInsertSelect, GetParametersTypes(whereParams)); + var sqlQueryParametersList = idInsertSelect.GetParameters().ToList(); + SqlType[] parameterTypes = whereParams.GetQueryParameterTypes(sqlQueryParametersList, session.Factory); - BindParameters(whereParams, ps, parameters, session); + ps = session.Batcher.PrepareCommand(CommandType.Text, idInsertSelect, parameterTypes); + foreach (var parameterSpecification in whereParams) + { + parameterSpecification.Bind(ps, sqlQueryParametersList, parameters, session); + } + resultCount = session.Batcher.ExecuteNonQuery(ps); } finally @@ -139,8 +146,16 @@ { try { - ps = session.Batcher.PrepareCommand(CommandType.Text, updates[i], GetParametersTypes(hqlParameters[i])); - BindParameters(hqlParameters[i], ps, parameters, session); + var sqlQueryParametersList = updates[i].GetParameters().ToList(); + var paramsSpec = hqlParameters[i]; + SqlType[] parameterTypes = paramsSpec.GetQueryParameterTypes(sqlQueryParametersList, session.Factory); + + ps = session.Batcher.PrepareCommand(CommandType.Text, updates[i], parameterTypes); + foreach (var parameterSpecification in paramsSpec) + { + parameterSpecification.Bind(ps, sqlQueryParametersList, parameters, session); + } + session.Batcher.ExecuteNonQuery(ps); } finally @@ -165,34 +180,6 @@ } } - private SqlType[] GetParametersTypes(IEnumerable<IParameterSpecification> specifications) - { - if (specifications == null) - { - return new SqlType[0]; - } - var result = new List<SqlType>(); - foreach (var specification in specifications) - { - result.AddRange(specification.ExpectedType.SqlTypes(Factory)); - } - return result.ToArray(); - } - - private static void BindParameters(IEnumerable<IParameterSpecification> specifications, IDbCommand command, - QueryParameters parameters, ISessionImplementor session) - { - if (specifications == null) - { - return; - } - int position = 0; // ADO params are 0-based - foreach (var specification in specifications) - { - position += specification.Bind(command, parameters, session, position); - } - } - protected override IQueryable[] AffectedQueryables { get { return new[] {persister}; } Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -907,6 +907,7 @@ { if (_namedParameters.Count > 0) { + // NH TODO: remove this limitation throw new SemanticException("cannot define positional parameter after any named parameters have been defined"); } ParameterNode parameter = (ParameterNode)adaptor.Create(PARAM, "?"); @@ -1081,7 +1082,8 @@ sql.whereExpr(); - fromElement.SetWithClauseFragment(visitor.GetJoinAlias(), "(" + sql.GetSQL() + ")"); + var withClauseFragment = new SqlString("(", sql.GetSQL(), ")"); + fromElement.SetWithClauseFragment(visitor.GetJoinAlias(), withClauseFragment); } catch (SemanticException) { Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,21 +1,24 @@ using System; +using System.Linq; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; - using NHibernate.Engine; using NHibernate.Event; using NHibernate.Hql.Ast.ANTLR.Tree; +using NHibernate.Hql.Classic; using NHibernate.Impl; using NHibernate.Loader; using NHibernate.Param; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; +using NHibernate.SqlTypes; using NHibernate.Transform; using NHibernate.Type; using NHibernate.Util; +using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Hql.Ast.ANTLR.Loader { @@ -23,7 +26,6 @@ public class QueryLoader : BasicLoader { private readonly QueryTranslatorImpl _queryTranslator; - private SelectClause _selectClause; private bool _hasScalars; private string[][] _scalarColumnNames; @@ -49,7 +51,6 @@ : base(factory) { _queryTranslator = queryTranslator; - _selectClause = selectClause; Initialize(selectClause); PostInstantiate(); @@ -434,5 +435,241 @@ } return result; } + + /// <summary> + /// Obtain an <c>IDbCommand</c> with all parameters pre-bound. Bind positional parameters, + /// named parameters, and limit parameters. + /// </summary> + /// <remarks> + /// Creates an IDbCommand object and populates it with the values necessary to execute it against the + /// database to Load an Entity. + /// </remarks> + /// <param name="queryParameters">The <see cref="QueryParameters"/> to use for the IDbCommand.</param> + /// <param name="scroll">TODO: find out where this is used...</param> + /// <param name="session">The SessionImpl this Command is being prepared in.</param> + /// <returns>A CommandWrapper wrapping an IDbCommand that is ready to be executed.</returns> + protected internal override IDbCommand PrepareQueryCommand(QueryParameters queryParameters, bool scroll, ISessionImplementor session) + { + // NH: In this QueryLoader we can know better all parameters used so we can simplify the IDbCommand construction + // NH: would be very useful if we can do the same with Criteria. This method works just for HQL and LINQ. + + // A distinct-copy of parameter specifications collected during query construction + var parameterSpecs = new HashSet<IParameterSpecification>(_queryTranslator.CollectedParameterSpecifications); + SqlString sqlString = SqlString.Copy(); + + // dynamic-filter parameters: during the HQL->SQL parsing, filters can be added as SQL_TOKEN/string and the SqlGenerator will not find it + sqlString = ExpandDynamicFilterParameters(sqlString, parameterSpecs, session); + AdjustQueryParametersForSubSelectFetching(sqlString, parameterSpecs, session, queryParameters); // NOTE: see TODO below + + sqlString = AddLimitsParametersIfNeeded(sqlString, parameterSpecs, queryParameters, session); + // TODO: for sub-select fetching we have to try to assign the QueryParameter.ProcessedSQL here (with limits) but only after use IParameterSpecification for any kind of queries + + // The PreprocessSQL method can modify the SqlString but should never add parameters (or we have to override it) + sqlString = PreprocessSQL(sqlString, queryParameters, session.Factory.Dialect); + + // After the last modification to the SqlString we can collect all parameters types. + ResetEffectiveExpectedType(parameterSpecs, queryParameters); // <= TODO: remove this method when we can infer the type during the parse + var sqlQueryParametersList = sqlString.GetParameters().ToList(); + SqlType[] parameterTypes = parameterSpecs.GetQueryParameterTypes(sqlQueryParametersList, session.Factory); + + parameterSpecs.SetQueryParameterLocations(sqlQueryParametersList, session.Factory); + + IDbCommand command = session.Batcher.PrepareQueryCommand(CommandType.Text, sqlString, parameterTypes); + + try + { + RowSelection selection = queryParameters.RowSelection; + if (selection != null && selection.Timeout != RowSelection.NoValue) + { + command.CommandTimeout = selection.Timeout; + } + + BindParametersValues(command, sqlQueryParametersList, parameterSpecs, queryParameters, session); + + session.Batcher.ExpandQueryParameters(command, sqlString); + } + catch (HibernateException) + { + session.Batcher.CloseCommand(command, null); + throw; + } + catch (Exception sqle) + { + session.Batcher.CloseCommand(command, null); + ADOExceptionReporter.LogExceptions(sqle); + throw; + } + return command; + } + + private void AdjustQueryParametersForSubSelectFetching(SqlString sqlString, IEnumerable<IParameterSpecification> parameterSpecs, ISessionImplementor session, QueryParameters queryParameters) + { + // TODO: Remove this when all parameters are managed using IParameterSpecification (QueryParameters does not need to have decomposed values for filters) + + var dynamicFilterParameterSpecifications = parameterSpecs.OfType<DynamicFilterParameterSpecification>().ToList(); + var filteredParameterValues = new List<object>(); + var filteredParameterTypes = new List<IType>(); + var filteredParameterLocations = new List<int>(); + + if (dynamicFilterParameterSpecifications.Count != 0) + { + var sqlQueryParametersList = sqlString.GetParameters().ToList(); + foreach (DynamicFilterParameterSpecification specification in dynamicFilterParameterSpecifications) + { + string backTrackId = specification.GetIdsForBackTrack(session.Factory).First(); + object value = session.GetFilterParameterValue(specification.FilterParameterFullName); + var elementType = specification.ExpectedType; + foreach (int position in sqlQueryParametersList.GetEffectiveParameterLocations(backTrackId)) + { + filteredParameterValues.Add(value); + filteredParameterTypes.Add(elementType); + filteredParameterLocations.Add(position); + } + } + } + + queryParameters.ProcessedSql = sqlString; + queryParameters.FilteredParameterLocations = filteredParameterLocations; + queryParameters.FilteredParameterTypes = filteredParameterTypes; + queryParameters.FilteredParameterValues = filteredParameterValues; + } + + private SqlString ExpandDynamicFilterParameters(SqlString sqlString, ICollection<IParameterSpecification> parameterSpecs, ISessionImplementor session) + { + var enabledFilters = session.EnabledFilters; + if (enabledFilters.Count == 0 || sqlString.ToString().IndexOf(ParserHelper.HqlVariablePrefix) < 0) + { + return sqlString; + } + + Dialect.Dialect dialect = session.Factory.Dialect; + string symbols = ParserHelper.HqlSeparators + dialect.OpenQuote + dialect.CloseQuote; + + var originSql = sqlString.Compact(); + var result = new SqlStringBuilder(); + foreach (var sqlPart in originSql.Parts) + { + var parameter = sqlPart as Parameter; + if (parameter != null) + { + result.Add(parameter); + continue; + } + + var sqlFragment = sqlPart.ToString(); + var tokens = new StringTokenizer(sqlFragment, symbols, true); + + foreach (string token in tokens) + { + if (token.StartsWith(ParserHelper.HqlVariablePrefix)) + { + string filterParameterName = token.Substring(1); + string[] parts = StringHelper.ParseFilterParameterName(filterParameterName); + string filterName = parts[0]; + string parameterName = parts[1]; + var filter = (FilterImpl)enabledFilters[filterName]; + + object value = filter.GetParameter(parameterName); + IType type = filter.FilterDefinition.GetParameterType(parameterName); + int parameterColumnSpan = type.GetColumnSpan(session.Factory); + var collectionValue = value as ICollection; + int? collectionSpan = null; + + // Add query chunk + string typeBindFragment = string.Join(", ", Enumerable.Repeat("?", parameterColumnSpan).ToArray()); + string bindFragment; + if (collectionValue != null && !type.ReturnedClass.IsArray) + { + collectionSpan = collectionValue.Count; + bindFragment = string.Join(", ", Enumerable.Repeat(typeBindFragment, collectionValue.Count).ToArray()); + } + else + { + bindFragment = typeBindFragment; + } + + // dynamic-filter parameter tracking + var filterParameterFragment = SqlString.Parse(bindFragment); + var dynamicFilterParameterSpecification = new DynamicFilterParameterSpecification(filterName, parameterName, type, collectionSpan); + var parameters = filterParameterFragment.GetParameters().ToArray(); + var sqlParameterPos = 0; + var paramTrackers = dynamicFilterParameterSpecification.GetIdsForBackTrack(session.Factory); + foreach (var paramTracker in paramTrackers) + { + parameters[sqlParameterPos++].BackTrack = paramTracker; + } + + parameterSpecs.Add(dynamicFilterParameterSpecification); + result.Add(filterParameterFragment); + } + else + { + result.Add(token); + } + } + } + return result.ToSqlString().Compact(); + } + + private SqlString AddLimitsParametersIfNeeded(SqlString sqlString, ICollection<IParameterSpecification> parameterSpecs, QueryParameters queryParameters, ISessionImplementor session) + { + var sessionFactory = session.Factory; + Dialect.Dialect dialect = sessionFactory.Dialect; + + RowSelection selection = queryParameters.RowSelection; + bool useLimit = UseLimit(selection, dialect); + if (useLimit) + { + bool hasFirstRow = GetFirstRow(selection) > 0; + bool useOffset = hasFirstRow && dialect.SupportsLimitOffset; + int max = GetMaxOrLimit(dialect, selection); + int? skip = useOffset ? (int?) dialect.GetOffsetValue(GetFirstRow(selection)) : null; + int? take = max != int.MaxValue ? (int?) max : null; + + Parameter skipSqlParameter = null; + Parameter takeSqlParameter = null; + if (skip.HasValue) + { + var skipParameter = new QuerySkipParameterSpecification(); + skipSqlParameter = Parameter.Placeholder; + skipSqlParameter.BackTrack = skipParameter.GetIdsForBackTrack(sessionFactory).First(); + parameterSpecs.Add(skipParameter); + } + if (take.HasValue) + { + var takeParameter = new QueryTakeParameterSpecification(); + takeSqlParameter = Parameter.Placeholder; + takeSqlParameter.BackTrack = takeParameter.GetIdsForBackTrack(sessionFactory).First(); + parameterSpecs.Add(takeParameter); + } + // The dialect can move the given parameters where he need, what it can't do is generates new parameters loosing the BackTrack. + return dialect.GetLimitString(sqlString, skip, take, skipSqlParameter, takeSqlParameter); + } + return sqlString; + } + + private void ResetEffectiveExpectedType(IEnumerable<IParameterSpecification> parameterSpecs, QueryParameters queryParameters) + { + foreach (var parameterSpecification in parameterSpecs.OfType<IExplicitParameterSpecification>()) + { + parameterSpecification.SetEffectiveType(queryParameters); + } + } + + /// <summary> + /// Bind all parameters values. + /// </summary> + /// <param name="command">The command where bind each value.</param> + /// <param name="sqlQueryParametersList">The list of Sql query parameter in the exact sequence they are present in the query.</param> + /// <param name="parameterSpecs">All parameter-specifications collected during query construction.</param> + /// <param name="queryParameters">The encapsulation of the parameter values to be bound.</param> + /// <param name="session">The session from where execute the query.</param> + private void BindParametersValues(IDbCommand command, IList<Parameter> sqlQueryParametersList, IEnumerable<IParameterSpecification> parameterSpecs, QueryParameters queryParameters, ISessionImplementor session) + { + foreach (var parameterSpecification in parameterSpecs) + { + parameterSpecification.Bind(command, sqlQueryParametersList, queryParameters, session); + } + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/SqlGenerator.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using Antlr.Runtime; using Antlr.Runtime.Tree; using NHibernate.Dialect.Function; @@ -92,11 +93,6 @@ writer.Clause(s); } - private void ParameterOut() - { - writer.Parameter(); - } - /// <summary> /// Add a aspace if the previous token was not a space or a parenthesis. /// </summary> @@ -129,11 +125,12 @@ private void Out(IASTNode n) { - var parameterNode= n as ParameterNode; + var parameterNode = n as ParameterNode; if (parameterNode != null) { var parameter = Parameter.Placeholder; - parameter.BackTrack = parameterNode.HqlParameterSpecification.IdForBackTrack; + // supposed to be simplevalue + parameter.BackTrack = parameterNode.HqlParameterSpecification.GetIdsForBackTrack(sessionFactory).Single(); writer.PushParameter(parameter); } else if (n is SqlNode) @@ -151,13 +148,11 @@ } else if (n is IParameterContainer) { - if (((IParameterContainer) n).HasEmbeddedParameters) + var parameterContainer = (IParameterContainer) n; + if (parameterContainer.HasEmbeddedParameters) { - IParameterSpecification[] specifications = ((IParameterContainer) n).GetEmbeddedParameters(); - if (specifications != null) - { - collectedParameters.AddRange(specifications); - } + IParameterSpecification[] specifications = parameterContainer.GetEmbeddedParameters(); + collectedParameters.AddRange(specifications); } } } @@ -340,8 +335,6 @@ } var dialect = sessionFactory.Dialect; - - // FIXME - We need to adjust the parameters from the user according to dialect settings like UseMaxForLimit, OffsetStartsAtOne. This will need to happen every time we query. // Skip-Take in HQL should be supported just for Dialect supporting variable limits at least when users use parameters for skip-take. if (!dialect.SupportsVariableLimit && (skipIsParameter || takeIsParameter)) @@ -349,26 +342,28 @@ throw new NotSupportedException("The dialect " + dialect.GetType().FullName + " does not supports variable limits"); } - // If a limit is a parameter, it should be of type IExplicitValueParameterSpecification. Parameter skipParameter = null; Parameter takeParameter = null; if(queryWriter.SkipParameter != null) { + queryWriter.SkipParameter.ExpectedType = NHibernateUtil.Int32; skipParameter = Parameter.Placeholder; - skipParameter.BackTrack = queryWriter.SkipParameter.IdForBackTrack; + skipParameter.BackTrack = queryWriter.SkipParameter.GetIdsForBackTrack(sessionFactory).First(); } if (queryWriter.TakeParameter != null) { + queryWriter.TakeParameter.ExpectedType = NHibernateUtil.Int32; takeParameter = Parameter.Placeholder; - takeParameter.BackTrack = queryWriter.TakeParameter.IdForBackTrack; + takeParameter.BackTrack = queryWriter.TakeParameter.GetIdsForBackTrack(sessionFactory).First(); } - // We allow the user to specify either constants or parameters for their limits. - return dialect.GetLimitString(sqlString, - queryWriter.Skip.HasValue ? (int?)dialect.GetOffsetValue(queryWriter.Skip.Value) : null, - queryWriter.Take.HasValue ? (int?)dialect.GetLimitValue(queryWriter.Skip ?? 0, queryWriter.Take.Value) : null, - skipParameter, - takeParameter); + // We allow the user to specify either constants or parameters for their limits. + // The dialect can move the given parameters where he need, what it can't do is generates new parameters loosing the BackTrack. + return dialect.GetLimitString(sqlString, + queryWriter.Skip.HasValue ? (int?) dialect.GetOffsetValue(queryWriter.Skip.Value) : null, + queryWriter.Take.HasValue ? (int?) dialect.GetLimitValue(queryWriter.Skip ?? 0, queryWriter.Take.Value) : null, + skipParameter, + takeParameter); } private void Skip(IASTNode node) Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/BinaryLogicOperatorNode.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/BinaryLogicOperatorNode.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/BinaryLogicOperatorNode.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; +using System.Linq; using Antlr.Runtime; using NHibernate.Engine; using NHibernate.Param; +using NHibernate.SqlCommand; using NHibernate.Type; -using NHibernate.Util; namespace NHibernate.Hql.Ast.ANTLR.Tree { @@ -13,9 +15,12 @@ /// Ported by: Steve Strong /// </summary> [CLSCompliant(false)] - public class BinaryLogicOperatorNode : HqlSqlWalkerNode, IBinaryOperatorNode + public class BinaryLogicOperatorNode : HqlSqlWalkerNode, IBinaryOperatorNode, IParameterContainer { - public BinaryLogicOperatorNode(IToken token) : base(token) + private List<IParameterSpecification> embeddedParameters; + + public BinaryLogicOperatorNode(IToken token) + : base(token) { } @@ -58,13 +63,15 @@ rhsType = lhsType; } - if ( typeof(IExpectedTypeAwareNode).IsAssignableFrom( lhs.GetType() ) ) + var lshExpectedTypeAwareNode = lhs as IExpectedTypeAwareNode; + if (lshExpectedTypeAwareNode != null) { - ( ( IExpectedTypeAwareNode ) lhs ).ExpectedType = rhsType; + lshExpectedTypeAwareNode.ExpectedType = rhsType; } - if ( typeof(IExpectedTypeAwareNode).IsAssignableFrom( rhs.GetType() ) ) + var rshExpectedTypeAwareNode = rhs as IExpectedTypeAwareNode; + if (rshExpectedTypeAwareNode != null) { - ( ( IExpectedTypeAwareNode ) rhs ).ExpectedType = lhsType; + rshExpectedTypeAwareNode.ExpectedType = lhsType; } MutateRowValueConstructorSyntaxesIfNecessary( lhsType, rhsType ); @@ -122,131 +129,138 @@ */ private void MutateRowValueConstructorSyntax(int valueElements) { + // Reduce the new tree in just one SqlFragment, to manage parameters + // mutation depends on the types of nodes invloved... - int comparisonType = Type; - string comparisonText = Text; - Type = HqlSqlWalker.AND; - Text = "AND"; + string comparisonText = "==".Equals(Text) ? "=" : Text; + Type = HqlSqlWalker.SQL_TOKEN; + string[] lhsElementTexts = ExtractMutationTexts(LeftHandOperand, valueElements); + string[] rhsElementTexts = ExtractMutationTexts(RightHandOperand, valueElements); - String[] lhsElementTexts = ExtractMutationTexts( LeftHandOperand, valueElements ); - String[] rhsElementTexts = ExtractMutationTexts( RightHandOperand, valueElements ); + var lho = LeftHandOperand as ParameterNode; + IParameterSpecification lhsEmbeddedCompositeParameterSpecification = (lho == null) ? null : lho.HqlParameterSpecification; - IParameterSpecification lhsEmbeddedCompositeParameterSpecification = - LeftHandOperand == null || ( !(LeftHandOperand is ParameterNode)) - ? null - : ( ( ParameterNode ) LeftHandOperand ).HqlParameterSpecification; + var rho = RightHandOperand as ParameterNode; + IParameterSpecification rhsEmbeddedCompositeParameterSpecification = (rho == null) ? null : rho.HqlParameterSpecification; - IParameterSpecification rhsEmbeddedCompositeParameterSpecification = - RightHandOperand == null || ( !(RightHandOperand is ParameterNode)) - ? null - : ( ( ParameterNode ) RightHandOperand ).HqlParameterSpecification; + var multicolumnComparisonClause = Translate(valueElements, comparisonText, lhsElementTexts, rhsElementTexts); - IASTNode container = this; + if (lhsEmbeddedCompositeParameterSpecification != null) + { + AddEmbeddedParameter(lhsEmbeddedCompositeParameterSpecification); + } + if (rhsEmbeddedCompositeParameterSpecification != null) + { + AddEmbeddedParameter(rhsEmbeddedCompositeParameterSpecification); + } + ClearChildren(); + Text = multicolumnComparisonClause; + } - for ( int i = valueElements - 1; i > 0; i-- ) + public override SqlString RenderText(ISessionFactoryImplementor sessionFactory) + { + if(!HasEmbeddedParameters) { - if ( i == 1 ) - { - container.ClearChildren(); + // this expression was not changed by MutateRowValueConstructorSyntax + return base.RenderText(sessionFactory); + } + var result = SqlString.Parse(Text); + // query-parameter = the parameter specified in the NHibernate query + // sql-parameter = real parameter/s inside the final SQL + // here is where we suppose the SqlString has all sql-parameters in sequence for a given query-parameter. + // This happen when the query-parameter spans multiple columns (components,custom-types and so on). + var parameters = result.GetParameters().ToArray(); + var sqlParameterPos = 0; + var paramTrackers = embeddedParameters.SelectMany(specification => specification.GetIdsForBackTrack(sessionFactory)); + foreach (var paramTracker in paramTrackers) + { + parameters[sqlParameterPos++].BackTrack = paramTracker; + } + return result; + } - container.AddChildren( - ASTFactory.CreateNode( - comparisonType, comparisonText, - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, lhsElementTexts[0]), - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, rhsElementTexts[0]) - ), - ASTFactory.CreateNode( - comparisonType, comparisonText, - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, lhsElementTexts[1]), - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, rhsElementTexts[1]) - )); + public void AddEmbeddedParameter(IParameterSpecification specification) + { + if (embeddedParameters == null) + { + embeddedParameters = new List<IParameterSpecification>(); + } + embeddedParameters.Add(specification); + } - // "pass along" our initial embedded parameter node(s) to the first generated - // sql fragment so that it can be handled later for parameter binding... - SqlFragment fragment = ( SqlFragment ) container.GetChild(0).GetChild(0); - if ( lhsEmbeddedCompositeParameterSpecification != null ) { - fragment.AddEmbeddedParameter( lhsEmbeddedCompositeParameterSpecification ); - } - if ( rhsEmbeddedCompositeParameterSpecification != null ) { - fragment.AddEmbeddedParameter( rhsEmbeddedCompositeParameterSpecification ); - } - } - else - { - container.ClearChildren(); - container.AddChildren( - ASTFactory.CreateNode(HqlSqlWalker.AND, "AND"), - ASTFactory.CreateNode( - comparisonType, comparisonText, - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, lhsElementTexts[i]), - ASTFactory.CreateNode(HqlSqlWalker.SQL_TOKEN, rhsElementTexts[i]) - )); + public bool HasEmbeddedParameters + { + get { return embeddedParameters != null && embeddedParameters.Count != 0; } + } - container = container.GetChild(0); - } + public IParameterSpecification[] GetEmbeddedParameters() + { + return embeddedParameters.ToArray(); + } + + private string Translate(int valueElements, string comparisonText, string[] lhsElementTexts, string[] rhsElementTexts) + { + var multicolumnComparisonClauses = new List<string>(); + for (int i = 0; i < valueElements; i++) + { + multicolumnComparisonClauses.Add(string.Format("{0} {1} {2}", lhsElementTexts[i], comparisonText, rhsElementTexts[i])); } + return "(" + string.Join(" and ", multicolumnComparisonClauses.ToArray()) + ")"; } private static string[] ExtractMutationTexts(IASTNode operand, int count) { if ( operand is ParameterNode ) { - string[] rtn = new string[count]; - for ( int i = 0; i < count; i++ ) - { - rtn[i] = "?"; - } - return rtn; + return Enumerable.Repeat("?", count).ToArray(); } - else if ( operand.Type == HqlSqlWalker.VECTOR_EXPR ) + if (operand is SqlNode) { - string[] rtn = new string[operand.ChildCount]; - - for (int x = 0; x < operand.ChildCount; x++) - { - rtn[ x++ ] = operand.GetChild(x).Text; - - } - return rtn; - } - else if ( operand is SqlNode ) - { string nodeText = operand.Text; - if ( nodeText.StartsWith( "(" ) ) + if (nodeText.StartsWith("(")) { - nodeText = nodeText.Substring( 1 ); + nodeText = nodeText.Substring(1); } - if ( nodeText.EndsWith( ")" ) ) + if (nodeText.EndsWith(")")) { - nodeText = nodeText.Substring( 0, nodeText.Length - 1 ); + nodeText = nodeText.Substring(0, nodeText.Length - 1); } string[] splits = nodeText.Split(new[] { ", " }, StringSplitOptions.None); - if ( count != splits.Length ) + if (count != splits.Length) { - throw new HibernateException( "SqlNode's text did not reference expected number of columns" ); + throw new HibernateException("SqlNode's text did not reference expected number of columns"); } return splits; } - else + if (operand.Type == HqlSqlWalker.VECTOR_EXPR) { - throw new HibernateException( "dont know how to extract row value elements from node : " + operand ); + var rtn = new string[operand.ChildCount]; + + for (int x = 0; x < operand.ChildCount; x++) + { + rtn[ x ] = operand.GetChild(x).Text; + } + return rtn; } + throw new HibernateException( "dont know how to extract row value elements from node : " + operand ); } protected static IType ExtractDataType(IASTNode operand) { IType type = null; - if ( operand is SqlNode ) + var sqlNode = operand as SqlNode; + if (sqlNode != null) { - type = ( ( SqlNode ) operand ).DataType; + type = sqlNode.DataType; } - if ( type == null && operand is IExpectedTypeAwareNode ) + var expectedTypeAwareNode = operand as IExpectedTypeAwareNode; + if (type == null && expectedTypeAwareNode != null) { - type = ( ( IExpectedTypeAwareNode ) operand ).ExpectedType; + type = expectedTypeAwareNode.ExpectedType; } return type; Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Collections.Generic; using System.Text; using Antlr.Runtime; @@ -10,6 +11,7 @@ using NHibernate.SqlCommand; using NHibernate.Type; using NHibernate.Util; +using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Hql.Ast.ANTLR.Tree { @@ -36,7 +38,7 @@ private bool _collectionJoin; private string _role; private bool _initialized; - private string _withClauseFragment; + private SqlString _withClauseFragment; private string _withClauseJoinAlias; private bool _filter; private IToken _token; @@ -73,7 +75,7 @@ _isAllPropertyFetch = fetch; } - public void SetWithClauseFragment(String withClauseJoinAlias, string withClauseFragment) + public void SetWithClauseFragment(String withClauseJoinAlias, SqlString withClauseFragment) { _withClauseJoinAlias = withClauseJoinAlias; _withClauseFragment = withClauseFragment; @@ -296,7 +298,7 @@ } } - public string WithClauseFragment + public SqlString WithClauseFragment { get { return _withClauseFragment; } } @@ -330,7 +332,22 @@ public override SqlString RenderText(Engine.ISessionFactoryImplementor sessionFactory) { - return SqlString.Parse(Text); + var result = SqlString.Parse(Text); + // query-parameter = the parameter specified in the NHibernate query + // sql-parameter = real parameter/s inside the final SQL + // here is where we suppose the SqlString has all sql-parameters in sequence for a given query-parameter. + // This happen when the query-parameter spans multiple columns (components,custom-types and so on). + if (HasEmbeddedParameters) + { + var parameters = result.GetParameters().ToArray(); + var sqlParameterPos = 0; + var paramTrackers = _embeddedParameters.SelectMany(specification => specification.GetIdsForBackTrack(sessionFactory)); + foreach (var paramTracker in paramTrackers) + { + parameters[sqlParameterPos++].BackTrack = paramTracker; + } + } + return result; } public string RenderCollectionSelectFragment(int size, int k) Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/SqlFragment.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/SqlFragment.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/SqlFragment.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Antlr.Runtime; using NHibernate.Param; using NHibernate.SqlCommand; @@ -39,7 +40,22 @@ public override SqlString RenderText(Engine.ISessionFactoryImplementor sessionFactory) { - return SqlString.Parse(Text); + var result = SqlString.Parse(Text); + // query-parameter = the parameter specified in the NHibernate query + // sql-parameter = real parameter/s inside the final SQL + // here is where we suppose the SqlString has all sql-parameters in sequence for a given query-parameter. + // This happen when the query-parameter spans multiple columns (components,custom-types and so on). + if (HasEmbeddedParameters) + { + var parameters = result.GetParameters().ToArray(); + var sqlParameterPos = 0; + var paramTrackers = _embeddedParameters.SelectMany(specification => specification.GetIdsForBackTrack(sessionFactory)); + foreach (var paramTracker in paramTrackers) + { + parameters[sqlParameterPos++].BackTrack = paramTracker; + } + } + return result; } // ParameterContainer impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/JoinProcessor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/JoinProcessor.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/JoinProcessor.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text; using NHibernate.Engine; @@ -129,11 +130,7 @@ log.Debug( "Using FROM fragment [" + fromFragment + "]" ); } - ProcessDynamicFilterParameters( - fromFragment, - fromElement, - _walker - ); + ProcessDynamicFilterParameters(fromFragment,fromElement,_walker); } _syntheticAndFactory.AddWhereFragment( @@ -167,43 +164,7 @@ return; } - Dialect.Dialect dialect = walker.SessionFactoryHelper.Factory.Dialect; - - string symbols = new StringBuilder().Append( ParserHelper.HqlSeparators ) - .Append( dialect.OpenQuote) - .Append( dialect.CloseQuote) - .ToString(); - - StringTokenizer tokens = new StringTokenizer( sqlFragment.ToString(), symbols, true ); - StringBuilder result = new StringBuilder(); - - foreach (string token in tokens) - { - if ( token.StartsWith( ParserHelper.HqlVariablePrefix ) ) - { - string filterParameterName = token.Substring( 1 ); - string[] parts = StringHelper.ParseFilterParameterName( filterParameterName ); - FilterImpl filter = ( FilterImpl ) walker.EnabledFilters[parts[0]]; - Object value = filter.GetParameter( parts[1] ); - IType type = filter.FilterDefinition.GetParameterType( parts[1] ); - String typeBindFragment = StringHelper.Join( - ",", - ArrayHelper.FillArray( "?", type.GetColumnSpan( walker.SessionFactoryHelper.Factory ) ) - ); - string bindFragment = ( value != null && value is ICollection) - ? StringHelper.Join( ",", ArrayHelper.FillArray( typeBindFragment, ( ( ICollection ) value ).Count ) ) - : typeBindFragment; - //result.Append( bindFragment ); - result.Append(token); - container.AddEmbeddedParameter( new DynamicFilterParameterSpecification( parts[0], parts[1], type ) ); - } - else - { - result.Append( token ); - } - } - - container.Text = result.ToString(); + container.Text = sqlFragment.ToString(); // dynamic-filters are processed altogether by Loader } private static bool HasDynamicFilterParam(SqlString sqlFragment) Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2011-05-30 13:58:24 UTC (rev 5881) @@ -495,6 +495,9 @@ <Compile Include="NonUniqueResultException.cs" /> <Compile Include="ObjectDeletedException.cs" /> <Compile Include="ObjectNotFoundException.cs" /> + <Compile Include="Param\ParametersBackTrackExtensions.cs" /> + <Compile Include="Param\QuerySkipParameterSpecification.cs" /> + <Compile Include="Param\QueryTakeParameterSpecification.cs" /> <Compile Include="PersistentObjectException.cs" /> <Compile Include="Persister\PersisterFactory.cs" /> <Compile Include="Properties\CamelCaseMUnderscoreStrategy.cs" /> Modified: trunk/nhibernate/src/NHibernate/Param/AbstractExplicitParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/AbstractExplicitParameterSpecification.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Param/AbstractExplicitParameterSpecification.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,43 +1,64 @@ -using System.Data; +using System; +using System.Collections.Generic; +using System.Data; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Param { - public abstract class AbstractExplicitParameterSpecification : IExplicitParameterSpecification + public abstract class AbstractExplicitParameterSpecification : IExplicitParameterSpecification { - private readonly int _sourceLine; - private readonly int _sourceColumn; - private IType _expectedType; + private readonly int sourceColumn; + private readonly int sourceLine; /// <summary> /// Constructs an AbstractExplicitParameterSpecification. /// </summary> /// <param name="sourceLine">sourceLine</param> /// <param name="sourceColumn">sourceColumn</param> - protected AbstractExplicitParameterSpecification(int sourceLine, int sourceColumn) { - _sourceLine = sourceLine; - _sourceColumn = sourceColumn; + protected AbstractExplicitParameterSpecification(int sourceLine, int sourceColumn) + { + this.sourceLine = sourceLine; + this.sourceColumn = sourceColumn; } - public int SourceLine + #region IExplicitParameterSpecification Members + + public int SourceLine { - get { return _sourceLine; } + get { return sourceLine; } } public int SourceColumn { - get { return _sourceColumn; } + get { return sourceColumn; } } - public IType ExpectedType + public IType ExpectedType { get; set; } + + public abstract string RenderDisplayInfo(); + public abstract IEnumerable<string> GetIdsForBackTrack(IMapping sessionFactory); + public abstract void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session); + public abstract void SetEffectiveType(QueryParameters queryParameters); + + #endregion + + protected int GetParemeterSpan(IMapping sessionFactory) { - get { return _expectedType; } - set { _expectedType = value; } + if (sessionFactory == null) + { + throw new ArgumentNullException("sessionFactory"); + } + if (ExpectedType != null) + { + // TODO: we have to find a way to set all expected types during the query parsing + var paremeterSpan = ExpectedType.GetColumnSpan(sessionFactory); + // NOTE: the OneToOneType does not return the real ColumnSpan + return paremeterSpan == 0 ? 1 : paremeterSpan; + } + // TODO: (see above) when the ExpectedType is null we will set the BackTrackId just for the first position (not a big problem because IType does not support something different... so far) + return 1; } - - public abstract string RenderDisplayInfo(); - public abstract object IdForBackTrack { get; } - public abstract int Bind(IDbCommand statement, QueryParameters qp, ISessionImplementor session, int position); } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Param/AggregatedIndexCollectionSelectorParameterSpecifications.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/AggregatedIndexCollectionSelectorParameterSpecifications.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Param/AggregatedIndexCollectionSelectorParameterSpecifications.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -3,6 +3,7 @@ using System.Data; using System.Text; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Param @@ -10,20 +11,25 @@ public class AggregatedIndexCollectionSelectorParameterSpecifications : IParameterSpecification { private readonly IList<IParameterSpecification> _paramSpecs; - private readonly Guid idForBackTrack = Guid.NewGuid(); public AggregatedIndexCollectionSelectorParameterSpecifications(IList<IParameterSpecification> paramSpecs) { _paramSpecs = paramSpecs; } - public int Bind(IDbCommand statement, QueryParameters qp, ISessionImplementor session, int position) + //public int Bind(IDbCommand statement, QueryParameters qp, ISessionImplementor session, int position) + //{ + // int bindCount = 0; + + // foreach (IParameterSpecification spec in _paramSpecs) + // { + // bindCount += spec.Bind(statement, qp, session, position + bindCount); + // } + // return bindCount; + //} + + + public void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) { - int bindCount = 0; - - foreach (IParameterSpecification spec in _paramSpecs) - { - bindCount += spec.Bind(statement, qp, session, position + bindCount); - } - return bindCount; + throw new NotImplementedException(); } public IType ExpectedType @@ -38,9 +44,9 @@ return "index-selector [" + CollectDisplayInfo() + "]" ; } - public object IdForBackTrack + public IEnumerable<string> GetIdsForBackTrack(IMapping sessionFactory) { - get { return idForBackTrack; } + throw new NotImplementedException(); } private string CollectDisplayInfo() Modified: trunk/nhibernate/src/NHibernate/Param/CollectionFilterKeyParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/CollectionFilterKeyParameterSpecification.cs 2011-05-29 18:36:56 UTC (rev 5880) +++ trunk/nhibernate/src/NHibernate/Param/CollectionFilterKeyParameterSpecification.cs 2011-05-30 13:58:24 UTC (rev 5881) @@ -1,17 +1,21 @@ using System; +using System.Collections.Generic; using System.Data; +using System.Linq; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Param { - class CollectionFilterKeyParameterSpecification : IParameterSpecification + public class CollectionFilterKeyParameterSpecification : IParameterSpecification { - private readonly string _collectionRole; - private readonly IType _keyType; - private readonly int _queryParameterPosition; - private readonly string idForBackTrack; + private const string CollectionFilterParameterIdTemplate = "<collfilter{0}{1}_{2}>"; + private readonly string collectionRole; + private readonly IType keyType; + private readonly int queryParameterPosition; + /// <summary> /// Creates a specialized collection-filter collection-key parameter spec. /// </summary> @@ -20,37 +24,66 @@ /// <param name="queryParameterPosition">The position within QueryParameters where we can find the appropriate param value to bind.</param> public CollectionFilterKeyParameterSpecification(string collectionRole, IType keyType, int queryParameterPosition) { - _collectionRole = collectionRole; - _keyType = keyType; - _queryParameterPosition = queryParameterPosition; - idForBackTrack = "nhcollkey_" + _collectionRole + "nh"; + this.collectionRole = collectionRole; + this.keyType = keyType; + this.queryParameterPosition = queryParameterPosition; } - public int Bind( - IDbCommand statement, - QueryParameters qp, - ISessionImplementor session, - int position) - { - object value = qp.PositionalParameterValues[_queryParameterPosition]; - _keyType.NullSafeSet(statement, value, position, session); - return _keyType.GetColumnSpan(session.Factory); - } + #region IParameterSpecification Members public IType ExpectedType { - get { return _keyType; } + get { return keyType; } set { throw new InvalidOperationException(); } } public string RenderDisplayInfo() { - return "collection-filter-key=" + _collectionRole; + return "collection-filter-key=" + collectionRole; } - public object IdForBackTrack + public IEnumerable<string> GetIdsForBackTrack(IMapping sessionFactory) { - get { return idForBackTrack; } + int paremeterSpan = keyType.GetColumnSpan(sessionFactory); + for (int i = 0; i < paremeterSpan; i++) + { + yield return string.Format(CollectionFilterParameterIdTemplate, collectionRole, queryParameterPosition, i); + } } + + public void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { + IType type = keyType; + object value = queryParameters.PositionalParameterValues[queryParameterPosition]; + + string backTrackId = GetIdsForBackTrack(session.Factory).First(); // just the first because IType suppose the oders in certain sequence + int position = sqlQueryParametersList.GetEffectiveParameterLocations(backTrackId).Single(); // an HQL positional parameter can't appear more than once + type.NullSafeSet(command, value, position, session); + } + + #endregion + + public override bool Equals(object obj) + { + return base.Equals(obj as CollectionFilterKeyParameterSpecification); + } + + public bool Equals(CollectionFilterKeyParameterSpecification other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + if (ReferenceEquals(this, other)) + { + return true; + } + return other.queryParameterPosition == queryParameterPosition; + } + + public override int GetHashCode() + { + return queryParameterPosition ^ 877; + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Param/DynamicFilterParameterSp... [truncated message content] |