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