From: <fab...@us...> - 2011-05-17 17:45:16
|
Revision: 5830 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5830&view=rev Author: fabiomaulo Date: 2011-05-17 17:45:09 +0000 (Tue, 17 May 2011) Log Message: ----------- Fix NH-2705 and NH-2615 Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementType.cs trunk/nhibernate/src/NHibernate/NHibernate.csproj trunk/nhibernate/src/NHibernate/PropertyNotFoundException.cs trunk/nhibernate/src/NHibernate/Type/ComponentType.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/ItemBase.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/Mappings.hbm.xml trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/SubItemBase.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/Test.cs Added Paths: ----------- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ComponentJoin.cs Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -678,23 +678,30 @@ // to the root dot node. dot.Resolve( true, false, alias == null ? null : alias.Text ); - FromElement fromElement = dot.GetImpliedJoin(); - - if (fromElement == null) + FromElement fromElement; + if (dot.DataType != null && dot.DataType.IsComponentType) { - throw new InvalidPathException("Invalid join: " + dot.Path); + var factory = new FromElementFactory(CurrentFromClause, dot.GetLhs().FromElement, dot.PropertyPath, alias == null ? null : alias.Text, null, false); + fromElement = factory.CreateComponentJoin((ComponentType) dot.DataType); } - - fromElement.SetAllPropertyFetch(propertyFetch!=null); - - if ( with != null ) + else { - if ( fetch ) + fromElement = dot.GetImpliedJoin(); + if (fromElement == null) { - throw new SemanticException( "with-clause not allowed on fetched associations; use filters" ); + throw new InvalidPathException("Invalid join: " + dot.Path); } + fromElement.SetAllPropertyFetch(propertyFetch != null); - HandleWithFragment( fromElement, with ); + if (with != null) + { + if (fetch) + { + throw new SemanticException("with-clause not allowed on fetched associations; use filters"); + } + + HandleWithFragment(fromElement, with); + } } if ( log.IsDebugEnabled ) Added: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ComponentJoin.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ComponentJoin.cs (rev 0) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ComponentJoin.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -0,0 +1,175 @@ +using System; +using System.Text; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; +using NHibernate.Type; +using NHibernate.Util; + +namespace NHibernate.Hql.Ast.ANTLR.Tree +{ + public class ComponentJoin : FromElement + { + private readonly string columns; + private readonly string componentPath; + private readonly string componentProperty; + private readonly ComponentType componentType; + + public ComponentJoin(FromClause fromClause, FromElement origin, string alias, string componentPath, ComponentType componentType) + : base(fromClause, origin, alias) + { + this.componentPath = componentPath; + this.componentType = componentType; + componentProperty = StringHelper.Unqualify(componentPath); + fromClause.AddJoinByPathMap(componentPath, this); + InitializeComponentJoin(new ComponentFromElementType(this)); + + string[] cols = origin.GetPropertyMapping("").ToColumns(TableAlias, componentProperty); + columns = string.Join(", ", cols); + } + + public string ComponentPath + { + get { return componentPath; } + } + + public ComponentType ComponentType + { + get { return componentType; } + } + + public string ComponentProperty + { + get { return componentProperty; } + } + + public override IType DataType + { + get { return ComponentType; } + set { base.DataType = value; } + } + + public override string GetIdentityColumn() + { + return columns; + } + + public override string GetDisplayText() + { + return "ComponentJoin{path=" + ComponentPath + ", type=" + componentType.ReturnedClass + "}"; + } + + #region Nested type: ComponentFromElementType + + public class ComponentFromElementType : FromElementType + { + private readonly ComponentJoin fromElement; + private readonly IPropertyMapping propertyMapping; + + public ComponentFromElementType(ComponentJoin fromElement) + : base(fromElement) + { + this.fromElement = fromElement; + propertyMapping = new ComponentPropertyMapping(this); + } + + public ComponentJoin FromElement + { + get { return fromElement; } + } + + public override IType DataType + { + get { return fromElement.ComponentType; } + } + + public override IQueryableCollection QueryableCollection + { + get { return null; } + set { base.QueryableCollection = value; } + } + + public override IPropertyMapping GetPropertyMapping(string propertyName) + { + return propertyMapping; + } + + public override IType GetPropertyType(string propertyName, string propertyPath) + { + int index = fromElement.ComponentType.GetPropertyIndex(propertyName); + return fromElement.ComponentType.Subtypes[index]; + } + + public override string RenderScalarIdentifierSelect(int i) + { + String[] cols = GetBasePropertyMapping().ToColumns(fromElement.TableAlias, fromElement.ComponentProperty); + var buf = new StringBuilder(); + // For property references generate <tablealias>.<columnname> as <projectionalias> + for (int j = 0; j < cols.Length; j++) + { + string column = cols[j]; + if (j > 0) + { + buf.Append(", "); + } + buf.Append(column).Append(" as ").Append(NameGenerator.ScalarName(i, j)); + } + return buf.ToString(); + } + + protected IPropertyMapping GetBasePropertyMapping() + { + return fromElement.Origin.GetPropertyMapping(""); + } + + #region Nested type: ComponentPropertyMapping + + private class ComponentPropertyMapping : IPropertyMapping + { + private readonly ComponentFromElementType fromElementType; + + public ComponentPropertyMapping(ComponentFromElementType fromElementType) + { + this.fromElementType = fromElementType; + } + + #region IPropertyMapping Members + + public IType Type + { + get { return fromElementType.FromElement.ComponentType; } + } + + public IType ToType(string propertyName) + { + return fromElementType.GetBasePropertyMapping().ToType(GetPropertyPath(propertyName)); + } + + public bool TryToType(string propertyName, out IType type) + { + return fromElementType.GetBasePropertyMapping().TryToType(GetPropertyPath(propertyName), out type); + } + + public string[] ToColumns(string alias, string propertyName) + { + return fromElementType.GetBasePropertyMapping().ToColumns(alias, GetPropertyPath(propertyName)); + } + + public string[] ToColumns(string propertyName) + { + return fromElementType.GetBasePropertyMapping().ToColumns(GetPropertyPath(propertyName)); + } + + #endregion + + private string GetPropertyPath(string propertyName) + { + return fromElementType.FromElement.ComponentPath + '.' + propertyName; + } + } + + #endregion + } + + #endregion + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -342,7 +342,7 @@ string tableAlias = element.TableAlias; if (tableAlias != null) { - _fromElementByTableAlias.Add(tableAlias, element); + _fromElementByTableAlias[tableAlias] = element; } } Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -39,12 +39,35 @@ private string _withClauseFragment; private string _withClauseJoinAlias; private bool _filter; + private IToken _token; - public FromElement(IToken token) : base(token) { + _token= token; } + /// <summary> + /// Constructor form used to initialize <see cref="ComponentJoin"/>. + /// </summary> + /// <param name="fromClause">The FROM clause to which this element belongs.</param> + /// <param name="origin">The origin (LHS) of this element.</param> + /// <param name="alias">The alias applied to this element.</param> + protected FromElement(FromClause fromClause,FromElement origin,string alias):this(origin._token) + { + _fromClause = fromClause; + _origin = origin; + _classAlias = alias; + _tableAlias = origin.TableAlias; + base.Initialize(fromClause.Walker); + } + + protected void InitializeComponentJoin(FromElementType elementType) + { + _elementType = elementType; + _fromClause.RegisterFromElement(this); + _initialized = true; + } + public void SetAllPropertyFetch(bool fetch) { _isAllPropertyFetch = fetch; @@ -429,7 +452,7 @@ return _elementType.GetPropertyType(propertyName, propertyPath); } - public string GetIdentityColumn() + public virtual string GetIdentityColumn() { CheckInitialized(); string table = TableAlias; Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementFactory.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -562,5 +562,10 @@ } } + public FromElement CreateComponentJoin(ComponentType type) + { + // need to create a "place holder" from-element that can store the component/alias for this component join + return new ComponentJoin(_fromClause, _origin, _classAlias, _path, type); + } } } Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementType.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementType.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromElementType.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -42,6 +42,11 @@ } } + protected FromElementType(FromElement fromElement) + { + _fromElement = fromElement; + } + public IEntityPersister EntityPersister { get { return _persister; } @@ -57,7 +62,7 @@ get { return _fromElement.CollectionTableAlias; } } - public IType DataType + public virtual IType DataType { get { @@ -167,7 +172,7 @@ /// </summary> /// <param name="i">the sequence of the returned type</param> /// <returns>the identifier select with the column alias.</returns> - public string RenderScalarIdentifierSelect(int i) + public virtual string RenderScalarIdentifierSelect(int i) { CheckInitialized(); string[] cols = GetPropertyMapping(Persister.Entity.EntityPersister.EntityID).ToColumns(TableAlias, Persister.Entity.EntityPersister.EntityID); @@ -273,7 +278,7 @@ } } - public IPropertyMapping GetPropertyMapping(string propertyName) + public virtual IPropertyMapping GetPropertyMapping(string propertyName) { CheckInitialized(); @@ -317,7 +322,7 @@ /// <param name="propertyName">The last part of the full path to the property.</param> /// <param name="propertyPath">The full property path.</param> /// <returns>The type</returns> - public IType GetPropertyType(string propertyName, string propertyPath) + public virtual IType GetPropertyType(string propertyName, string propertyPath) { CheckInitialized(); @@ -359,7 +364,7 @@ get { return (_persister is IQueryable) ? (IQueryable) _persister : null; } } - public IQueryableCollection QueryableCollection + public virtual IQueryableCollection QueryableCollection { get { return _queryableCollection; } set Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2011-05-17 17:45:09 UTC (rev 5830) @@ -206,6 +206,7 @@ <Compile Include="FetchMode.cs" /> <Compile Include="FlushMode.cs" /> <Compile Include="HibernateException.cs" /> + <Compile Include="Hql\Ast\ANTLR\Tree\ComponentJoin.cs" /> <Compile Include="Hql\Classic\ClauseParser.cs" /> <Compile Include="Hql\Classic\FromParser.cs" /> <Compile Include="Hql\Classic\FromPathExpressionParser.cs" /> Modified: trunk/nhibernate/src/NHibernate/PropertyNotFoundException.cs =================================================================== --- trunk/nhibernate/src/NHibernate/PropertyNotFoundException.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate/PropertyNotFoundException.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -38,7 +38,7 @@ /// <param name="targetType">The <see cref="System.Type" /> that is missing the field</param> /// <param name="propertyName">The name of the missing property</param> public PropertyNotFoundException(System.Type targetType, string propertyName) - : base(String.Format("Could not find field '{0}' in class '{1}'", + : base(String.Format("Could not find property nor field '{0}' in class '{1}'", propertyName, targetType)) { this.targetType = targetType; Modified: trunk/nhibernate/src/NHibernate/Type/ComponentType.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Type/ComponentType.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate/Type/ComponentType.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -688,5 +688,18 @@ { get { return true; } } + + public int GetPropertyIndex(string name) + { + string[] names = PropertyNames; + for (int i = 0; i < names.Length; i++) + { + if (names[i].Equals(name)) + { + return i; + } + } + throw new PropertyNotFoundException(ReturnedClass, name); + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/ItemBase.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/ItemBase.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/ItemBase.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -7,6 +7,4 @@ } public class ItemWithComponentSubItem : ItemBase {} - - public class ItemWithManyToOneSubItem : ItemBase {} } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/Mappings.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/Mappings.hbm.xml 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/Mappings.hbm.xml 2011-05-17 17:45:09 UTC (rev 5830) @@ -14,20 +14,8 @@ <many-to-one class="SubItemDetails" name="Details" column="SubItemDetails_id"/> </component> </joined-subclass> - <joined-subclass name="ItemWithManyToOneSubItem"> - <key column="ItemBase_id"/> - <many-to-one class="SubItemEntity" name="SubItem" column="SubItem_id"/> - </joined-subclass> </class> - <class name="SubItemEntity"> - <id name="Id" type="int"> - <generator class="native" /> - </id> - <property name="Name" /> - <many-to-one class="SubItemDetails" name="Details" column="SubItemDetails_id" /> - </class> - <class name="SubItemDetails"> <id name="Id" type="int"> <generator class="native" /> Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/SubItemBase.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/SubItemBase.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/SubItemBase.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -1,5 +1,7 @@ namespace NHibernate.Test.NHSpecificTest.NH2705 { + // NOTE: an Entity and a Component in the same hierarchy is not supported + // we are using this trick just to ""simplify"" the test. public class SubItemBase { public virtual string Name { get; set; } @@ -7,9 +9,4 @@ } public class SubItemComponent : SubItemBase {} - - public class SubItemEntity : SubItemBase - { - public virtual int Id { get; set; } - } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/Test.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/Test.cs 2011-05-16 14:37:27 UTC (rev 5829) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2705/Test.cs 2011-05-17 17:45:09 UTC (rev 5830) @@ -8,7 +8,7 @@ namespace NHibernate.Test.NHSpecificTest.NH2705 { - [TestFixture, Ignore("Not fixed yet")] + [TestFixture] public class Test : BugTestCase { private static IEnumerable<T> GetAndFetch<T>(string name, ISession session) where T : ItemBase @@ -30,22 +30,26 @@ } [Test] - public void Fetch_OnManyToOne_ShouldNotThrow() + public void HqlQueryWithFetch_WhenDerivedClassesUseComponentAndManyToOne_DoesNotGenerateInvalidSql() { using (ISession s = OpenSession()) { - Executing.This(() => GetAndFetch<ItemWithManyToOneSubItem>("hello", s)).Should().NotThrow(); + using (var log = new SqlLogSpy()) + { + Executing.This(() => s.CreateQuery("from ItemWithComponentSubItem i left join fetch i.SubItem").List() + ).Should().NotThrow(); + } } } [Test] - public void HqlQueryWithFetch_WhenDerivedClassesUseComponentAndManyToOne_DoesNotGenerateInvalidSql() + public void HqlQueryWithFetch_WhenDerivedClassesUseComponentAndEagerFetchManyToOne_DoesNotGenerateInvalidSql() { using (ISession s = OpenSession()) { using (var log = new SqlLogSpy()) { - Executing.This(() => s.CreateQuery("from ItemBase i left join fetch i.SubItem").List() + Executing.This(() => s.CreateQuery("from ItemWithComponentSubItem i left join fetch i.SubItem.Details").List() ).Should().NotThrow(); } } @@ -64,11 +68,23 @@ // fetching second level properties should work too - Executing.This(() => s.Query<ItemBase>() + Executing.This(() => s.Query<ItemWithComponentSubItem>() .Fetch(p => p.SubItem).ThenFetch(p => p.Details).ToList() ).Should().NotThrow(); } } } + + [Test, Ignore("Locked by re-linq")] + public void LinqQueryWithFetch_WhenDerivedClassesUseComponentAndEagerFetchManyToOne_DoesNotGenerateInvalidSql() + { + using (ISession s = OpenSession()) + { + using (var log = new SqlLogSpy()) + { + Executing.This(() => s.Query<ItemWithComponentSubItem>().Fetch(p => p.SubItem.Details).ToList()).Should().NotThrow(); + } + } + } } } \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |