|
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] |