From: <fab...@us...> - 2009-03-28 19:07:13
|
Revision: 4158 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4158&view=rev Author: fabiomaulo Date: 2009-03-28 19:07:03 +0000 (Sat, 28 Mar 2009) Log Message: ----------- Fix NH-1693 and one more step to fix NH-1098 Thanks to Richard Brown Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs trunk/nhibernate/src/NHibernate/Hql/Classic/QueryTranslator.cs trunk/nhibernate/src/NHibernate/Impl/MultiCriteriaImpl.cs trunk/nhibernate/src/NHibernate/Impl/MultiQueryImpl.cs trunk/nhibernate/src/NHibernate/Loader/Collection/SubselectCollectionLoader.cs trunk/nhibernate/src/NHibernate/Loader/Collection/SubselectOneToManyLoader.cs trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs trunk/nhibernate/src/NHibernate/Loader/Loader.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1098/FilterParameterOrderFixture.cs trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Fixture.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Mappings.hbm.xml trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Model.cs Modified: trunk/nhibernate/src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -57,58 +57,6 @@ } } - /// <summary> - /// Bind positional parameter values to the <tt>PreparedStatement</tt> - /// (these are parameters specified by a JDBC-style ?). - /// </summary> - private static int BindPositionalParameters(IDbCommand st, QueryParameters queryParameters, int start, ISessionImplementor session) - { - object[] values = queryParameters.FilteredPositionalParameterValues; - IType[] types = queryParameters.FilteredPositionalParameterTypes; - int span = 0; - for (int i = 0; i < values.Length; i++) - { - types[i].NullSafeSet(st, values[i], start + span, session); - span += types[i].GetColumnSpan(session.Factory); - } - return span; - } - - /// <summary> - /// Bind named parameters to the <tt>PreparedStatement</tt>. This has an - /// empty implementation on this superclass and should be implemented by - /// subclasses (queries) which allow named parameters. - /// </summary> - private void BindNamedParameters(IDbCommand ps, IEnumerable<KeyValuePair<string, TypedValue>> namedParams, int start, ISessionImplementor session) - { - if (namedParams != null) - { - // assumes that types are all of span 1 - int result = 0; - foreach (KeyValuePair<string, TypedValue> param in namedParams) - { - string name = param.Key; - TypedValue typedval = param.Value; - - int[] locs = GetNamedParameterLocs(name); - for (int i = 0; i < locs.Length; i++) - { - if (log.IsDebugEnabled) - { - log.Debug(string.Format("BindNamedParameters() {0} -> {1} [{2}]", typedval.Value, name, (locs[i] + start))); - } - typedval.Type.NullSafeSet(ps, typedval.Value, locs[i] + start, session); - } - result += locs.Length; - } - return; - } - else - { - return; - } - } - private void CoordinateSharedCacheCleanup(ISessionImplementor session) { BulkOperationCleanupAction action = new BulkOperationCleanupAction(session, CustomQuery.QuerySpaces); @@ -149,9 +97,12 @@ // NH Difference : set Timeout for native query ps.CommandTimeout = selection.Timeout; } - int col = 0; // NH Different (initialized to 1 in JAVA) - col += BindPositionalParameters(ps, queryParameters, col, session); - BindNamedParameters(ps, queryParameters.NamedParameters, col, session); + // NH Different behavior: + // The inital value is 0 (initialized to 1 in JAVA) + // The responsibility of parameter binding was entirely moved to QueryParameters + // to deal with positionslParameter+NamedParameter+ParameterOfFilters + + queryParameters.BindParameters(ps, GetNamedParameterLocs, 0, session); result = session.Batcher.ExecuteNonQuery(ps); } finally @@ -180,7 +131,7 @@ List<IType> paramTypeList = new List<IType>(); int span = 0; - foreach (IType type in parameters.FilteredPositionalParameterTypes) + foreach (IType type in parameters.PositionalParameterTypes) { paramTypeList.Add(type); span += type.GetColumnSpan(session.Factory); Modified: trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -1,5 +1,7 @@ using System; using System.Collections; +using System.Collections.Generic; +using System.Data; using log4net; using NHibernate.Hql.Classic; using NHibernate.Impl; @@ -7,7 +9,6 @@ using NHibernate.Transform; using NHibernate.Type; using NHibernate.Util; -using System.Collections.Generic; namespace NHibernate.Engine { @@ -17,45 +18,39 @@ [Serializable] public sealed class QueryParameters { - private static readonly ILog log = LogManager.GetLogger(typeof(QueryParameters)); + public delegate int[] GetNamedParameterLocations(string parameterName); + private static readonly ILog log = LogManager.GetLogger(typeof (QueryParameters)); + private IType[] _positionalParameterTypes; private object[] _positionalParameterValues; + private int[] _positionalParameterLocations; private IDictionary<string, TypedValue> _namedParameters; private IDictionary<string, LockMode> _lockModes; + private IList<IType> filteredParameterTypes; + private IList<object> filteredParameterValues; + private IList<int> filteredParameterLocations; private RowSelection _rowSelection; private bool _cacheable; private string _cacheRegion; - private bool _forceCacheRefresh; private object[] _collectionKeys; private object _optionalObject; private string _optionalEntityName; private object _optionalId; private string _comment; - private bool _naturalKeyLookup; private bool _readOnly; - private bool _callable; - private bool autoDiscoverTypes; private SqlString processedSQL; - private IType[] processedPositionalParameterTypes; - private object[] processedPositionalParameterValues; private readonly IResultTransformer _resultTransformer; // not implemented: private ScrollMode _scrollMode; - public QueryParameters() - : this(ArrayHelper.EmptyTypeArray, ArrayHelper.EmptyObjectArray) - { - } + public QueryParameters() : this(ArrayHelper.EmptyTypeArray, ArrayHelper.EmptyObjectArray) {} - public QueryParameters(IType type, object value) - : this(new IType[] { type }, new object[] { value }) - { - } + public QueryParameters(IType type, object value) : this(new[] {type}, new[] {value}) {} - public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, - object optionalObject, string optionalEntityName, object optionalObjectId) + public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, object optionalObject, + string optionalEntityName, object optionalObjectId) : this(positionalParameterTypes, postionalParameterValues) { _optionalObject = optionalObject; @@ -64,33 +59,31 @@ } public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues) - : this(positionalParameterTypes, postionalParameterValues, null, null, false, null, null, false, null) - { - } + : this(positionalParameterTypes, postionalParameterValues, null, null, false, null, null, false, null) {} - public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, - object[] collectionKeys) - : this(positionalParameterTypes, postionalParameterValues, null, collectionKeys) - { - } + public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, object[] collectionKeys) + : this(positionalParameterTypes, postionalParameterValues, null, collectionKeys) {} public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, - IDictionary<string, TypedValue> namedParameters, object[] collectionKeys) - : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, null, null, collectionKeys, null) - { - } + IDictionary<string, TypedValue> namedParameters, object[] collectionKeys) + : this( + positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, null, null, + collectionKeys, null) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, - IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) - : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, false, cacheable, cacheRegion, comment, null, transformer) + IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool cacheable, + string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) + : this( + positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, false, cacheable, cacheRegion, + comment, null, transformer) { - _naturalKeyLookup = isLookupByNaturalKey; + NaturalKeyLookup = isLookupByNaturalKey; } public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, - IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes, RowSelection rowSelection, - bool readOnly, bool cacheable, string cacheRegion, string comment, - object[] collectionKeys, IResultTransformer transformer) + IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes, + RowSelection rowSelection, bool readOnly, bool cacheable, string cacheRegion, string comment, + object[] collectionKeys, IResultTransformer transformer) { _positionalParameterTypes = positionalParameterTypes; _positionalParameterValues = positionalParameterValues; @@ -103,13 +96,21 @@ _collectionKeys = collectionKeys; _readOnly = readOnly; _resultTransformer = transformer; + + if (_positionalParameterLocations == null) + { + CreatePositionalParameterLocations(); + } } public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, - IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes, RowSelection rowSelection, - bool readOnly, bool cacheable, string cacheRegion, string comment, object[] collectionKeys, - object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer) - : this(positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, readOnly, cacheable, cacheRegion, comment, collectionKeys, transformer) + IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes, + RowSelection rowSelection, bool readOnly, bool cacheable, string cacheRegion, string comment, + object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, + IResultTransformer transformer) + : this( + positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, readOnly, cacheable, + cacheRegion, comment, collectionKeys, transformer) { _optionalEntityName = optionalEntityName; _optionalId = optionalId; @@ -141,6 +142,11 @@ set { _positionalParameterTypes = value; } } + public int[] PositionalParameterLocations + { + get { return _positionalParameterLocations; } + } + /// <summary> /// Gets or sets an array of <see cref="object"/> objects that is stored at the index /// of the Parameter. @@ -171,6 +177,18 @@ set { _lockModes = value; } } + private void CreatePositionalParameterLocations() + { + if (_positionalParameterTypes != null) + { + _positionalParameterLocations = new int[_positionalParameterTypes.Length]; + for (int i = 0; i < _positionalParameterLocations.Length; i++) + { + _positionalParameterLocations[i] = i; + } + } + } + private int SafeLength(Array array) { if (array == null) @@ -183,11 +201,10 @@ /// <summary></summary> public void LogParameters(ISessionFactoryImplementor factory) { - Printer print = new Printer(factory); + var print = new Printer(factory); if (_positionalParameterValues.Length != 0) { - log.Debug("parameters: " - + print.ToString(_positionalParameterTypes, _positionalParameterValues)); + log.Debug("parameters: " + print.ToString(_positionalParameterTypes, _positionalParameterValues)); } if (_namedParameters != null) @@ -228,16 +245,12 @@ if (typesLength != valuesLength) { - throw new QueryException("Number of positional parameter types (" + typesLength + - ") does not match number of positional parameter values (" + valuesLength + ")"); + throw new QueryException("Number of positional parameter types (" + typesLength + + ") does not match number of positional parameter values (" + valuesLength + ")"); } } - public bool ForceCacheRefresh - { - get { return _forceCacheRefresh; } - set { _forceCacheRefresh = value; } - } + public bool ForceCacheRefresh { get; set; } public string OptionalEntityName { @@ -263,11 +276,7 @@ set { _collectionKeys = value; } } - public bool Callable - { - get { return _callable; } - set { _callable = value; } - } + public bool Callable { get; set; } public bool ReadOnly { @@ -277,100 +286,178 @@ /************** Filters ********************************/ + private void AdjustPostionalParameterLocations(int parameterIndex) + { + for (int i = 0; i < _positionalParameterLocations.Length; i++) + { + if (_positionalParameterLocations[i] >= parameterIndex) + { + _positionalParameterLocations[i]++; + } + } + } + public void ProcessFilters(SqlString sql, ISessionImplementor session) { + filteredParameterValues = new List<object>(); + filteredParameterTypes = new List<IType>(); + filteredParameterLocations = new List<int>(); + if (session.EnabledFilters.Count == 0 || sql.ToString().IndexOf(ParserHelper.HqlVariablePrefix) < 0) { - processedPositionalParameterValues = PositionalParameterValues; - processedPositionalParameterTypes = PositionalParameterTypes; processedSQL = sql; + return; } - else - { - Dialect.Dialect dialect = session.Factory.Dialect; - string symbols = ParserHelper.HqlSeparators + dialect.OpenQuote + dialect.CloseQuote; - SqlStringBuilder result = new SqlStringBuilder(); + Dialect.Dialect dialect = session.Factory.Dialect; + string symbols = ParserHelper.HqlSeparators + dialect.OpenQuote + dialect.CloseQuote; - List<object> parameters = new List<object>(); - List<IType> parameterTypes = new List<IType>(); - int parameterCount = 0; // keep track of the positional parameter + var result = new SqlStringBuilder(); - foreach (object part in sql.Parts) + int parameterIndex = 0; // keep track of the positional parameter + + foreach (var part in sql.Parts) + { + if (part is Parameter) { - if (part is Parameter) - { - result.AddParameter(); + result.AddParameter(); - // (?) 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 - // order of appearance is best that can be done at this point of time, but if they - // are mixed with named parameters, the order is still wrong, because values and - // types for the named parameters are added later to the end of the list. - // see test fixture NH-1098 - if (parameterCount < PositionalParameterValues.Length) - { - parameters.Add(PositionalParameterValues[parameterCount]); - parameterTypes.Add(PositionalParameterTypes[parameterCount]); - parameterCount++; - } + // (?) 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 + // order of appearance is best that can be done at this point of time, but if they + // are mixed with named parameters, the order is still wrong, because values and + // types for the named parameters are added later to the end of the list. + // see test fixture NH-1098 - continue; - } + parameterIndex++; + continue; + } - StringTokenizer tokenizer = new StringTokenizer((string)part, symbols, true); + var tokenizer = new StringTokenizer((string) part, symbols, true); - foreach (string token in tokenizer) + foreach (var token in tokenizer) + { + if (token.StartsWith(ParserHelper.HqlVariablePrefix)) { - if (token.StartsWith(ParserHelper.HqlVariablePrefix)) + string filterParameterName = token.Substring(1); + object value = session.GetFilterParameterValue(filterParameterName); + IType type = session.GetFilterParameterType(filterParameterName); + + // If the value is not a value of the type but a collection of values... + if (value != null && !type.ReturnedClass.IsAssignableFrom(value.GetType()) && // Added to fix NH-882 + typeof (ICollection).IsAssignableFrom(value.GetType())) { - string filterParameterName = token.Substring(1); - object value = session.GetFilterParameterValue(filterParameterName); - IType type = session.GetFilterParameterType(filterParameterName); - - // If the value is not a value of the type but a collection of values... - if (value != null && - !type.ReturnedClass.IsAssignableFrom(value.GetType()) && // Added to fix NH-882 - typeof(ICollection).IsAssignableFrom(value.GetType())) + var coll = (ICollection) value; + int i = 0; + foreach (var elementValue in coll) { - ICollection coll = (ICollection)value; - int i = 0; - foreach (object elementValue in coll) - { - i++; - int span = type.GetColumnSpan(session.Factory); - if (span > 0) - { - result.AddParameter(); - parameters.Add(elementValue); - parameterTypes.Add(type); - if (i < coll.Count) - result.Add(", "); - } - } - } - else - { + i++; int span = type.GetColumnSpan(session.Factory); if (span > 0) { result.AddParameter(); - parameters.Add(value); - parameterTypes.Add(type); + filteredParameterTypes.Add(type); + filteredParameterValues.Add(elementValue); + filteredParameterLocations.Add(parameterIndex); + AdjustPostionalParameterLocations(parameterIndex); + parameterIndex++; + if (i < coll.Count) + { + result.Add(", "); + } } } } else { - result.Add(token); + int span = type.GetColumnSpan(session.Factory); + if (span > 0) + { + result.AddParameter(); + filteredParameterTypes.Add(type); + filteredParameterValues.Add(value); + filteredParameterLocations.Add(parameterIndex); + AdjustPostionalParameterLocations(parameterIndex); + parameterIndex++; + } } } + else + { + result.Add(token); + } } + } - processedPositionalParameterValues = parameters.ToArray(); - processedPositionalParameterTypes = parameterTypes.ToArray(); - processedSQL = result.ToSqlString(); + processedSQL = result.ToSqlString(); + } + + public int BindParameters(IDbCommand command, GetNamedParameterLocations getNamedParameterLocations, int start, + ISessionImplementor session) + { + var values = new List<object>(); + var types = new List<IType>(); + var sources = new List<string>(); + + for (int i = 0; i < _positionalParameterLocations.Length; i++) + { + int location = _positionalParameterLocations[i]; + object value = _positionalParameterValues[i]; + IType type = _positionalParameterTypes[i]; + ArrayHelper.SafeSetValue(values, location, value); + ArrayHelper.SafeSetValue(types, location, type); + ArrayHelper.SafeSetValue(sources, location, "Positional" + i); } + + for (int i = 0; i < filteredParameterLocations.Count; i++) + { + int location = filteredParameterLocations[i]; + object value = filteredParameterValues[i]; + IType type = filteredParameterTypes[i]; + ArrayHelper.SafeSetValue(values, location, value); + ArrayHelper.SafeSetValue(types, location, type); + ArrayHelper.SafeSetValue(sources, location, "Filter" + i); + } + + if ((_namedParameters != null) && (_namedParameters.Count > 0)) + { + foreach (var namedParameter in _namedParameters) + { + string name = namedParameter.Key; + TypedValue typedval = namedParameter.Value; + int[] locations = getNamedParameterLocations(name); + for (int i = 0; i < locations.Length; i++) + { + int location = locations[i]; + + // can still clash with positional parameters + // could consider throwing an exception to locate problem (NH-1098) + while ((location < types.Count) && (types[location] != null)) + { + location++; + } + + ArrayHelper.SafeSetValue(values, location, typedval.Value); + ArrayHelper.SafeSetValue(types, location, typedval.Type); + ArrayHelper.SafeSetValue(sources, location, "name" + i); + } + } + } + + int span = 0; + for (int i = 0; i < values.Count; i++) + { + IType type = types[i]; + object value = values[i]; + if (log.IsDebugEnabled) + { + log.Debug(string.Format("BindParameters({0}:{1}) {2} -> [{3}]", "Named", type, value, i)); + } + type.NullSafeSet(command, value, start + span, session); + span += type.GetColumnSpan(session.Factory); + } + + return span; } public SqlString FilteredSQL @@ -378,42 +465,41 @@ get { return processedSQL; } } - public object[] FilteredPositionalParameterValues + public IList<IType> FilteredParameterTypes { - get { return processedPositionalParameterValues; } + get { return filteredParameterTypes; } } - public IType[] FilteredPositionalParameterTypes + public IList<object> FilteredParameterValues { - get { return processedPositionalParameterTypes; } + get { return filteredParameterValues; } } - public bool NaturalKeyLookup + public IList<int> FilteredParameterLocations { - get { return _naturalKeyLookup; } - set { _naturalKeyLookup = value; } + get { return filteredParameterLocations; } } + public bool NaturalKeyLookup { get; set; } + public IResultTransformer ResultTransformer { get { return _resultTransformer; } } - public bool HasAutoDiscoverScalarTypes - { - get { return autoDiscoverTypes; } - set { autoDiscoverTypes = value; } - } + public bool HasAutoDiscoverScalarTypes { get; set; } public QueryParameters CreateCopyUsing(RowSelection selection) { - QueryParameters copy = new QueryParameters(_positionalParameterTypes, _positionalParameterValues, - _namedParameters, _lockModes, selection, _readOnly, _cacheable, _cacheRegion, _comment, - _collectionKeys, _optionalObject, _optionalEntityName, _optionalId, _resultTransformer); + var copy = new QueryParameters(_positionalParameterTypes, _positionalParameterValues, _namedParameters, _lockModes, + selection, _readOnly, _cacheable, _cacheRegion, _comment, _collectionKeys, + _optionalObject, _optionalEntityName, _optionalId, _resultTransformer); + copy._positionalParameterLocations = _positionalParameterLocations; copy.processedSQL = processedSQL; - copy.processedPositionalParameterTypes = processedPositionalParameterTypes; - copy.processedPositionalParameterValues = processedPositionalParameterValues; + copy.filteredParameterTypes = filteredParameterTypes; + copy.filteredParameterValues = filteredParameterValues; + copy.filteredParameterLocations = filteredParameterLocations; return copy; } } -} +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Hql/Classic/QueryTranslator.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Classic/QueryTranslator.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Hql/Classic/QueryTranslator.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -701,6 +701,21 @@ return o.ToArray(); } + protected override void AdjustNamedParameterLocationsForQueryParameters(QueryParameters parameters) + { + foreach (int existingParameterLocation in parameters.FilteredParameterLocations) + { + foreach (IList<int> namedParameterLocations in namedParameters.Values) + { + for (int index = 0; index < namedParameterLocations.Count; index++) + { + if (namedParameterLocations[index] == existingParameterLocation) + namedParameterLocations[index]++; + } + } + } + } + public static string ScalarName(int x, int y) { return new StringBuilder() Modified: trunk/nhibernate/src/NHibernate/Impl/MultiCriteriaImpl.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Impl/MultiCriteriaImpl.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Impl/MultiCriteriaImpl.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -307,8 +307,7 @@ for (int i = 0; i < loaders.Count; i++) { QueryParameters parameter = parameters[i]; - colIndex += loaders[i].BindPositionalParameters(command, parameter, colIndex, session); - colIndex += loaders[i].BindNamedParameters(command, parameter.NamedParameters, colIndex, session); + colIndex += parameter.BindParameters(command, loaders[i].GetNamedParameterLocs, colIndex, session); } return colIndex; } Modified: trunk/nhibernate/src/NHibernate/Impl/MultiQueryImpl.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Impl/MultiQueryImpl.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Impl/MultiQueryImpl.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -632,8 +632,7 @@ { QueryTranslator translator = Translators[i]; QueryParameters parameter = Parameters[i]; - colIndex += translator.BindPositionalParameters(command, parameter, colIndex, session); - colIndex += translator.BindNamedParameters(command, parameter.NamedParameters, colIndex, session); + colIndex += parameter.BindParameters(command, translator.GetNamedParameterLocs, colIndex, session); } return colIndex; } Modified: trunk/nhibernate/src/NHibernate/Loader/Collection/SubselectCollectionLoader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Collection/SubselectCollectionLoader.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Loader/Collection/SubselectCollectionLoader.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -28,8 +28,9 @@ } namedParameters = queryParameters.NamedParameters; - types = queryParameters.FilteredPositionalParameterTypes; - values = queryParameters.FilteredPositionalParameterValues; + // NH Different behavior: to deal with positionslParameter+NamedParameter+ParameterOfFilters + types = queryParameters.PositionalParameterTypes; + values = queryParameters.PositionalParameterValues; this.namedParameterLocMap = namedParameterLocMap; } @@ -42,5 +43,23 @@ { return namedParameterLocMap[name]; } + + protected override void AdjustNamedParameterLocationsForQueryParameters(QueryParameters parameters) + { + if (namedParameterLocMap == null) + return; + + foreach (int existingParameterLocation in parameters.FilteredParameterLocations) + { + foreach (IList<int> namedParameterLocations in namedParameterLocMap.Values) + { + for (int index = 0; index < namedParameterLocations.Count; index++) + { + if (namedParameterLocations[index] >= existingParameterLocation) + namedParameterLocations[index]++; + } + } + } + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Loader/Collection/SubselectOneToManyLoader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Collection/SubselectOneToManyLoader.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Loader/Collection/SubselectOneToManyLoader.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -30,8 +30,9 @@ } namedParameters = queryParameters.NamedParameters; - types = queryParameters.FilteredPositionalParameterTypes; - values = queryParameters.FilteredPositionalParameterValues; + // NH Different behavior: to deal with positionslParameter+NamedParameter+ParameterOfFilters + types = queryParameters.PositionalParameterTypes; + values = queryParameters.PositionalParameterValues; this.namedParameterLocMap = namedParameterLocMap; } @@ -44,5 +45,24 @@ { return namedParameterLocMap[name]; } + + protected override void AdjustNamedParameterLocationsForQueryParameters(QueryParameters parameters) + { + if (namedParameterLocMap == null) + return; + + foreach (int existingParameterLocation in parameters.FilteredParameterLocations) + { + foreach (IList<int> namedParameterLocations in namedParameterLocMap.Values) + { + for (int index = 0; index < namedParameterLocations.Count; index++) + { + if (namedParameterLocations[index] >= existingParameterLocation) + namedParameterLocations[index]++; + } + } + } + } + } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -320,6 +320,30 @@ } } + protected override void AdjustNamedParameterLocationsForQueryParameters(QueryParameters parameters) + { + var existingParameterLocations = parameters.FilteredParameterLocations.GetEnumerator(); + while (existingParameterLocations.MoveNext()) + { + foreach (string name in parameters.NamedParameters.Keys) + { + object locations = namedParameterBindPoints[name]; + if (locations is int) + { + namedParameterBindPoints[name] = ((int)locations) + 1; + } + else + { + IList locationsList = (IList)locations; + for (int i = 0; i < locationsList.Count; i++) + { + locationsList[i] = ((int)locationsList[i]) + 1; + } + } + } + } + } + protected override void AutoDiscoverTypes(IDataReader rs) { MetaData metadata = new MetaData(rs); Modified: trunk/nhibernate/src/NHibernate/Loader/Loader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -1173,6 +1173,7 @@ protected virtual SqlString ProcessFilters(QueryParameters parameters, ISessionImplementor session) { parameters.ProcessFilters(SqlString, session); + AdjustNamedParameterLocationsForQueryParameters(parameters); return parameters.FilteredSQL; } @@ -1256,79 +1257,26 @@ protected internal virtual int BindParameterValues(IDbCommand statement, QueryParameters queryParameters, int startIndex, ISessionImplementor session) { - int span = 0; - span += BindPositionalParameters(statement, queryParameters, startIndex, session); - span += BindNamedParameters(statement, queryParameters.NamedParameters, startIndex + span, session); - return span; + // NH Different behavior: + // The responsibility of parameter binding was entirely moved to QueryParameters + // to deal with positionslParameter+NamedParameter+ParameterOfFilters + return queryParameters.BindParameters(statement, GetNamedParameterLocs, 0, session); } - /// <summary> - /// Bind positional parameter values to the <c>IDbCommand</c> - /// (these are parameters specified by ?). - /// </summary> - /// <param name="st">The ADO prepared statement </param> - /// <param name="queryParameters">The encapsulation of the parameter values to be bound. </param> - /// <param name="start">The position from which to start binding parameter values. </param> - /// <param name="session">The originating session. </param> - /// <returns> The number of ADO bind positions actually bound during this method execution. </returns> - protected internal virtual int BindPositionalParameters(IDbCommand st, QueryParameters queryParameters, int start, - ISessionImplementor session) + public virtual int[] GetNamedParameterLocs(string name) { - object[] values = queryParameters.FilteredPositionalParameterValues; - IType[] types = queryParameters.FilteredPositionalParameterTypes; - - int span = 0; - for (int i = 0; i < values.Length; i++) - { - types[i].NullSafeSet(st, values[i], start + span, session); - span += types[i].GetColumnSpan(session.Factory); - } - - return span; + throw new AssertionFailure("no named parameters"); } - /// <summary> - /// Bind named parameters to the <c>IDbCommand</c> - /// </summary> - /// <param name="st">The <see cref="IDbCommand"/> that contains the parameters.</param> - /// <param name="namedParams">The named parameters (key) and the values to set.</param> - /// <param name="session">The <see cref="ISession"/> this Loader is using.</param> - /// <param name="start"></param> - protected internal virtual int BindNamedParameters(IDbCommand st, IDictionary<string, TypedValue> namedParams, - int start, ISessionImplementor session) + protected virtual void AdjustNamedParameterLocationsForQueryParameters(QueryParameters parameters) { - if (namedParams != null) - { - // assumes that types are all of span 1 - int result = 0; - foreach (KeyValuePair<string, TypedValue> namedParam in namedParams) - { - string name = namedParam.Key; - TypedValue typedval = namedParam.Value; - int[] locs = GetNamedParameterLocs(name); - for (int i = 0; i < locs.Length; i++) - { - if (log.IsDebugEnabled) - { - log.Debug("BindNamedParameters() " + typedval.Value + " -> " + name + " [" + (locs[i] + start) + "]"); - } - typedval.Type.NullSafeSet(st, typedval.Value, locs[i] + start, session); - } - result += locs.Length; - } - return result; - } - else - { - return 0; - } + // if you support named parameter locations (by overriding GetNamedParameterLocs), then you might need to + // allow for the locations to be adjusted by introduced filtered parameters by overriding + // this method too. + if ((parameters.NamedParameters != null) && (parameters.NamedParameters.Keys.Count > 0)) + throw new AssertionFailure(GetType() + " must override to handle implementation of named parameter locations"); } - public virtual int[] GetNamedParameterLocs(string name) - { - throw new AssertionFailure("no named parameters"); - } - /// <summary> /// Fetch a <c>IDbCommand</c>, call <c>SetMaxRows</c> and then execute it, /// advance to the first result and return an SQL <c>IDataReader</c> @@ -1784,16 +1732,24 @@ List<IType> paramTypeList = new List<IType>(); int span = 0; - foreach (IType type in parameters.FilteredPositionalParameterTypes) + for (int index = 0; index < parameters.PositionalParameterTypes.Length; index++) { - paramTypeList.Add(type); + int location = parameters.PositionalParameterLocations[index]; + IType type = parameters.PositionalParameterTypes[index]; + ArrayHelper.SafeSetValue(paramTypeList, location, type); span += type.GetColumnSpan(Factory); } - if (parameters.NamedParameters != null && parameters.NamedParameters.Count > 0) + for (int index = 0; index < parameters.FilteredParameterTypes.Count; index++) { - int offset = paramTypeList.Count; + int location = parameters.FilteredParameterLocations[index]; + IType type = parameters.FilteredParameterTypes[index]; + ArrayHelper.SafeSetValue(paramTypeList, location, type); + span += type.GetColumnSpan(Factory); + } + if (parameters.NamedParameters != null && parameters.NamedParameters.Count > 0) + { // convert the named parameters to an array of types foreach (KeyValuePair<string, TypedValue> namedParameter in parameters.NamedParameters) { @@ -1804,7 +1760,14 @@ for (int i = 0; i < locs.Length; i++) { - ArrayHelper.SafeSetValue(paramTypeList, locs[i] + offset, typedval.Type); + int location = locs[i]; + + // can still clash with positional parameters + // could consider throwing an exception to locate problem (NH-1098) + while ((location < paramTypeList.Count) && (paramTypeList[location] != null)) + location++; + + ArrayHelper.SafeSetValue(paramTypeList, location, typedval.Type); } } } Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1098/FilterParameterOrderFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1098/FilterParameterOrderFixture.cs 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1098/FilterParameterOrderFixture.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -121,7 +121,7 @@ Assert.AreEqual( 1, result.Count ); } - [Test, Ignore( "Known issue, parameter order is wrong when named and positional parameters are mixed" )] + [Test] public void QueryWithNamedParameters() { ISession session = OpenSession(); Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Fixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Fixture.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Fixture.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -0,0 +1,76 @@ +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace NHibernate.Test.NHSpecificTest.NH1693 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnTearDown() + { + using (var session = OpenSession()) + { + session.Delete("from Invoice"); + session.Flush(); + } + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var tx = session.BeginTransaction()) + { + session.Save(new Invoice { Mode = "a", Num = 1, Category = 10 }); + session.Save(new Invoice { Mode = "a", Num = 2, Category = 10 }); + session.Save(new Invoice { Mode = "a", Num = 3, Category = 20 }); + session.Save(new Invoice { Mode = "a", Num = 4, Category = 10 }); + session.Save(new Invoice { Mode = "b", Num = 2, Category = 10 }); + session.Save(new Invoice { Mode = "b", Num = 3, Category = 10 }); + session.Save(new Invoice { Mode = "b", Num = 5, Category = 10 }); + + tx.Commit(); + } + } + + [Test] + public void without_filter() + { + using (var session = OpenSession()) + using (var tx = session.BeginTransaction()) + { + var q1 = + "from Invoice i where i.Mode='a' and i.Category=:cat and not exists (from Invoice i2 where i2.Mode='a' and i2.Category=:cat and i2.Num=i.Num+1)"; + var list = session.CreateQuery(q1) + .SetParameter("cat", 10) + .List<Invoice>(); + Assert.That(list, Has.Count(2)); + Assert.That(list[0].Num == 2 && list[0].Mode == "a"); + Assert.That(list[1].Num == 4 && list[1].Mode == "a"); + + tx.Commit(); + } + } + + [Test] + public void with_filter() + { + using (var session = OpenSession()) + using (var tx = session.BeginTransaction()) + { + session.EnableFilter("modeFilter").SetParameter("currentMode", "a"); + + var q1 = + "from Invoice i where i.Category=:cat and not exists (from Invoice i2 where i2.Category=:cat and i2.Num=i.Num+1)"; + var list = session.CreateQuery(q1) + .SetParameter("cat", 10) + .List<Invoice>(); + Assert.That(list, Has.Count(2)); + Assert.That(list[0].Num == 2 && list[0].Mode == "a"); + Assert.That(list[1].Num == 4 && list[1].Mode == "a"); + + tx.Commit(); + } + } + + } +} Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Mappings.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Mappings.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Mappings.hbm.xml 2009-03-28 19:07:03 UTC (rev 4158) @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" ?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + assembly="NHibernate.Test" + namespace="NHibernate.Test.NHSpecificTest.NH1693"> + + <class name="Invoice"> + <id name="ID" type="Int32"> + <generator class="hilo" /> + </id> + <property name="Mode" type="String" /> + <property name="Num" type="Int32" /> + <property name="Category" type="Int32" /> + + <filter name="modeFilter" condition="Mode=:currentMode" /> + </class> + + <filter-def name="modeFilter"> + <filter-param name="currentMode" type="String"/> + </filter-def> + +</hibernate-mapping> Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Model.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Model.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1693/Model.cs 2009-03-28 19:07:03 UTC (rev 4158) @@ -0,0 +1,10 @@ +namespace NHibernate.Test.NHSpecificTest.NH1693 +{ + public class Invoice + { + public virtual int ID { get; private set; } + public virtual string Mode { get; set; } + public virtual int Category { get; set; } + public virtual int Num { get; set; } + } +} Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2009-03-28 16:35:33 UTC (rev 4157) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2009-03-28 19:07:03 UTC (rev 4158) @@ -294,6 +294,8 @@ <Compile Include="HQL\BaseFunctionFixture.cs" /> <Compile Include="NHSpecificTest\DtcFailures\DtcFailuresFixture.cs" /> <Compile Include="NHSpecificTest\DtcFailures\Person.cs" /> + <Compile Include="NHSpecificTest\NH1693\Fixture.cs" /> + <Compile Include="NHSpecificTest\NH1693\Model.cs" /> <Compile Include="NHSpecificTest\NH1694\Fixture.cs" /> <Compile Include="NHSpecificTest\NH1706\Domain.cs" /> <Compile Include="NHSpecificTest\NH1706\KeyPropertyRefFixture.cs" /> @@ -1696,6 +1698,7 @@ <EmbeddedResource Include="Cascade\JobBatch.hbm.xml" /> <EmbeddedResource Include="Deletetransient\Person.hbm.xml" /> <Content Include="DynamicEntity\package.html" /> + <EmbeddedResource Include="NHSpecificTest\NH1693\Mappings.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1710\WithColumnNode.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1710\InLine.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1710\Heuristic.hbm.xml" /> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |