From aec3a300ac870bf091ea43878af294fa8d386d2a Mon Sep 17 00:00:00 2001 From: ITMAGINATION Date: Tue, 21 Oct 2014 02:15:50 +1300 Subject: [PATCH 1/2] NH-3727 - Add test case --- .../NHSpecificTest/NH3727/Entity.cs | 7 ++ .../NHSpecificTest/NH3727/FixtureByCode.cs | 83 +++++++++++++++++++ src/NHibernate.Test/NHibernate.Test.csproj | 2 + 3 files changed, 92 insertions(+) create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3727/Entity.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3727/FixtureByCode.cs diff --git a/src/NHibernate.Test/NHSpecificTest/NH3727/Entity.cs b/src/NHibernate.Test/NHSpecificTest/NH3727/Entity.cs new file mode 100644 index 00000000000..af7cf52808b --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3727/Entity.cs @@ -0,0 +1,7 @@ +namespace NHibernate.Test.NHSpecificTest.NH3727 +{ + class Entity + { + public virtual int Id { get; set; } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH3727/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/NH3727/FixtureByCode.cs new file mode 100644 index 00000000000..775208ccf5d --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3727/FixtureByCode.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Criterion; +using NHibernate.Mapping.ByCode; +using NHibernate.Transform; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH3727 +{ + public class ByCodeFixture : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class(rc => + { + rc.Id(x => x.Id); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + [Test] + public void QueryOverWithSubqueryProjectionCanBeExecutedMoreThanOnce() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + const int parameter1 = 111; + + var countSubquery = QueryOver.Of() + .Where(x => x.Id == parameter1) //any condition which makes output SQL has parameter + .Select(Projections.RowCount()) + ; + + var originalQueryOver = session.QueryOver() + .SelectList(l => l + .Select(x => x.Id) + .SelectSubQuery(countSubquery) + ) + .TransformUsing(Transformers.ToList); + + var objects = originalQueryOver.List(); + + Assert.DoesNotThrow(() => originalQueryOver.List(), "Second try to execute QueryOver thrown exception."); + } + } + + [Test] + public void ClonedQueryOverExecutionMakesOriginalQueryOverNotWorking() + { + // Projections are copied by clone operation. + // SubqueryProjection use SubqueryExpression which holds CriteriaQueryTranslator (class SubqueryExpression { private CriteriaQueryTranslator innerQuery; }) + // So given CriteriaQueryTranslator is used twice. + // Since CriteriaQueryTranslator has CollectedParameters collection, second execution of the Criteria does not fit SqlCommand parameters. + + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + const int parameter1 = 111; + + var countSubquery = QueryOver.Of() + .Where(x => x.Id == parameter1) //any condition which makes output SQL has parameter + .Select(Projections.RowCount()) + ; + + var originalQueryOver = session.QueryOver() + //.Where(x => x.Id == parameter2) + .SelectList(l => l + .Select(x => x.Id) + .SelectSubQuery(countSubquery) + ) + .TransformUsing(Transformers.ToList); + + var clonedQueryOver = originalQueryOver.Clone(); + clonedQueryOver.List(); + + Assert.DoesNotThrow(() => originalQueryOver.List(), "Cloned QueryOver execution caused source QueryOver throw exception when executed."); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index 45d796ec55f..3bab554137d 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -869,6 +869,8 @@ + + From e4d000e022c2294092a617115ac6b98c02a83990 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Tue, 5 May 2015 22:14:33 +1200 Subject: [PATCH 2/2] NH-3727 - Fix re-using Criteria with SubqueryProjection --- .../Criterion/SubqueryExpression.cs | 47 ++++++------------- .../Criterion/SubqueryProjection.cs | 7 ++- 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/src/NHibernate/Criterion/SubqueryExpression.cs b/src/NHibernate/Criterion/SubqueryExpression.cs index 017e67e8973..b9ff54595b1 100644 --- a/src/NHibernate/Criterion/SubqueryExpression.cs +++ b/src/NHibernate/Criterion/SubqueryExpression.cs @@ -20,10 +20,8 @@ public abstract class SubqueryExpression : AbstractCriterion private QueryParameters parameters; private IType[] types; - [NonSerialized] private CriteriaQueryTranslator innerQuery; - protected SubqueryExpression(String op, String quantifier, DetachedCriteria dc) - :this(op, quantifier, dc, true) + : this(op, quantifier, dc, true) { } @@ -44,16 +42,23 @@ public IType[] GetTypes() public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary enabledFilters) { - InitializeInnerQueryAndParameters(criteriaQuery); + ISessionFactoryImplementor factory = criteriaQuery.Factory; + + var innerQuery = new CriteriaQueryTranslator( + factory, + criteriaImpl, //implicit polymorphism not supported (would need a union) + criteriaImpl.EntityOrClassName, + criteriaQuery.GenerateSQLAlias(), + criteriaQuery); + + types = innerQuery.HasProjection ? innerQuery.ProjectedTypes : null; if (innerQuery.HasProjection == false) { throw new QueryException("Cannot use subqueries on a criteria without a projection."); } - ISessionFactoryImplementor factory = criteriaQuery.Factory; - - IOuterJoinLoadable persister = (IOuterJoinLoadable)factory.GetEntityPersister(criteriaImpl.EntityOrClassName); + IOuterJoinLoadable persister = (IOuterJoinLoadable) factory.GetEntityPersister(criteriaImpl.EntityOrClassName); //patch to generate joins on subqueries //stolen from CriteriaLoader @@ -73,11 +78,6 @@ public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteri sql = factory.Dialect.GetLimitString(sql, offset, limit, offsetParameter, limitParameter); } - // during CriteriaImpl.Clone we are doing a shallow copy of each criterion. - // this is not a problem for common criterion but not for SubqueryExpression because here we are holding the state of inner CriteriaTraslator (ICriteriaQuery). - // After execution (ToSqlString) we have to clean the internal state because the next execution may be performed in a different tree reusing the same istance of SubqueryExpression. - innerQuery = null; - SqlStringBuilder buf = new SqlStringBuilder().Add(ToLeftSqlString(criteria, criteriaQuery)); if (op != null) { @@ -88,7 +88,7 @@ public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteri { buf.Add(quantifier).Add(" "); } - + buf.Add("(").Add(sql).Add(")"); if (quantifier != null && prefixOp == false) @@ -101,9 +101,9 @@ public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteri public override string ToString() { - if(prefixOp) + if (prefixOp) return string.Format("{0} {1} ({2})", op, quantifier, criteriaImpl); - + return string.Format("{0} ({1}) {2}", op, criteriaImpl, quantifier); } @@ -117,23 +117,6 @@ public override IProjection[] GetProjections() return null; } - public void InitializeInnerQueryAndParameters(ICriteriaQuery criteriaQuery) - { - if (innerQuery == null) - { - ISessionFactoryImplementor factory = criteriaQuery.Factory; - - innerQuery = new CriteriaQueryTranslator( - factory, - criteriaImpl, //implicit polymorphism not supported (would need a union) - criteriaImpl.EntityOrClassName, - criteriaQuery.GenerateSQLAlias(), - criteriaQuery); - - types = innerQuery.HasProjection ? innerQuery.ProjectedTypes : null; - } - } - public ICriteria Criteria { // NH-1146 diff --git a/src/NHibernate/Criterion/SubqueryProjection.cs b/src/NHibernate/Criterion/SubqueryProjection.cs index 5d535ebcead..eec6204059f 100644 --- a/src/NHibernate/Criterion/SubqueryProjection.cs +++ b/src/NHibernate/Criterion/SubqueryProjection.cs @@ -13,7 +13,7 @@ namespace NHibernate.Criterion [Serializable] public class SubqueryProjection : SimpleProjection { - private SelectSubqueryExpression _subQuery; + private readonly SelectSubqueryExpression _subQuery; protected internal SubqueryProjection(SelectSubqueryExpression subquery) { @@ -37,14 +37,13 @@ public override bool IsAggregate public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery) { - _subQuery.InitializeInnerQueryAndParameters(criteriaQuery); return _subQuery.GetTypes(); } public override SqlString ToSqlString(ICriteria criteria, int loc, ICriteriaQuery criteriaQuery, IDictionary enabledFilters) { - SqlString sqlStringSubquery = _subQuery.ToSqlString(criteria, criteriaQuery, enabledFilters); - return sqlStringSubquery.Append(new SqlString(new object[] { " as y", loc.ToString(), "_" } )); + return _subQuery.ToSqlString(criteria, criteriaQuery, enabledFilters) + .Append(new SqlString(" as y", loc.ToString(), "_")); } public override SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary enabledFilters)