From: <fab...@us...> - 2011-06-12 22:04:51
|
Revision: 5915 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5915&view=rev Author: fabiomaulo Date: 2011-06-12 22:04:44 +0000 (Sun, 12 Jun 2011) Log Message: ----------- - "re-fix" multi-criteria with pagination - re implemented MultiCriteriaImpl/MultiQueryImpl to fix parameters related problems Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Driver/BasicResultSetsCommand.cs trunk/nhibernate/src/NHibernate/Driver/IResultSetsCommand.cs trunk/nhibernate/src/NHibernate/Engine/Query/ParameterParser.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs trunk/nhibernate/src/NHibernate/Impl/MultiCriteriaImpl.cs trunk/nhibernate/src/NHibernate/Impl/MultiQueryImpl.cs trunk/nhibernate/src/NHibernate/Loader/Criteria/CriteriaLoader.cs trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs trunk/nhibernate/src/NHibernate/Loader/Loader.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/CriteriaNamedParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/DynamicFilterParameterSpecification.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/QuerySkipParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/QueryTakeParameterSpecification.cs trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs trunk/nhibernate/src/NHibernate.Test/Pagination/CustomDialectFixture.cs Added Paths: ----------- trunk/nhibernate/src/NHibernate/SqlCommand/SqlCommandImpl.cs Modified: trunk/nhibernate/src/NHibernate/Driver/BasicResultSetsCommand.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Driver/BasicResultSetsCommand.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Driver/BasicResultSetsCommand.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Linq; using NHibernate.Engine; using NHibernate.SqlCommand; using NHibernate.SqlTypes; @@ -14,9 +15,8 @@ private readonly ISessionImplementor session; private readonly Dialect.Dialect dialect; private readonly IBatcher batcher; - private int resultSetsCount = 0; - private readonly List<SqlType> types = new List<SqlType>(); private SqlString sqlString = new SqlString(); + private readonly List<ISqlCommand> commands= new List<ISqlCommand>(); public BasicResultSetsCommand(ISessionImplementor session) { @@ -25,21 +25,15 @@ batcher = session.Batcher; } - public virtual void Append(SqlCommandInfo commandInfo) + public void Append(ISqlCommand command) { - resultSetsCount++; - sqlString = sqlString.Append(commandInfo.Text).Append(";").Append(Environment.NewLine); - types.AddRange(commandInfo.ParameterTypes); + commands.Add(command); + sqlString = sqlString.Append(command.Query).Append(";").Append(Environment.NewLine); } - public int ParametersCount - { - get { return types.Count; } - } - public bool HasQueries { - get { return resultSetsCount > 0; } + get { return commands.Count > 0; } } public SqlString Sql @@ -47,17 +41,22 @@ get { return sqlString; } } - public virtual IDataReader GetReader(Loader.Loader[] queryLoaders, QueryParameters[] queryParameters, int? commandTimeout) + public IDataReader GetReader(int? commandTimeout) { - SqlType[] sqlTypes = types.ToArray(); - var command= batcher.PrepareQueryCommand(CommandType.Text, sqlString, sqlTypes); - if(commandTimeout.HasValue) + SqlType[] sqlTypes = commands.SelectMany(c => c.ParameterTypes).ToArray(); + var command = batcher.PrepareQueryCommand(CommandType.Text, sqlString, sqlTypes); + if (commandTimeout.HasValue) { - command.CommandTimeout = commandTimeout.Value; + command.CommandTimeout = commandTimeout.Value; } log.Info(command.CommandText); - - BindParameters(command, queryLoaders, queryParameters); + var wholeQueryParametersList = sqlString.GetParameters().ToList(); + var singleQueryParameterOffset = 0; + foreach (var sqlLoaderCommand in commands) + { + sqlLoaderCommand.Bind(command, wholeQueryParametersList, singleQueryParameterOffset, session); + singleQueryParameterOffset += sqlLoaderCommand.ParameterTypes.Length; + } return new BatcherDataReaderWrapper(batcher, command); } @@ -65,7 +64,7 @@ { int colIndex = 0; - for (int queryIndex = 0; queryIndex < resultSetsCount; queryIndex++) + for (int queryIndex = 0; queryIndex < commands.Count; queryIndex++) { int limitParameterSpan = BindLimitParametersFirstIfNeccesary(command, queryLoaders[queryIndex], queryParameters[queryIndex], colIndex); colIndex = BindQueryParameters(command, queryLoaders[queryIndex], queryParameters[queryIndex], colIndex + limitParameterSpan); Modified: trunk/nhibernate/src/NHibernate/Driver/IResultSetsCommand.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Driver/IResultSetsCommand.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Driver/IResultSetsCommand.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -1,15 +1,13 @@ using System.Data; -using NHibernate.Engine; using NHibernate.SqlCommand; namespace NHibernate.Driver { public interface IResultSetsCommand { - void Append(SqlCommandInfo commandInfo); - int ParametersCount { get; } + void Append(ISqlCommand command); bool HasQueries { get; } SqlString Sql { get; } - IDataReader GetReader(Loader.Loader[] queryLoaders, QueryParameters[] queryParameters, int? commandTimeout); + IDataReader GetReader(int? commandTimeout); } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Engine/Query/ParameterParser.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Query/ParameterParser.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Engine/Query/ParameterParser.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -43,6 +43,7 @@ /// <exception cref="QueryException" /> public static void Parse(string sqlString, IRecognizer recognizer) { + // TODO: WTF? "CALL"... it may work for ORACLE but what about others RDBMS ? (by FM) bool hasMainOutputParameter = sqlString.IndexOf("call") > 0 && sqlString.IndexOf("?") > 0 && sqlString.IndexOf("=") > 0 && Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -436,6 +436,29 @@ return result; } + public override ISqlCommand CreateSqlCommandInfo(QueryParameters queryParameters, ISessionImplementor session) + { + // NOTE: repeated code PrepareQueryCommand + // 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 + + return new SqlCommand.SqlCommandImpl(sqlString, parameterSpecs, queryParameters, session.Factory); + } + /// <summary> /// Obtain an <c>IDbCommand</c> with all parameters pre-bound. Bind positional parameters, /// named parameters, and limit parameters. Modified: trunk/nhibernate/src/NHibernate/Impl/MultiCriteriaImpl.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Impl/MultiCriteriaImpl.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Impl/MultiCriteriaImpl.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -183,8 +183,8 @@ translators.Add(translator); QueryParameters queryParameters = translator.GetQueryParameters(); parameters.Add(queryParameters); - SqlCommandInfo commandInfo = loader.GetQueryStringAndTypes(session, queryParameters, resultSetsCommand.ParametersCount); - resultSetsCommand.Append(commandInfo); + ISqlCommand singleCommand = loader.CreateSqlCommandInfo(queryParameters, session); + resultSetsCommand.Append(singleCommand); } } @@ -200,7 +200,7 @@ try { - using (var reader = resultSetsCommand.GetReader(loaders.ToArray(), parameters.ToArray(), null)) + using (var reader = resultSetsCommand.GetReader(null)) { ArrayList[] hydratedObjects = new ArrayList[loaders.Count]; List<EntityKey[]>[] subselectResultKeys = new List<EntityKey[]>[loaders.Count]; Modified: trunk/nhibernate/src/NHibernate/Impl/MultiQueryImpl.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Impl/MultiQueryImpl.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Impl/MultiQueryImpl.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -504,7 +504,7 @@ try { - using (var reader = resultSetsCommand.GetReader(translators.Select(t=> t.Loader).ToArray(), Parameters.ToArray(), commandTimeout != RowSelection.NoValue ? commandTimeout : (int?)null)) + using (var reader = resultSetsCommand.GetReader(commandTimeout != RowSelection.NoValue ? commandTimeout : (int?)null)) { if (log.IsDebugEnabled) { @@ -640,9 +640,8 @@ { translators.Add(translator); parameters.Add(queryParameters); - queryParameters = GetFilteredQueryParameters(queryParameters, translator); - SqlCommandInfo commandInfo = translator.Loader.GetQueryStringAndTypes(session, queryParameters, resultSetsCommand.ParametersCount); - resultSetsCommand.Append(commandInfo); + ISqlCommand singleCommand = translator.Loader.CreateSqlCommandInfo(queryParameters, session); + resultSetsCommand.Append(singleCommand); } } } @@ -866,7 +865,7 @@ var sqlQueryImpl = (SqlQueryImpl) sqlQuery; NativeSQLQuerySpecification sqlQuerySpec = sqlQueryImpl.GenerateQuerySpecification(sqlQueryImpl.NamedParams); var sqlCustomQuery = new SQLCustomQuery(sqlQuerySpec.SqlQueryReturns, sqlQuerySpec.QueryString, sqlQuerySpec.QuerySpaces, sessionFactory); - loader = new CustomLoader(sqlCustomQuery, sessionFactory); + loader = new CustomLoader(sqlCustomQuery, sqlCustomQuery.CollectedParametersSpecifications, sessionFactory); } public IType[] ReturnTypes Modified: trunk/nhibernate/src/NHibernate/Loader/Criteria/CriteriaLoader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Criteria/CriteriaLoader.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Loader/Criteria/CriteriaLoader.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -185,6 +185,26 @@ return customResultTransformer.TransformList(results); } + public override ISqlCommand CreateSqlCommandInfo(QueryParameters queryParameters, ISessionImplementor session) + { + // NOTE: repeated code PrepareQueryCommand + // A distinct-copy of parameter specifications collected during query construction + var parameterSpecs = new HashSet<IParameterSpecification>(translator.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); + + return new SqlCommand.SqlCommandImpl(sqlString, parameterSpecs, queryParameters, session.Factory); + } + /// <summary> /// Obtain an <c>IDbCommand</c> with all parameters pre-bound. Bind positional parameters, /// named parameters, and limit parameters. @@ -199,9 +219,7 @@ /// <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 Loader 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. - + // NOTE: repeated code CreateSqlCommandInfo (here we are reusing some other variables) // A distinct-copy of parameter specifications collected during query construction var parameterSpecs = new HashSet<IParameterSpecification>(translator.CollectedParameterSpecifications); SqlString sqlString = SqlString.Copy(); Modified: trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -1,3 +1,5 @@ +using System; +using System.Linq; using System.Collections; using System.Collections.Generic; using System.Data; @@ -4,12 +6,14 @@ using Iesi.Collections.Generic; using NHibernate.Engine; using NHibernate.Hql; +using NHibernate.Param; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Transform; using NHibernate.Type; using NHibernate.Util; +using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Loader.Custom { @@ -23,6 +27,7 @@ private readonly SqlString sql; private readonly ISet<string> querySpaces = new HashedSet<string>(); private readonly IDictionary<string, object> namedParameterBindPoints; + private List<IParameterSpecification> parametersSpecifications; private readonly IQueryable[] entityPersisters; private readonly int[] entityOwners; @@ -38,6 +43,12 @@ private IType[] resultTypes; private string[] transformerAliases; + public CustomLoader(ICustomQuery customQuery, IEnumerable<IParameterSpecification> parametersSpecifications, ISessionFactoryImplementor factory) + : this(customQuery, factory) + { + this.parametersSpecifications = parametersSpecifications.ToList(); + } + public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory) : base(factory) { sql = customQuery.SQL; @@ -337,6 +348,92 @@ transformerAliases = aliases.ToArray(); } + public override ISqlCommand CreateSqlCommandInfo(QueryParameters queryParameters, ISessionImplementor session) + { + if(parametersSpecifications == null) + { + throw new InvalidOperationException("The custom SQL loader was not initialized with Parameters Specifications."); + } + // A distinct-copy of parameter specifications collected during query construction + var parameterSpecs = new HashSet<IParameterSpecification>(parametersSpecifications); + 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 + + return new SqlCommand.SqlCommandImpl(sqlString, parameterSpecs, queryParameters, session.Factory); + } + + 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); + } + } + + public IType[] ResultTypes + { + get { return resultTypes; } + } + + public string[] ReturnAliases + { + get { return transformerAliases; } + } + + public IEnumerable<string> NamedParameters + { + get { return namedParameterBindPoints.Keys; } + } + public class ResultRowProcessor { private readonly bool hasScalars; @@ -402,21 +499,6 @@ } } - public IType[] ResultTypes - { - get { return resultTypes; } - } - - public string[] ReturnAliases - { - get { return transformerAliases; } - } - - public IEnumerable<string> NamedParameters - { - get { return namedParameterBindPoints.Keys; } - } - public interface IResultColumnProcessor { object Extract(object[] data, IDataReader resultSet, ISessionImplementor session); Modified: trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -1,8 +1,10 @@ using System.Collections.Generic; +using System.Linq; using Iesi.Collections.Generic; using NHibernate.Engine; using NHibernate.Engine.Query.Sql; +using NHibernate.Param; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; @@ -20,6 +22,7 @@ private readonly Dictionary<string, object> namedParameterBindPoints = new Dictionary<string, object>(); private readonly ISet<string> querySpaces = new HashedSet<string>(); private readonly SqlString sql; + private List<IParameterSpecification> parametersSpecifications; public SQLCustomQuery(INativeSQLQueryReturn[] queryReturns, string sqlQuery, ICollection<string> additionalQuerySpaces, ISessionFactoryImplementor factory) @@ -28,10 +31,11 @@ SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(queryReturns, factory); SQLQueryReturnProcessor.ResultAliasContext aliasContext = processor.Process(); - SQLQueryParser parser = new SQLQueryParser(sqlQuery, new ParserContext(aliasContext)); + SQLQueryParser parser = new SQLQueryParser(factory, sqlQuery, new ParserContext(aliasContext)); sql = parser.Process(); ArrayHelper.AddAll(namedParameterBindPoints, parser.NamedParameters); ArrayHelper.AddAll(customQueryReturns, processor.GenerateCustomReturns(parser.QueryHasAliases)); + parametersSpecifications = parser.CollectedParametersSpecifications.ToList(); if (additionalQuerySpaces != null) { @@ -39,6 +43,11 @@ } } + public IEnumerable<IParameterSpecification> CollectedParametersSpecifications + { + get { return parametersSpecifications; } + } + #region ICustomQuery Members public SqlString SQL Modified: trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -1,7 +1,10 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text; +using NHibernate.Engine; using NHibernate.Engine.Query; +using NHibernate.Param; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; @@ -21,15 +24,18 @@ IDictionary<string, string[]> GetPropertyResultsMapByAlias(string alias); } + private readonly ISessionFactoryImplementor factory; private readonly string originalQueryString; private readonly IParserContext context; private readonly Dictionary<string, object> namedParameters = new Dictionary<string, object>(); private long aliasesFound; + private IEnumerable<IParameterSpecification> parametersSpecifications; - public SQLQueryParser(string sqlQuery, IParserContext context) + public SQLQueryParser(ISessionFactoryImplementor factory, string sqlQuery, IParserContext context) { + this.factory = factory; originalQueryString = sqlQuery; this.context = context; } @@ -49,6 +55,11 @@ return SubstituteParams(SubstituteBrackets()); } + public IEnumerable<IParameterSpecification> CollectedParametersSpecifications + { + get { return parametersSpecifications; } + } + // TODO: should "record" how many properties we have reffered to - and if we // don't get'em'all we throw an exception! Way better than trial and error ;) private string SubstituteBrackets() @@ -239,9 +250,9 @@ /// <returns> The SQL query with parameter substitution complete. </returns> private SqlString SubstituteParams(string sqlString) { - ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer(); + var recognizer = new ParameterSubstitutionRecognizer(factory); ParameterParser.Parse(sqlString, recognizer); - + parametersSpecifications = recognizer.ParametersSpecifications.ToList(); namedParameters.Clear(); foreach (KeyValuePair<string, object> de in recognizer.namedParameterBindPoints) { @@ -253,24 +264,49 @@ public class ParameterSubstitutionRecognizer : ParameterParser.IRecognizer { + private readonly ISessionFactoryImplementor factory; internal SqlStringBuilder result = new SqlStringBuilder(); internal Dictionary<string, object> namedParameterBindPoints = new Dictionary<string, object>(); internal int parameterCount = 0; + private readonly List<IParameterSpecification> parametersSpecifications = new List<IParameterSpecification>(); + private int positionalParameterCount; + public ParameterSubstitutionRecognizer(ISessionFactoryImplementor factory) + { + this.factory = factory; + } + + public IEnumerable<IParameterSpecification> ParametersSpecifications + { + get { return parametersSpecifications; } + } + public void OutParameter(int position) { - result.Add(Parameter.Placeholder); + var paramSpec = new PositionalParameterSpecification(1, position, positionalParameterCount++); + var parameter = Parameter.Placeholder; + parameter.BackTrack = paramSpec.GetIdsForBackTrack(factory).First(); + parametersSpecifications.Add(paramSpec); + result.Add(parameter); } public void OrdinalParameter(int position) { - result.Add(Parameter.Placeholder); + var paramSpec = new PositionalParameterSpecification(1, position, positionalParameterCount++); + var parameter = Parameter.Placeholder; + parameter.BackTrack = paramSpec.GetIdsForBackTrack(factory).First(); + parametersSpecifications.Add(paramSpec); + result.Add(parameter); } public void NamedParameter(string name, int position) { AddNamedParameter(name); - result.Add(Parameter.Placeholder); + var paramSpec = new NamedParameterSpecification(1, position, name); + var parameter = Parameter.Placeholder; + parameter.BackTrack = paramSpec.GetIdsForBackTrack(factory).First(); + parametersSpecifications.Add(paramSpec); + result.Add(parameter); } public void JpaPositionalParameter(string name, int position) Modified: trunk/nhibernate/src/NHibernate/Loader/Loader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -1785,6 +1785,11 @@ return new SqlCommandInfo(sqlString, sqlTypes); } + public virtual ISqlCommand CreateSqlCommandInfo(QueryParameters queryParameters, ISessionImplementor session) + { + throw new NotSupportedException("This loader does not support extraction of single command."); + } + #endregion } } Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2011-06-12 22:04:44 UTC (rev 5915) @@ -569,6 +569,7 @@ <Compile Include="SqlCommand\SqlBaseBuilder.cs" /> <Compile Include="SqlCommand\SqlDeleteBuilder.cs" /> <Compile Include="SqlCommand\SqlInsertBuilder.cs" /> + <Compile Include="SqlCommand\SqlCommandImpl.cs" /> <Compile Include="SqlCommand\SqlSelectBuilder.cs" /> <Compile Include="SqlCommand\SqlSimpleSelectBuilder.cs" /> <Compile Include="SqlCommand\SqlString.cs" /> Modified: trunk/nhibernate/src/NHibernate/Param/AbstractExplicitParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/AbstractExplicitParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/AbstractExplicitParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -40,6 +40,7 @@ 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 Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session); public abstract void SetEffectiveType(QueryParameters queryParameters); #endregion Modified: trunk/nhibernate/src/NHibernate/Param/AggregatedIndexCollectionSelectorParameterSpecifications.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/AggregatedIndexCollectionSelectorParameterSpecifications.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/AggregatedIndexCollectionSelectorParameterSpecifications.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -32,6 +32,11 @@ throw new NotImplementedException(); } + public void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { + throw new NotImplementedException(); + } + public IType ExpectedType { get { return null; } Modified: trunk/nhibernate/src/NHibernate/Param/CollectionFilterKeyParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/CollectionFilterKeyParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/CollectionFilterKeyParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -31,6 +31,16 @@ #region IParameterSpecification Members + public void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, 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 + singleSqlParametersOffset, session); + } + public IType ExpectedType { get { return keyType; } @@ -53,12 +63,7 @@ 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); + Bind(command, sqlQueryParametersList, 0, sqlQueryParametersList, queryParameters, session); } #endregion Modified: trunk/nhibernate/src/NHibernate/Param/CriteriaNamedParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/CriteriaNamedParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/CriteriaNamedParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -23,11 +23,16 @@ public void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) { + Bind(command, sqlQueryParametersList, 0, sqlQueryParametersList, queryParameters, session); + } + + public void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { TypedValue typedValue = queryParameters.NamedParameters[name]; string backTrackId = GetIdsForBackTrack(session.Factory).First(); foreach (int position in sqlQueryParametersList.GetEffectiveParameterLocations(backTrackId)) { - ExpectedType.NullSafeSet(command, typedValue.Value, position, session); + ExpectedType.NullSafeSet(command, typedValue.Value, position + singleSqlParametersOffset, session); } } Modified: trunk/nhibernate/src/NHibernate/Param/DynamicFilterParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/DynamicFilterParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/DynamicFilterParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -37,11 +37,16 @@ public void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) { + Bind(command, sqlQueryParametersList, 0, sqlQueryParametersList, queryParameters, session); + } + + public void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { string backTrackId = GetIdsForBackTrack(session.Factory).First(); // just the first because IType suppose the oders in certain sequence - // The same filterName-parameterName can appear more than once + // The same filterName-parameterName can appear more than once in the whole query object value = session.GetFilterParameterValue(filterParameterFullName); - foreach (int position in sqlQueryParametersList.GetEffectiveParameterLocations(backTrackId)) + foreach (int position in multiSqlQueryParametersList.GetEffectiveParameterLocations(backTrackId)) { ExpectedType.NullSafeSet(command, value, position, session); } Modified: trunk/nhibernate/src/NHibernate/Param/IParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/IParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/IParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -21,6 +21,22 @@ void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session); /// <summary> + /// Bind the appropriate value into the given command. + /// </summary> + /// <param name="command">The command into which the value should be bound.</param> + /// <param name="multiSqlQueryParametersList">The parameter-list of the whole query of the command.</param> + /// <param name="singleSqlParametersOffset">The offset from where start the list of <see cref="IDataParameter"/> in the given <paramref name="command"/> for the query where this <see cref="IParameterSpecification"/> was used. </param> + /// <param name="sqlQueryParametersList">The list of Sql query parameter in the exact sequence they are present in the query where this <see cref="IParameterSpecification"/> was used.</param> + /// <param name="queryParameters">The defined values for the query where this <see cref="IParameterSpecification"/> was used.</param> + /// <param name="session">The session against which the current execution is occuring.</param> + /// <remarks> + /// Suppose the <paramref name="command"/> is composed by two queries. The <paramref name="singleSqlParametersOffset"/> for the first query is zero. + /// If the first query in <paramref name="command"/> has 12 parameters (size of its SqlType array) the offset to bind all <see cref="IParameterSpecification"/>s of the second query in the + /// <paramref name="command"/> is 12 (for the first query we are using from 0 to 11). + /// </remarks> + void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session); + + /// <summary> /// Get or set the type which we are expeting for a bind into this parameter based /// on translated contextual information. /// </summary> Modified: trunk/nhibernate/src/NHibernate/Param/NamedParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/NamedParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/NamedParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -52,11 +52,16 @@ public override void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) { + Bind(command, sqlQueryParametersList, 0, sqlQueryParametersList, queryParameters, session); + } + + public override void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { TypedValue typedValue = queryParameters.NamedParameters[name]; string backTrackId = GetIdsForBackTrack(session.Factory).First(); // just the first because IType suppose the oders in certain sequence foreach (int position in sqlQueryParametersList.GetEffectiveParameterLocations(backTrackId)) { - ExpectedType.NullSafeSet(command, typedValue.Value, position, session); + ExpectedType.NullSafeSet(command, typedValue.Value, position + singleSqlParametersOffset, session); } } Modified: trunk/nhibernate/src/NHibernate/Param/PositionalParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/PositionalParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/PositionalParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -52,6 +52,11 @@ public override void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) { + Bind(command, sqlQueryParametersList, 0, sqlQueryParametersList, queryParameters, session); + } + + public override void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { IType type = ExpectedType; object value = queryParameters.PositionalParameterValues[hqlPosition]; @@ -59,7 +64,7 @@ // an HQL positional parameter can appear more than once because a custom HQL-Function can duplicate it foreach (int position in sqlQueryParametersList.GetEffectiveParameterLocations(backTrackId)) { - type.NullSafeSet(command, value, position, session); + type.NullSafeSet(command, value, position + singleSqlParametersOffset, session); } } Modified: trunk/nhibernate/src/NHibernate/Param/QuerySkipParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/QuerySkipParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/QuerySkipParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -27,7 +27,13 @@ public void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) { - var effectiveParameterLocations = sqlQueryParametersList.GetEffectiveParameterLocations(limitParametersNameForThisQuery).ToArray(); + Bind(command, sqlQueryParametersList, 0, sqlQueryParametersList, queryParameters, session); + } + + public void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { + // The QuerySkipParameterSpecification is unique so we can use multiSqlQueryParametersList + var effectiveParameterLocations = multiSqlQueryParametersList.GetEffectiveParameterLocations(limitParametersNameForThisQuery).ToArray(); if (effectiveParameterLocations.Length > 0) { // if the dialect does not support variable limits the parameter may was removed Modified: trunk/nhibernate/src/NHibernate/Param/QueryTakeParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/QueryTakeParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/QueryTakeParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -27,7 +27,13 @@ public void Bind(IDbCommand command, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) { - var effectiveParameterLocations = sqlQueryParametersList.GetEffectiveParameterLocations(limitParametersNameForThisQuery).ToArray(); + Bind(command, sqlQueryParametersList, 0, sqlQueryParametersList, queryParameters, session); + } + + public void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { + // The QueryTakeParameterSpecification is unique so we can use multiSqlQueryParametersList + var effectiveParameterLocations = multiSqlQueryParametersList.GetEffectiveParameterLocations(limitParametersNameForThisQuery).ToArray(); if (effectiveParameterLocations.Any()) { // if the dialect does not support variable limits the parameter may was removed Modified: trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -26,6 +27,11 @@ type.NullSafeSet(command, type.Seed(session), position, session); } + public void Bind(IDbCommand command, IList<Parameter> multiSqlQueryParametersList, int singleSqlParametersOffset, IList<Parameter> sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session) + { + throw new NotSupportedException("Not supported for multiquery loader."); + } + public IType ExpectedType { get { return type; } Added: trunk/nhibernate/src/NHibernate/SqlCommand/SqlCommandImpl.cs =================================================================== --- trunk/nhibernate/src/NHibernate/SqlCommand/SqlCommandImpl.cs (rev 0) +++ trunk/nhibernate/src/NHibernate/SqlCommand/SqlCommandImpl.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Data; +using System.Linq; +using NHibernate.Engine; +using NHibernate.Param; +using NHibernate.SqlTypes; + +namespace NHibernate.SqlCommand +{ + public interface ISqlCommand + { + SqlType[] ParameterTypes { get; } + SqlString Query { get; } + QueryParameters QueryParameters { get; } + + /// <summary> + /// Bind the appropriate value into the given command. + /// </summary> + /// <param name="command">The command into which the value should be bound.</param> + /// <param name="commandQueryParametersList">The parameter-list of the whole query of the command.</param> + /// <param name="singleSqlParametersOffset">The offset from where start the list of <see cref="IDataParameter"/>, in the given <paramref name="command"/>, for the this <see cref="SqlCommandImpl"/>. </param> + /// <param name="session">The session against which the current execution is occuring.</param> + void Bind(IDbCommand command, IList<Parameter> commandQueryParametersList, int singleSqlParametersOffset, ISessionImplementor session); + } + + public class SqlCommandImpl : ISqlCommand + { + private readonly SqlString query; + private readonly ICollection<IParameterSpecification> specifications; + private readonly QueryParameters queryParameters; + private readonly ISessionFactoryImplementor factory; + private SqlType[] parameterTypes; + List<Parameter> sqlQueryParametersList; + + public SqlCommandImpl(SqlString query, ICollection<IParameterSpecification> specifications, QueryParameters queryParameters, ISessionFactoryImplementor factory) + { + this.query = query; + this.specifications = specifications; + this.queryParameters = queryParameters; + this.factory = factory; + } + + private List<Parameter> SqlQueryParametersList + { + get { return sqlQueryParametersList ?? (sqlQueryParametersList = query.GetParameters().ToList()); } + } + + public SqlType[] ParameterTypes + { + get { return parameterTypes ?? (parameterTypes = specifications.GetQueryParameterTypes(SqlQueryParametersList, factory)); } + } + + public SqlString Query + { + get { return query; } + } + + public IEnumerable<IParameterSpecification> Specifications + { + get { return specifications; } + } + + public QueryParameters QueryParameters + { + get { return queryParameters; } + } + + /// <summary> + /// Bind the appropriate value into the given command. + /// </summary> + /// <param name="command">The command into which the value should be bound.</param> + /// <param name="commandQueryParametersList">The parameter-list of the whole query of the command.</param> + /// <param name="singleSqlParametersOffset">The offset from where start the list of <see cref="IDataParameter"/>, in the given <paramref name="command"/>, for the this <see cref="SqlCommandImpl"/>. </param> + /// <param name="session">The session against which the current execution is occuring.</param> + public void Bind(IDbCommand command, IList<Parameter> commandQueryParametersList, int singleSqlParametersOffset, ISessionImplementor session) + { + foreach (IParameterSpecification parameterSpecification in Specifications) + { + parameterSpecification.Bind(command, commandQueryParametersList, singleSqlParametersOffset, SqlQueryParametersList, QueryParameters, session); + } + } + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/Pagination/CustomDialectFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Pagination/CustomDialectFixture.cs 2011-06-07 01:47:49 UTC (rev 5914) +++ trunk/nhibernate/src/NHibernate.Test/Pagination/CustomDialectFixture.cs 2011-06-12 22:04:44 UTC (rev 5915) @@ -91,7 +91,7 @@ } } - [Test, Ignore("To be fixed")] + [Test] public void LimitFirstMultiCriteria() { using (ISession s = OpenSession()) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |