From cc46650dfd2e94f671f867a50e6d70e4b903b5ed Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Thu, 18 Oct 2012 01:39:51 +0200 Subject: [PATCH 1/9] Removed duplicate custom SQL returns from test mapping --- src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml index 4ca17d89857..993b7fa1be1 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml @@ -241,8 +241,6 @@ - - SELECT org.ORGID as orgid, org.NAME as name, emp.EMPLOYER as employer, From 2bd8bc799f368b2b55c7d822a106df3e3ca78e64 Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Thu, 18 Oct 2012 01:40:50 +0200 Subject: [PATCH 2/9] Fixed reporting of incorrect result-ref attribute values --- src/NHibernate/Impl/SessionFactoryImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index 24c1b4ffeb2..4f4ca3dcade 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -1177,8 +1177,8 @@ private IDictionary CheckNamedQueries() NativeSQLQuerySpecification spec; if (qd.ResultSetRef != null) { - ResultSetMappingDefinition definition = sqlResultSetMappings[qd.ResultSetRef]; - if (definition == null) + ResultSetMappingDefinition definition; + if (!sqlResultSetMappings.TryGetValue(qd.ResultSetRef, out definition)) { throw new MappingException("Unable to find resultset-ref definition: " + qd.ResultSetRef); } From 69cd430159425c20f0cfe1249f032b0fc0c84455 Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Thu, 18 Oct 2012 01:42:40 +0200 Subject: [PATCH 3/9] Raise exception when user defined resultset mapping misses expected properties --- src/NHibernate/Loader/DefaultEntityAliases.cs | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/NHibernate/Loader/DefaultEntityAliases.cs b/src/NHibernate/Loader/DefaultEntityAliases.cs index 14fcefd8b44..29f6ee16a83 100644 --- a/src/NHibernate/Loader/DefaultEntityAliases.cs +++ b/src/NHibernate/Loader/DefaultEntityAliases.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using NHibernate.Persister.Entity; using NHibernate.Util; @@ -26,18 +27,14 @@ public DefaultEntityAliases(ILoadable persister, string suffix) /// public DefaultEntityAliases(IDictionary userProvidedAliases, ILoadable persister, string suffix) { + ValidateUserProvidedAliases(userProvidedAliases, persister); + this.suffix = suffix; this.userProvidedAliases = userProvidedAliases; string[] keyColumnsCandidates = GetUserProvidedAliases(persister.IdentifierPropertyName, null); - if (keyColumnsCandidates == null) - { - suffixedKeyColumns = GetUserProvidedAliases(EntityPersister.EntityID, GetIdentifierAliases(persister, suffix)); - } - else - { - suffixedKeyColumns = keyColumnsCandidates; - } + suffixedKeyColumns = keyColumnsCandidates ?? + GetUserProvidedAliases(EntityPersister.EntityID, GetIdentifierAliases(persister, suffix)); Intern(suffixedKeyColumns); suffixedPropertyColumns = GetSuffixedPropertyAliases(persister); @@ -54,6 +51,21 @@ public DefaultEntityAliases(IDictionary userProvidedAliases, I rowIdAlias = Loadable.RowIdAlias + suffix; // TODO: not visible to the user! } + private static void ValidateUserProvidedAliases(IDictionary userProvidedAliases, ILoadable persister) + { + if (userProvidedAliases != null && userProvidedAliases.Count > 0) + { + var missingPropertyNames = persister.PropertyNames.Except(userProvidedAliases.Keys).ToArray(); + if (missingPropertyNames.Length > 0) + { + throw new MappingException( + string.Format( + "User provided resulset mapping for entity '{0}' misses mappings for the following properties: {1}.", + persister.EntityName, string.Join(", ", missingPropertyNames))); + } + } + } + protected virtual string GetDiscriminatorAlias(ILoadable persister, string suffix) { return persister.GetDiscriminatorAlias(suffix); From 71a41651a501749f35e3e31522a82f34d0d041dc Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Thu, 18 Oct 2012 01:44:58 +0200 Subject: [PATCH 4/9] Cleaned up some custom SQL code --- .../Loader/Custom/Sql/SQLCustomQuery.cs | 2 +- .../Custom/Sql/SQLQueryReturnProcessor.cs | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs index c91aeddee4c..328b571677a 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs @@ -21,7 +21,7 @@ public class SQLCustomQuery : ICustomQuery private readonly List customQueryReturns = new List(); private readonly ISet querySpaces = new HashedSet(); private readonly SqlString sql; - private List parametersSpecifications; + private readonly List parametersSpecifications; public SQLCustomQuery(INativeSQLQueryReturn[] queryReturns, string sqlQuery, ICollection additionalQuerySpaces, ISessionFactoryImplementor factory) diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs index 70a0b981541..f042816aaaa 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs @@ -103,14 +103,7 @@ public IDictionary GetPropertyResultsMap(string alias) private IDictionary InternalGetPropertyResultsMap(string alias) { NativeSQLQueryNonScalarReturn rtn = alias2Return[alias] as NativeSQLQueryNonScalarReturn; - if (rtn != null) - { - return rtn.PropertyResultsMap; - } - else - { - return null; - } + return rtn != null ? rtn.PropertyResultsMap : null; } private bool HasPropertyResultMap(string alias) @@ -125,13 +118,13 @@ public ResultAliasContext Process() // so that role returns can be more easily resolved to their owners for (int i = 0; i < queryReturns.Length; i++) { - if (queryReturns[i] is NativeSQLQueryNonScalarReturn) + var rtn = queryReturns[i] as NativeSQLQueryNonScalarReturn; + if (rtn != null) { - NativeSQLQueryNonScalarReturn rtn = (NativeSQLQueryNonScalarReturn) queryReturns[i]; alias2Return[rtn.Alias] = rtn; - if (rtn is NativeSQLQueryJoinReturn) + var roleReturn = queryReturns[i] as NativeSQLQueryJoinReturn; + if (roleReturn != null) { - NativeSQLQueryJoinReturn roleReturn = (NativeSQLQueryJoinReturn) queryReturns[i]; alias2OwnerAlias[roleReturn.Alias] = roleReturn.OwnerAlias; } } From 55e3e201c1e40340efcec902ef3e7b2de9f088f6 Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Wed, 24 Oct 2012 09:36:31 +0200 Subject: [PATCH 5/9] Refactored custom SQL query implementation. --- src/NHibernate/Loader/BasicLoader.cs | 3 +- .../Loader/Custom/CollectionFetchReturn.cs | 27 -- .../Loader/Custom/CollectionReturn.cs | 48 -- .../Loader/Custom/ColumnCollectionAliases.cs | 53 ++- .../Loader/Custom/ColumnEntityAliases.cs | 4 +- src/NHibernate/Loader/Custom/CustomLoader.cs | 239 +++------- .../Loader/Custom/EntityFetchReturn.cs | 19 - src/NHibernate/Loader/Custom/FetchReturn.cs | 28 -- .../GeneratedCollectionAliases.cs | 49 +-- src/NHibernate/Loader/Custom/IReturn.cs | 4 + .../Loader/Custom/NonScalarReturn.cs | 87 +++- src/NHibernate/Loader/Custom/RootReturn.cs | 32 -- src/NHibernate/Loader/Custom/ScalarReturn.cs | 4 +- .../Loader/Custom/Sql/SQLCustomQuery.cs | 91 ++-- .../Loader/Custom/Sql/SQLQueryContext.cs | 285 ++++++++++++ .../Loader/Custom/Sql/SQLQueryParser.cs | 87 ++-- .../Custom/Sql/SQLQueryReturnProcessor.cs | 409 ------------------ src/NHibernate/Loader/DefaultEntityAliases.cs | 42 +- src/NHibernate/NHibernate.csproj | 11 +- 19 files changed, 580 insertions(+), 942 deletions(-) delete mode 100644 src/NHibernate/Loader/Custom/CollectionFetchReturn.cs delete mode 100644 src/NHibernate/Loader/Custom/CollectionReturn.cs delete mode 100644 src/NHibernate/Loader/Custom/EntityFetchReturn.cs delete mode 100644 src/NHibernate/Loader/Custom/FetchReturn.cs rename src/NHibernate/Loader/{ => Custom}/GeneratedCollectionAliases.cs (78%) delete mode 100644 src/NHibernate/Loader/Custom/RootReturn.cs create mode 100644 src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs delete mode 100644 src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs diff --git a/src/NHibernate/Loader/BasicLoader.cs b/src/NHibernate/Loader/BasicLoader.cs index a5cd43b174d..64ee5d787fa 100644 --- a/src/NHibernate/Loader/BasicLoader.cs +++ b/src/NHibernate/Loader/BasicLoader.cs @@ -1,4 +1,5 @@ -using NHibernate.Engine; +using NHibernate.Engine; +using NHibernate.Loader.Custom; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.Type; diff --git a/src/NHibernate/Loader/Custom/CollectionFetchReturn.cs b/src/NHibernate/Loader/Custom/CollectionFetchReturn.cs deleted file mode 100644 index 63484739ad8..00000000000 --- a/src/NHibernate/Loader/Custom/CollectionFetchReturn.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// Spefically a fetch return that refers to a collection association. - public class CollectionFetchReturn : FetchReturn - { - private readonly ICollectionAliases collectionAliases; - private readonly IEntityAliases elementEntityAliases; - - public CollectionFetchReturn(string alias, NonScalarReturn owner, string ownerProperty, - ICollectionAliases collectionAliases, IEntityAliases elementEntityAliases, - LockMode lockMode) : base(owner, ownerProperty, alias, lockMode) - { - this.collectionAliases = collectionAliases; - this.elementEntityAliases = elementEntityAliases; - } - - public ICollectionAliases CollectionAliases - { - get { return collectionAliases; } - } - - public IEntityAliases ElementEntityAliases - { - get { return elementEntityAliases; } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/CollectionReturn.cs b/src/NHibernate/Loader/Custom/CollectionReturn.cs deleted file mode 100644 index 573c7240ac3..00000000000 --- a/src/NHibernate/Loader/Custom/CollectionReturn.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// - /// Represents a return which names a collection role; it - /// is used in defining a custom query for loading an entity's - /// collection in non-fetching scenarios (i.e., loading the collection - /// itself as the "root" of the result). - /// - public class CollectionReturn : NonScalarReturn - { - private readonly string ownerEntityName; - private readonly string ownerProperty; - private readonly ICollectionAliases collectionAliases; - private readonly IEntityAliases elementEntityAliases; - - public CollectionReturn(string alias, string ownerEntityName, string ownerProperty, - ICollectionAliases collectionAliases, IEntityAliases elementEntityAliases, LockMode lockMode) - : base(alias, lockMode) - { - this.ownerEntityName = ownerEntityName; - this.ownerProperty = ownerProperty; - this.collectionAliases = collectionAliases; - this.elementEntityAliases = elementEntityAliases; - } - - /// Returns the class owning the collection. - public string OwnerEntityName - { - get { return ownerEntityName; } - } - - /// Returns the name of the property representing the collection from the . - public string OwnerProperty - { - get { return ownerProperty; } - } - - public ICollectionAliases CollectionAliases - { - get { return collectionAliases; } - } - - public IEntityAliases ElementEntityAliases - { - get { return elementEntityAliases; } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs index bc6c18836f2..108d950ed79 100644 --- a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs @@ -21,13 +21,36 @@ public ColumnCollectionAliases(IDictionary userProvidedAliases { this.userProvidedAliases = userProvidedAliases; - keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); + this.keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); + this.indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); + this.elementAliases = GetUserProvidedAliases("element", persister.ElementColumnNames); - indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); + // NH-1612: Add aliases for all composite element properties to support access + // to individual composite element properties in elements. + this.elementAliases = persister.ElementType.IsComponentType + ? GetUserProvidedCompositeElementAliases(persister.ElementColumnNames) + : GetUserProvidedAliases("element", persister.ElementColumnNames); - elementAliases = GetUserProvidedAliases("element", persister.ElementColumnNames); + this.identifierAlias = GetUserProvidedAlias("id", persister.IdentifierColumnName); + } - identifierAlias = GetUserProvidedAlias("id", persister.IdentifierColumnName); + private string[] GetUserProvidedCompositeElementAliases(string[] defaultAliases) + { + if (userProvidedAliases != null) + { + var aliases = new List(); + foreach (var userProvidedAlias in userProvidedAliases) + { + if (userProvidedAlias.Key.StartsWith("element.")) + { + aliases.AddRange(userProvidedAlias.Value); + } + } + + if (aliases.Count > 0) return aliases.ToArray(); + } + + return defaultAliases; } /// @@ -96,27 +119,17 @@ private static string Join(IEnumerable aliases) private string[] GetUserProvidedAliases(string propertyPath, string[] defaultAliases) { string[] result; - if (!userProvidedAliases.TryGetValue(propertyPath, out result)) - { - return defaultAliases; - } - else - { - return result; - } + return userProvidedAliases == null || !userProvidedAliases.TryGetValue(propertyPath, out result) + ? defaultAliases + : result; } private string GetUserProvidedAlias(string propertyPath, string defaultAlias) { string[] columns; - if (!userProvidedAliases.TryGetValue(propertyPath, out columns)) - { - return defaultAlias; - } - else - { - return columns[0]; - } + return userProvidedAliases == null || !userProvidedAliases.TryGetValue(propertyPath, out columns) + ? defaultAlias + : columns[0]; } } } diff --git a/src/NHibernate/Loader/Custom/ColumnEntityAliases.cs b/src/NHibernate/Loader/Custom/ColumnEntityAliases.cs index 810a453699d..009d06f345b 100644 --- a/src/NHibernate/Loader/Custom/ColumnEntityAliases.cs +++ b/src/NHibernate/Loader/Custom/ColumnEntityAliases.cs @@ -8,8 +8,8 @@ namespace NHibernate.Loader.Custom /// public class ColumnEntityAliases : DefaultEntityAliases { - public ColumnEntityAliases(IDictionary returnProperties, ILoadable persister, string suffix) - : base(returnProperties, persister, suffix) {} + public ColumnEntityAliases(IDictionary returnProperties, ILoadable persister) + : base(returnProperties, persister, null) {} protected override string[] GetIdentifierAliases(ILoadable persister, string suffix) { diff --git a/src/NHibernate/Loader/Custom/CustomLoader.cs b/src/NHibernate/Loader/Custom/CustomLoader.cs index e4155c5666b..b695f5c9835 100644 --- a/src/NHibernate/Loader/Custom/CustomLoader.cs +++ b/src/NHibernate/Loader/Custom/CustomLoader.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Collections; using System.Collections.Generic; @@ -12,8 +11,6 @@ using NHibernate.SqlCommand; using NHibernate.Transform; using NHibernate.Type; -using NHibernate.Util; -using IQueryable = NHibernate.Persister.Entity.IQueryable; namespace NHibernate.Loader.Custom { @@ -26,13 +23,13 @@ public class CustomLoader : Loader private readonly SqlString sql; private readonly ISet querySpaces = new HashedSet(); - private List parametersSpecifications; + private readonly List parametersSpecifications; - private readonly IQueryable[] entityPersisters; + private readonly ILoadable[] entityPersisters; private readonly int[] entityOwners; private readonly IEntityAliases[] entityAliases; - private readonly IQueryableCollection[] collectionPersisters; + private readonly ICollectionPersister[] collectionPersisters; private readonly int[] collectionOwners; private readonly ICollectionAliases[] collectionAliases; @@ -44,133 +41,79 @@ public class CustomLoader : Loader public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory) : base(factory) { - sql = customQuery.SQL; - querySpaces.AddAll(customQuery.QuerySpaces); - parametersSpecifications = customQuery.CollectedParametersSpecifications.ToList(); - - List entitypersisters = new List(); - List entityowners = new List(); - List entityaliases = new List(); - - List collectionpersisters = new List(); - List collectionowners = new List(); - List collectionaliases = new List(); - - List lockmodes = new List(); - List resultColumnProcessors = new List(); - List nonScalarReturnList = new List(); - List resulttypes = new List(); - List specifiedAliases = new List(); + this.sql = customQuery.SQL; + this.querySpaces.AddAll(customQuery.QuerySpaces); + this.parametersSpecifications = customQuery.CollectedParametersSpecifications.ToList(); + + var entityPersisters = new List(); + var entityOwners = new List(); + var entityAliases = new List(); + var collectionPersisters = new List(); + var collectionOwners = new List(); + var collectionAliases = new List(); + var lockModes = new List(); + var resultColumnProcessors = new List(); + var resultTypes = new List(); + var transformerAliases = new List(); int returnableCounter = 0; bool hasScalars = false; foreach (IReturn rtn in customQuery.CustomQueryReturns) { + transformerAliases.Add(rtn.Alias); + if (rtn is ScalarReturn) { - ScalarReturn scalarRtn = (ScalarReturn) rtn; - resulttypes.Add(scalarRtn.Type); - specifiedAliases.Add(scalarRtn.ColumnAlias); - resultColumnProcessors.Add(new ScalarResultColumnProcessor(scalarRtn.ColumnAlias, scalarRtn.Type)); + resultTypes.Add(rtn.Type); + resultColumnProcessors.Add(new ScalarResultColumnProcessor(rtn.Alias, rtn.Type)); hasScalars = true; + continue; } - else if (rtn is RootReturn) - { - RootReturn rootRtn = (RootReturn) rtn; - IQueryable persister = (IQueryable) factory.GetEntityPersister(rootRtn.EntityName); - entitypersisters.Add(persister); - lockmodes.Add(rootRtn.LockMode); - resultColumnProcessors.Add(new NonScalarResultColumnProcessor(returnableCounter++)); - nonScalarReturnList.Add(rtn); - entityowners.Add(-1); - resulttypes.Add(persister.Type); - specifiedAliases.Add(rootRtn.Alias); - entityaliases.Add(rootRtn.EntityAliases); - querySpaces.AddAll(persister.QuerySpaces); - } - else if (rtn is CollectionReturn) + + var nonScalarRtn = rtn as NonScalarReturn; + if (nonScalarRtn != null) { - CollectionReturn collRtn = (CollectionReturn) rtn; - string role = collRtn.OwnerEntityName + "." + collRtn.OwnerProperty; - IQueryableCollection persister = (IQueryableCollection) factory.GetCollectionPersister(role); - collectionpersisters.Add(persister); - lockmodes.Add(collRtn.LockMode); - resultColumnProcessors.Add(new NonScalarResultColumnProcessor(returnableCounter++)); - nonScalarReturnList.Add(rtn); - collectionowners.Add(-1); - resulttypes.Add(persister.Type); - specifiedAliases.Add(collRtn.Alias); - collectionaliases.Add(collRtn.CollectionAliases); - // determine if the collection elements are entities... - IType elementType = persister.ElementType; - if (elementType.IsEntityType) + lockModes.Add(nonScalarRtn.LockMode); + + var ownerIndex = nonScalarRtn.Owner != null + ? entityPersisters.IndexOf(nonScalarRtn.Owner.EntityPersister) + : -1; + if (nonScalarRtn.EntityPersister != null) { - IQueryable elementPersister = (IQueryable) ((EntityType) elementType).GetAssociatedJoinable(factory); - entitypersisters.Add(elementPersister); - entityowners.Add(-1); - entityaliases.Add(collRtn.ElementEntityAliases); - querySpaces.AddAll(elementPersister.QuerySpaces); + entityPersisters.Add(nonScalarRtn.EntityPersister); + entityAliases.Add(nonScalarRtn.EntityAliases); + entityOwners.Add(ownerIndex); + querySpaces.AddAll(nonScalarRtn.EntityPersister.QuerySpaces); } - } - else if (rtn is EntityFetchReturn) - { - EntityFetchReturn fetchRtn = (EntityFetchReturn) rtn; - NonScalarReturn ownerDescriptor = fetchRtn.Owner; - int ownerIndex = nonScalarReturnList.IndexOf(ownerDescriptor); - entityowners.Add(ownerIndex); - lockmodes.Add(fetchRtn.LockMode); - IQueryable ownerPersister = DetermineAppropriateOwnerPersister(ownerDescriptor); - EntityType fetchedType = (EntityType) ownerPersister.GetPropertyType(fetchRtn.OwnerProperty); - string entityName = fetchedType.GetAssociatedEntityName(Factory); - IQueryable persister = (IQueryable) factory.GetEntityPersister(entityName); - entitypersisters.Add(persister); - nonScalarReturnList.Add(rtn); - specifiedAliases.Add(fetchRtn.Alias); - entityaliases.Add(fetchRtn.EntityAliases); - querySpaces.AddAll(persister.QuerySpaces); - } - else if (rtn is CollectionFetchReturn) - { - CollectionFetchReturn fetchRtn = (CollectionFetchReturn) rtn; - NonScalarReturn ownerDescriptor = fetchRtn.Owner; - int ownerIndex = nonScalarReturnList.IndexOf(ownerDescriptor); - collectionowners.Add(ownerIndex); - lockmodes.Add(fetchRtn.LockMode); - IQueryable ownerPersister = DetermineAppropriateOwnerPersister(ownerDescriptor); - string role = ownerPersister.EntityName + '.' + fetchRtn.OwnerProperty; - IQueryableCollection persister = (IQueryableCollection) factory.GetCollectionPersister(role); - collectionpersisters.Add(persister); - nonScalarReturnList.Add(rtn); - specifiedAliases.Add(fetchRtn.Alias); - collectionaliases.Add(fetchRtn.CollectionAliases); - // determine if the collection elements are entities... - IType elementType = persister.ElementType; - if (elementType.IsEntityType) + if (nonScalarRtn.CollectionPersister != null) { - IQueryable elementPersister = (IQueryable) ((EntityType) elementType).GetAssociatedJoinable(factory); - entitypersisters.Add(elementPersister); - entityowners.Add(ownerIndex); - entityaliases.Add(fetchRtn.ElementEntityAliases); - querySpaces.AddAll(elementPersister.QuerySpaces); + collectionPersisters.Add(nonScalarRtn.CollectionPersister); + collectionAliases.Add(nonScalarRtn.CollectionAliases); + collectionOwners.Add(ownerIndex); } + if (nonScalarRtn.Owner == null) + { + resultTypes.Add(nonScalarRtn.Type); + resultColumnProcessors.Add(new NonScalarResultColumnProcessor(returnableCounter++)); + } + + continue; } - else - { - throw new HibernateException("unexpected custom query return type : " + rtn.GetType().FullName); - } + + throw new HibernateException("unexpected custom query return type : " + rtn.GetType().FullName); } - entityPersisters = entitypersisters.ToArray(); - entityOwners = entityowners.ToArray(); - entityAliases = entityaliases.ToArray(); - collectionPersisters = collectionpersisters.ToArray(); - collectionOwners = collectionowners.ToArray(); - collectionAliases = collectionaliases.ToArray(); - lockModes = lockmodes.ToArray(); - resultTypes = resulttypes.ToArray(); - transformerAliases = specifiedAliases.ToArray(); - rowProcessor = new ResultRowProcessor(hasScalars, resultColumnProcessors.ToArray()); + this.entityPersisters = entityPersisters.Cast().ToArray(); + this.entityOwners = entityOwners.ToArray(); + this.entityAliases = entityAliases.ToArray(); + this.collectionPersisters = collectionPersisters.ToArray(); + this.collectionOwners = collectionOwners.ToArray(); + this.collectionAliases = collectionAliases.ToArray(); + this.lockModes = lockModes.ToArray(); + this.resultTypes = resultTypes.ToArray(); + this.transformerAliases = transformerAliases.ToArray(); + this.rowProcessor = new ResultRowProcessor(hasScalars, resultColumnProcessors.ToArray()); } public ISet QuerySpaces @@ -183,6 +126,10 @@ protected override int[] CollectionOwners get { return collectionOwners; } } + /// + /// An array of indexes of the entity that owns a one-to-one association + /// to the entity at the given index (-1 if there is no "owner") + /// protected override int[] Owners { get { return entityOwners; } @@ -203,49 +150,6 @@ protected override ICollectionAliases[] CollectionAliases get { return collectionAliases; } } - private IQueryable DetermineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) - { - string entityName = null; - RootReturn odrr = ownerDescriptor as RootReturn; - if (odrr != null) - { - entityName = odrr.EntityName; - } - else if (ownerDescriptor is CollectionReturn) - { - CollectionReturn collRtn = (CollectionReturn) ownerDescriptor; - string role = collRtn.OwnerEntityName + "." + collRtn.OwnerProperty; - ICollectionPersister persister = Factory.GetCollectionPersister(role); - EntityType ownerType = (EntityType) persister.ElementType; - entityName = ownerType.GetAssociatedEntityName(Factory); - } - else if (ownerDescriptor is FetchReturn) - { - FetchReturn fetchRtn = (FetchReturn) ownerDescriptor; - IQueryable persister = DetermineAppropriateOwnerPersister(fetchRtn.Owner); - IType ownerType = persister.GetPropertyType(fetchRtn.OwnerProperty); - if (ownerType.IsEntityType) - { - entityName = ((EntityType) ownerType).GetAssociatedEntityName(Factory); - } - else if (ownerType.IsCollectionType) - { - IType ownerCollectionElementType = ((CollectionType) ownerType).GetElementType(Factory); - if (ownerCollectionElementType.IsEntityType) - { - entityName = ((EntityType) ownerCollectionElementType).GetAssociatedEntityName(Factory); - } - } - } - - if (entityName == null) - { - throw new HibernateException("Could not determine fetch owner : " + ownerDescriptor); - } - - return (IQueryable) Factory.GetEntityPersister(entityName); - } - public override string QueryIdentifier { get { return sql.ToString(); } @@ -289,21 +193,16 @@ public override IList GetResultList(IList results, IResultTransformer resultTran // meant to handle dynamic instantiation queries...(Copy from QueryLoader) HolderInstantiator holderInstantiator = HolderInstantiator.GetHolderInstantiator(null, resultTransformer, ReturnAliasesForTransformer); - if (holderInstantiator.IsRequired) - { - for (int i = 0; i < results.Count; i++) - { - object[] row = (object[]) results[i]; - object result = holderInstantiator.Instantiate(row); - results[i] = result; - } + if (!holderInstantiator.IsRequired) return results; - return resultTransformer.TransformList(results); - } - else + for (int i = 0; i < results.Count; i++) { - return results; + object[] row = (object[]) results[i]; + object result = holderInstantiator.Instantiate(row); + results[i] = result; } + + return resultTransformer.TransformList(results); } protected override void AutoDiscoverTypes(IDataReader rs) diff --git a/src/NHibernate/Loader/Custom/EntityFetchReturn.cs b/src/NHibernate/Loader/Custom/EntityFetchReturn.cs deleted file mode 100644 index adce3caeca0..00000000000 --- a/src/NHibernate/Loader/Custom/EntityFetchReturn.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// Specifically a fetch return that refers to an entity association. - public class EntityFetchReturn : FetchReturn - { - private readonly IEntityAliases entityAliases; - - public EntityFetchReturn(string alias, IEntityAliases entityAliases, NonScalarReturn owner, string ownerProperty, - LockMode lockMode) : base(owner, ownerProperty, alias, lockMode) - { - this.entityAliases = entityAliases; - } - - public IEntityAliases EntityAliases - { - get { return entityAliases; } - } - } -} diff --git a/src/NHibernate/Loader/Custom/FetchReturn.cs b/src/NHibernate/Loader/Custom/FetchReturn.cs deleted file mode 100644 index 0f8e0d27526..00000000000 --- a/src/NHibernate/Loader/Custom/FetchReturn.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// Represents a return which names a fetched association. - public abstract class FetchReturn : NonScalarReturn - { - private readonly NonScalarReturn owner; - private readonly string ownerProperty; - - public FetchReturn(NonScalarReturn owner, string ownerProperty, string alias, LockMode lockMode) - : base(alias, lockMode) - { - this.owner = owner; - this.ownerProperty = ownerProperty; - } - - /// Retrieves the return descriptor for the owner of this fetch. - public NonScalarReturn Owner - { - get { return owner; } - } - - /// The name of the property on the owner which represents this association. - public string OwnerProperty - { - get { return ownerProperty; } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/GeneratedCollectionAliases.cs b/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs similarity index 78% rename from src/NHibernate/Loader/GeneratedCollectionAliases.cs rename to src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs index 3c17766500e..f1736bb9572 100644 --- a/src/NHibernate/Loader/GeneratedCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs @@ -2,7 +2,7 @@ using NHibernate.Persister.Collection; using NHibernate.Util; -namespace NHibernate.Loader +namespace NHibernate.Loader.Custom { /// /// CollectionAliases which handles the logic of selecting user provided aliases (via return-property), @@ -17,8 +17,7 @@ public class GeneratedCollectionAliases : ICollectionAliases private readonly string identifierAlias; private readonly IDictionary userProvidedAliases; - public GeneratedCollectionAliases(IDictionary userProvidedAliases, ICollectionPersister persister, - string suffix) + public GeneratedCollectionAliases(IDictionary userProvidedAliases, ICollectionPersister persister, string suffix) { this.suffix = suffix; this.userProvidedAliases = userProvidedAliases; @@ -41,16 +40,21 @@ public GeneratedCollectionAliases(ICollectionPersister persister, string str) private string[] GetUserProvidedCompositeElementAliases(string[] defaultAliases) { - var aliases = new List(); - foreach (KeyValuePair userProvidedAlias in userProvidedAliases) + if (userProvidedAliases != null) { - if (userProvidedAlias.Key.StartsWith("element.")) + var aliases = new List(); + foreach (var userProvidedAlias in userProvidedAliases) { - aliases.AddRange(userProvidedAlias.Value); + if (userProvidedAlias.Key.StartsWith("element.")) + { + aliases.AddRange(userProvidedAlias.Value); + } } + + if (aliases.Count > 0) return aliases.ToArray(); } - return aliases.Count > 0 ? aliases.ToArray() : defaultAliases; + return defaultAliases; } /// @@ -104,38 +108,23 @@ public override string ToString() private static string Join(IEnumerable aliases) { - if (aliases == null) - { - return null; - } - - return StringHelper.Join(", ", aliases); + return aliases == null ? null : StringHelper.Join(", ", aliases); } private string[] GetUserProvidedAliases(string propertyPath, string[] defaultAliases) { string[] result; - if (!userProvidedAliases.TryGetValue(propertyPath, out result)) - { - return defaultAliases; - } - else - { - return result; - } + return userProvidedAliases == null || !userProvidedAliases.TryGetValue(propertyPath, out result) + ? defaultAliases + : result; } private string GetUserProvidedAlias(string propertyPath, string defaultAlias) { string[] columns; - if (!userProvidedAliases.TryGetValue(propertyPath, out columns)) - { - return defaultAlias; - } - else - { - return columns[0]; - } + return userProvidedAliases == null || !userProvidedAliases.TryGetValue(propertyPath, out columns) + ? defaultAlias + : columns[0]; } } } diff --git a/src/NHibernate/Loader/Custom/IReturn.cs b/src/NHibernate/Loader/Custom/IReturn.cs index 94282ad4a87..321f18dd2d8 100644 --- a/src/NHibernate/Loader/Custom/IReturn.cs +++ b/src/NHibernate/Loader/Custom/IReturn.cs @@ -1,7 +1,11 @@ +using NHibernate.Type; + namespace NHibernate.Loader.Custom { /// Represents a return in a custom query. public interface IReturn { + string Alias { get; } + IType Type { get; } } } \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/NonScalarReturn.cs b/src/NHibernate/Loader/Custom/NonScalarReturn.cs index 0be5df5ee32..0ddb7f613dd 100644 --- a/src/NHibernate/Loader/Custom/NonScalarReturn.cs +++ b/src/NHibernate/Loader/Custom/NonScalarReturn.cs @@ -1,29 +1,106 @@ +using System; +using NHibernate.Loader.Custom.Sql; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; +using NHibernate.Type; + namespace NHibernate.Loader.Custom { /// Represents some non-scalar (entity/collection) return within the query result. - public abstract class NonScalarReturn : IReturn + internal class NonScalarReturn : IReturn { private readonly string alias; private readonly LockMode lockMode; + private readonly NonScalarReturn owner; + private readonly ISqlLoadable entityPersister; + private readonly IEntityAliases entityAliases; + private readonly ISqlLoadableCollection collectionPersister; + private readonly ICollectionAliases collectionAliases; + + public NonScalarReturn(SQLQueryContext context, bool queryHasAliases, string alias, LockMode lockMode) + : this(context, queryHasAliases, alias, lockMode, null) + {} - public NonScalarReturn(string alias, LockMode lockMode) + public NonScalarReturn(SQLQueryContext context, bool queryHasAliases, string alias, LockMode lockMode, NonScalarReturn owner) { - this.alias = alias; + if (context == null) + { + throw new ArgumentNullException("context"); + } if (string.IsNullOrEmpty(alias)) { throw new HibernateException("alias must be specified"); } + + this.alias = alias; this.lockMode = lockMode; + this.owner = owner; + + this.collectionPersister = context.GetCollectionPersister(alias); + if (this.collectionPersister != null) + { + var collectionPropertyResultMap = context.GetCollectionPropertyResultsMap(alias); + this.collectionAliases = queryHasAliases + ? new GeneratedCollectionAliases(collectionPropertyResultMap, this.collectionPersister, context.GetCollectionSuffix(alias)) + : (ICollectionAliases)new ColumnCollectionAliases(collectionPropertyResultMap, this.collectionPersister); + } + + if (this.collectionPersister == null || this.CollectionPersister.ElementType.IsEntityType) + { + this.entityPersister = context.GetEntityPersister(alias); + if (this.entityPersister != null) + { + var entityPropertyResultMap = context.GetEntityPropertyResultsMap(alias); + this.entityAliases = queryHasAliases + ? new DefaultEntityAliases(entityPropertyResultMap, this.entityPersister, context.GetEntitySuffix(alias)) + : new ColumnEntityAliases(entityPropertyResultMap, this.entityPersister); + } + } } public string Alias { - get { return alias; } + get { return this.alias; } } public LockMode LockMode { - get { return lockMode; } + get { return this.lockMode; } + } + + public NonScalarReturn Owner + { + get { return this.owner; } + } + + public ICollectionPersister CollectionPersister + { + get { return this.collectionPersister; } + } + + public ICollectionAliases CollectionAliases + { + get { return this.collectionAliases; } + } + + public ISqlLoadable EntityPersister + { + get { return this.entityPersister; } + } + + public IEntityAliases EntityAliases + { + get { return this.entityAliases; } + } + + public IType Type + { + get + { + if (this.collectionPersister != null) return this.collectionPersister.CollectionType; + if (this.entityPersister != null) return this.entityPersister.EntityMetamodel.EntityType; + return null; + } } } } \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/RootReturn.cs b/src/NHibernate/Loader/Custom/RootReturn.cs deleted file mode 100644 index cfd7f1286e4..00000000000 --- a/src/NHibernate/Loader/Custom/RootReturn.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// - /// Represents a return which names a "root" entity. - /// - /// - /// A root entity means it is explicitly a "column" in the result, as opposed to - /// a fetched association. - /// - public class RootReturn : NonScalarReturn - { - private readonly string entityName; - private readonly IEntityAliases entityAliases; - - public RootReturn(string alias, string entityName, IEntityAliases entityAliases, LockMode lockMode) - : base(alias, lockMode) - { - this.entityName = entityName; - this.entityAliases = entityAliases; - } - - public string EntityName - { - get { return entityName; } - } - - public IEntityAliases EntityAliases - { - get { return entityAliases; } - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/ScalarReturn.cs b/src/NHibernate/Loader/Custom/ScalarReturn.cs index 3f0bd10e706..6a9ad6337ac 100644 --- a/src/NHibernate/Loader/Custom/ScalarReturn.cs +++ b/src/NHibernate/Loader/Custom/ScalarReturn.cs @@ -3,7 +3,7 @@ namespace NHibernate.Loader.Custom { /// Represent a scalar (AKA simple value) return within a query result. - public class ScalarReturn : IReturn + internal class ScalarReturn : IReturn { private readonly IType type; private readonly string columnAlias; @@ -19,7 +19,7 @@ public IType Type get { return type; } } - public string ColumnAlias + public string Alias { get { return columnAlias; } } diff --git a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs index 328b571677a..9dba3181a38 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs @@ -1,14 +1,10 @@ using System.Collections.Generic; using System.Linq; using Iesi.Collections.Generic; - using NHibernate.Engine; using NHibernate.Engine.Query.Sql; using NHibernate.Param; -using NHibernate.Persister.Collection; -using NHibernate.Persister.Entity; using NHibernate.SqlCommand; -using NHibernate.Util; namespace NHibernate.Loader.Custom.Sql { @@ -18,7 +14,7 @@ public class SQLCustomQuery : ICustomQuery { private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof (SQLCustomQuery)); - private readonly List customQueryReturns = new List(); + private readonly List customQueryReturns; private readonly ISet querySpaces = new HashedSet(); private readonly SqlString sql; private readonly List parametersSpecifications; @@ -27,17 +23,16 @@ public SQLCustomQuery(INativeSQLQueryReturn[] queryReturns, string sqlQuery, ICo ISessionFactoryImplementor factory) { log.Debug("starting processing of sql query [" + sqlQuery + "]"); - SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(queryReturns, factory); - SQLQueryReturnProcessor.ResultAliasContext aliasContext = processor.Process(); + var processor = new SQLQueryContext(queryReturns, factory); - SQLQueryParser parser = new SQLQueryParser(factory, sqlQuery, new ParserContext(aliasContext)); - sql = parser.Process(); - ArrayHelper.AddAll(customQueryReturns, processor.GenerateCustomReturns(parser.QueryHasAliases)); - parametersSpecifications = parser.CollectedParametersSpecifications.ToList(); + var parser = new SQLQueryParser(factory, sqlQuery, processor); + this.sql = parser.Process(); + this.customQueryReturns = GenerateCustomReturns(queryReturns, parser.QueryHasAliases, processor).ToList(); + this.parametersSpecifications = parser.CollectedParametersSpecifications.ToList(); if (additionalQuerySpaces != null) { - querySpaces.AddAll(additionalQuerySpaces); + this.querySpaces.AddAll(additionalQuerySpaces); } } @@ -65,53 +60,39 @@ public IList CustomQueryReturns #endregion - private class ParserContext : SQLQueryParser.IParserContext - { - private readonly SQLQueryReturnProcessor.ResultAliasContext aliasContext; - - public ParserContext(SQLQueryReturnProcessor.ResultAliasContext aliasContext) - { - this.aliasContext = aliasContext; - } - - #region IParserContext Members - - public bool IsEntityAlias(string alias) - { - return GetEntityPersisterByAlias(alias) != null; - } + #region private methods - public ISqlLoadable GetEntityPersisterByAlias(string alias) - { - return aliasContext.GetEntityPersister(alias); - } - - public string GetEntitySuffixByAlias(string alias) - { - return aliasContext.GetEntitySuffix(alias); - } - - public bool IsCollectionAlias(string alias) - { - return GetCollectionPersisterByAlias(alias) != null; - } - - public ISqlLoadableCollection GetCollectionPersisterByAlias(string alias) - { - return aliasContext.GetCollectionPersister(alias); - } - - public string GetCollectionSuffixByAlias(string alias) - { - return aliasContext.GetCollectionSuffix(alias); - } + private static IEnumerable GenerateCustomReturns(IEnumerable queryReturns, bool queryHadAliases, SQLQueryContext context) + { + IDictionary customReturnsByAlias = new Dictionary(); - public IDictionary GetPropertyResultsMapByAlias(string alias) + foreach (var nativeRtn in queryReturns) { - return aliasContext.GetPropertyResultsMap(alias); + var nativeScalarRtn = nativeRtn as NativeSQLQueryScalarReturn; + if (nativeScalarRtn != null) + { + yield return new ScalarReturn(nativeScalarRtn.Type, nativeScalarRtn.ColumnAlias); + continue; + } + + var nativeJoinRtn = nativeRtn as NativeSQLQueryJoinReturn; + if (nativeJoinRtn != null) + { + var owner = customReturnsByAlias[nativeJoinRtn.OwnerAlias]; + var fetchReturn = new NonScalarReturn(context, queryHadAliases, nativeJoinRtn.Alias, nativeJoinRtn.LockMode, owner); + yield return customReturnsByAlias[fetchReturn.Alias] = fetchReturn; + continue; + } + + var nativeNonScalarRtn = nativeRtn as NativeSQLQueryNonScalarReturn; + if (nativeNonScalarRtn != null) + { + var nonFetchReturn = new NonScalarReturn(context, queryHadAliases, nativeNonScalarRtn.Alias, nativeNonScalarRtn.LockMode); + yield return customReturnsByAlias[nonFetchReturn.Alias] = nonFetchReturn; + } } - - #endregion } + + #endregion } } \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs new file mode 100644 index 00000000000..d7cf4038ef1 --- /dev/null +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs @@ -0,0 +1,285 @@ +using System.Collections.Generic; +using NHibernate.Engine; +using NHibernate.Engine.Query.Sql; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; +using NHibernate.Type; + +namespace NHibernate.Loader.Custom.Sql +{ + /// + /// Provides mappings from entity and collection aliases to persisters, suffixes + /// and custom property result maps. + /// + internal class SQLQueryContext + { + private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof (SQLQueryContext)); + + #region Instance fields + + private readonly Dictionary alias2Return = + new Dictionary(); + + private readonly Dictionary alias2OwnerAlias = new Dictionary(); + + private readonly Dictionary alias2EntityPersister = new Dictionary(); + private readonly Dictionary alias2EntitySuffix = new Dictionary(); + private readonly Dictionary> entityPropertyResultMaps = + new Dictionary>(); + + private readonly Dictionary alias2CollectionPersister = + new Dictionary(); + private readonly Dictionary alias2CollectionSuffix = new Dictionary(); + private readonly Dictionary> collectionPropertyResultMaps = + new Dictionary>(); + + private readonly ISessionFactoryImplementor factory; + + private int entitySuffixSeed; + private int collectionSuffixSeed; + + #endregion + + #region .ctor + + public SQLQueryContext(INativeSQLQueryReturn[] queryReturns, ISessionFactoryImplementor factory) + { + this.factory = factory; + + // first, break down the returns into maps keyed by alias + // so that role returns can be more easily resolved to their owners + foreach (INativeSQLQueryReturn rtn in queryReturns) + { + var nonScalarRtn = rtn as NativeSQLQueryNonScalarReturn; + if (nonScalarRtn != null) + { + alias2Return[nonScalarRtn.Alias] = rtn; + var joinRtn = rtn as NativeSQLQueryJoinReturn; + if (joinRtn != null) + { + alias2OwnerAlias[joinRtn.Alias] = joinRtn.OwnerAlias; + } + } + } + + // Now, process the returns + foreach (INativeSQLQueryReturn rtn in queryReturns) + { + ProcessReturn(rtn); + } + } + + #endregion + + #region ISQLQueryAliasContext implementation + + public bool IsEntityAlias(string alias) + { + return alias2EntityPersister.ContainsKey(alias); + } + + public ISqlLoadable GetEntityPersister(string alias) + { + ISqlLoadable result; + alias2EntityPersister.TryGetValue(alias, out result); + return result; + } + + public string GetEntitySuffix(string alias) + { + string result; + alias2EntitySuffix.TryGetValue(alias, out result); + return result; + } + + public IDictionary GetEntityPropertyResultsMap(string alias) + { + IDictionary result; + entityPropertyResultMaps.TryGetValue(alias, out result); + return result; + } + + public bool IsCollectionAlias(string alias) + { + return alias2CollectionPersister.ContainsKey(alias); + } + + public ISqlLoadableCollection GetCollectionPersister(string alias) + { + ISqlLoadableCollection result; + alias2CollectionPersister.TryGetValue(alias, out result); + return result; + } + + public string GetCollectionSuffix(string alias) + { + string result; + alias2CollectionSuffix.TryGetValue(alias, out result); + return result; + } + + public IDictionary GetCollectionPropertyResultsMap(string alias) + { + IDictionary result; + collectionPropertyResultMaps.TryGetValue(alias, out result); + return result; + } + + #endregion + + #region Private methods + + private ISqlLoadable GetSQLLoadable(string entityName) + { + IEntityPersister persister = factory.GetEntityPersister(entityName); + var persisterAsSqlLoadable = persister as ISqlLoadable; + if (persisterAsSqlLoadable == null) + { + throw new MappingException("class persister is not ISqlLoadable: " + entityName); + } + return persisterAsSqlLoadable; + } + + private string GenerateEntitySuffix() + { + return BasicLoader.GenerateSuffixes(entitySuffixSeed++, 1)[0]; + } + + private string GenerateCollectionSuffix() + { + return collectionSuffixSeed++ + "__"; + } + + private void ProcessReturn(INativeSQLQueryReturn rtn) + { + if (rtn is NativeSQLQueryScalarReturn) + { + ProcessScalarReturn((NativeSQLQueryScalarReturn) rtn); + } + else if (rtn is NativeSQLQueryRootReturn) + { + ProcessRootReturn((NativeSQLQueryRootReturn) rtn); + } + else if (rtn is NativeSQLQueryCollectionReturn) + { + ProcessCollectionReturn((NativeSQLQueryCollectionReturn) rtn); + } + else + { + ProcessJoinReturn((NativeSQLQueryJoinReturn) rtn); + } + } + + private void ProcessScalarReturn(NativeSQLQueryScalarReturn typeReturn) {} + + private void ProcessRootReturn(NativeSQLQueryRootReturn rootReturn) + { + if (alias2EntityPersister.ContainsKey(rootReturn.Alias)) + { + // already been processed... + return; + } + + ISqlLoadable persister = GetSQLLoadable(rootReturn.ReturnEntityName); + AddPersister(rootReturn.Alias, rootReturn.PropertyResultsMap, persister); + } + + private void AddPersister(string alias, IDictionary propertyResultMap, ISqlLoadable persister) + { + alias2EntityPersister[alias] = persister; + string suffix = GenerateEntitySuffix(); + log.Debug("mapping alias [" + alias + "] to entity-suffix [" + suffix + "]"); + alias2EntitySuffix[alias] = suffix; + if (propertyResultMap != null && propertyResultMap.Count > 0) + { + entityPropertyResultMaps[alias] = propertyResultMap; + } + } + + private void AddCollection(string role, string alias, IDictionary propertyResultMap) + { + ISqlLoadableCollection collectionPersister = (ISqlLoadableCollection) factory.GetCollectionPersister(role); + alias2CollectionPersister[alias] = collectionPersister; + string suffix = GenerateCollectionSuffix(); + log.Debug("mapping alias [" + alias + "] to collection-suffix [" + suffix + "]"); + alias2CollectionSuffix[alias] = suffix; + if (propertyResultMap != null && propertyResultMap.Count > 0) + { + collectionPropertyResultMaps[alias] = propertyResultMap; + } + + if (collectionPersister.IsOneToMany) + { + var persister = (ISqlLoadable)collectionPersister.ElementPersister; + AddPersister(alias, FilterElementProperties(propertyResultMap), persister); + } + } + + private IDictionary FilterElementProperties(IDictionary propertyResults) + { + const string PREFIX = "element."; + + var result = new Dictionary(propertyResults.Count); + foreach (KeyValuePair element in propertyResults) + { + string path = element.Key; + if (path.StartsWith(PREFIX)) + { + result[path.Substring(PREFIX.Length)] = element.Value; + } + } + + return result; + } + + private void ProcessCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) + { + // we are initializing an owned collection + string role = collectionReturn.OwnerEntityName + '.' + collectionReturn.OwnerProperty; + AddCollection(role, collectionReturn.Alias, collectionReturn.PropertyResultsMap); + } + + private void ProcessJoinReturn(NativeSQLQueryJoinReturn fetchReturn) + { + string alias = fetchReturn.Alias; + if (alias2EntityPersister.ContainsKey(alias) || alias2CollectionPersister.ContainsKey(alias)) + { + // already been processed... + return; + } + + string ownerAlias = fetchReturn.OwnerAlias; + + // Make sure the owner alias is known... + INativeSQLQueryReturn ownerReturn; + if (!alias2Return.TryGetValue(ownerAlias, out ownerReturn)) + { + throw new HibernateException(string.Format("Owner alias [{0}] is unknown for alias [{1}]", ownerAlias, alias)); + } + + // If this return's alias has not been processed yet, do so b4 further processing of this return + if (!alias2EntityPersister.ContainsKey(ownerAlias)) + { + ProcessReturn(ownerReturn); + } + + var ownerPersister = alias2EntityPersister[ownerAlias]; + var returnType = ownerPersister.GetPropertyType(fetchReturn.OwnerProperty); + + if (returnType.IsCollectionType) + { + string role = ownerPersister.EntityName + '.' + fetchReturn.OwnerProperty; + AddCollection(role, alias, fetchReturn.PropertyResultsMap); + } + else if (returnType.IsEntityType) + { + EntityType eType = (EntityType) returnType; + string returnEntityName = eType.GetAssociatedEntityName(); + ISqlLoadable persister = GetSQLLoadable(returnEntityName); + AddPersister(alias, fetchReturn.PropertyResultsMap, persister); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs index ecce1bcfe5b..092d8a28196 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs @@ -1,4 +1,3 @@ -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -11,27 +10,16 @@ namespace NHibernate.Loader.Custom.Sql { - public class SQLQueryParser + internal class SQLQueryParser { - public interface IParserContext - { - bool IsEntityAlias(string aliasName); - ISqlLoadable GetEntityPersisterByAlias(string alias); - string GetEntitySuffixByAlias(string alias); - bool IsCollectionAlias(string aliasName); - ISqlLoadableCollection GetCollectionPersisterByAlias(string alias); - string GetCollectionSuffixByAlias(string alias); - IDictionary GetPropertyResultsMapByAlias(string alias); - } - private readonly ISessionFactoryImplementor factory; private readonly string originalQueryString; - private readonly IParserContext context; + private readonly SQLQueryContext context; private long aliasesFound; private IEnumerable parametersSpecifications; - public SQLQueryParser(ISessionFactoryImplementor factory, string sqlQuery, IParserContext context) + public SQLQueryParser(ISessionFactoryImplementor factory, string sqlQuery, SQLQueryContext context) { this.factory = factory; originalQueryString = sqlQuery; @@ -53,7 +41,7 @@ public IEnumerable CollectedParametersSpecifications get { return parametersSpecifications; } } - // TODO: should "record" how many properties we have reffered to - and if we + // TODO: should "record" how many properties we have refered to - and if we // don't get'em'all we throw an exception! Way better than trial and error ;) private string SubstituteBrackets() { @@ -131,16 +119,16 @@ private string SubstituteBrackets() private string ResolveCollectionProperties(string aliasName, string propertyName) { - IDictionary fieldResults = context.GetPropertyResultsMapByAlias(aliasName); - ISqlLoadableCollection collectionPersister = context.GetCollectionPersisterByAlias(aliasName); - string collectionSuffix = context.GetCollectionSuffixByAlias(aliasName); + IDictionary fieldResults = context.GetCollectionPropertyResultsMap(aliasName); + ISqlLoadableCollection collectionPersister = context.GetCollectionPersister(aliasName); + string collectionSuffix = context.GetCollectionSuffix(aliasName); // NH Different behavior for NH-1612 if ("*".Equals(propertyName)) { - if (fieldResults.Count != 0) + if (fieldResults != null) { - throw new QueryException("Using return-propertys together with * syntax is not supported."); + throw new QueryException("Using return-property elements together with * syntax is not supported."); } string selectFragment = collectionPersister.SelectFragment(aliasName, collectionSuffix); @@ -161,7 +149,7 @@ private string ResolveCollectionProperties(string aliasName, string propertyName { return ResolveProperties(aliasName, elementPropertyName); } - else if (elementPropertyName == "*") + if (elementPropertyName == "*") { throw new QueryException("Using element.* syntax is only supported for entity elements."); } @@ -170,7 +158,7 @@ private string ResolveCollectionProperties(string aliasName, string propertyName string[] columnAliases; // Let return-propertys override whatever the persister has for aliases. - if (!fieldResults.TryGetValue(propertyName, out columnAliases)) + if (fieldResults == null || !fieldResults.TryGetValue(propertyName, out columnAliases)) { columnAliases = collectionPersister.GetCollectionPropertyColumnAliases(propertyName, collectionSuffix); } @@ -193,44 +181,43 @@ private string ResolveCollectionProperties(string aliasName, string propertyName private string ResolveProperties(string aliasName, string propertyName) { - IDictionary fieldResults = context.GetPropertyResultsMapByAlias(aliasName); - ISqlLoadable persister = context.GetEntityPersisterByAlias(aliasName); - string suffix = context.GetEntitySuffixByAlias(aliasName); + IDictionary fieldResults = context.GetEntityPropertyResultsMap(aliasName); + ISqlLoadable persister = context.GetEntityPersister(aliasName); + string suffix = context.GetEntitySuffix(aliasName); if ("*".Equals(propertyName)) { - if (fieldResults.Count != 0) + if (fieldResults != null) { - throw new QueryException("Using return-propertys together with * syntax is not supported."); + throw new QueryException("Using return-property elements together with * syntax is not supported."); } aliasesFound++; return persister.SelectFragment(aliasName, suffix); } - else - { - string[] columnAliases; - // Let return-propertys override whatever the persister has for aliases. - if (!fieldResults.TryGetValue(propertyName, out columnAliases)) - { - columnAliases = persister.GetSubclassPropertyColumnAliases(propertyName, suffix); - } + string[] columnAliases; - if (columnAliases == null || columnAliases.Length == 0) - { - throw new QueryException("No column name found for property [" + propertyName + "] for alias [" + aliasName + "]", - originalQueryString); - } - if (columnAliases.Length != 1) - { - // TODO: better error message since we actually support composites if names are explicitly listed. - throw new QueryException( - "SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " - + columnAliases.Length + " columns.", originalQueryString); - } - aliasesFound++; - return columnAliases[0]; + // Let return-property elements override whatever the persister has for aliases. + if (fieldResults == null || !fieldResults.TryGetValue(propertyName, out columnAliases)) + { + columnAliases = persister.GetSubclassPropertyColumnAliases(propertyName, suffix); + } + + if (columnAliases == null || columnAliases.Length == 0) + { + throw new QueryException("No column name found for property [" + propertyName + "] for alias [" + aliasName + "]", + originalQueryString); + } + if (columnAliases.Length != 1) + { + // TODO: better error message since we actually support composites if names are explicitly listed. + throw new QueryException( + "SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " + + columnAliases.Length + " columns.", originalQueryString); } + aliasesFound++; + + return columnAliases[0]; } /// diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs deleted file mode 100644 index f042816aaaa..00000000000 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs +++ /dev/null @@ -1,409 +0,0 @@ -using System.Collections; -using System.Collections.Generic; - -using NHibernate.Engine; -using NHibernate.Engine.Query.Sql; -using NHibernate.Persister.Collection; -using NHibernate.Persister.Entity; -using NHibernate.Type; - -namespace NHibernate.Loader.Custom.Sql -{ - public class SQLQueryReturnProcessor - { - private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof (SQLQueryReturnProcessor)); - - private readonly INativeSQLQueryReturn[] queryReturns; - - private readonly Dictionary alias2Return = - new Dictionary(); - - private readonly Dictionary alias2OwnerAlias = new Dictionary(); - - private readonly Dictionary alias2Persister = new Dictionary(); - private readonly Dictionary alias2Suffix = new Dictionary(); - - private readonly Dictionary alias2CollectionPersister = - new Dictionary(); - - private readonly Dictionary alias2CollectionSuffix = new Dictionary(); - - private readonly Dictionary> entityPropertyResultMaps = - new Dictionary>(); - - private readonly Dictionary> collectionPropertyResultMaps = - new Dictionary>(); - - private readonly ISessionFactoryImplementor factory; - - private int entitySuffixSeed = 0; - private int collectionSuffixSeed = 0; - - private ISessionFactoryImplementor Factory - { - get { return factory; } - } - - public SQLQueryReturnProcessor(INativeSQLQueryReturn[] queryReturns, ISessionFactoryImplementor factory) - { - this.queryReturns = queryReturns; - this.factory = factory; - } - - public class ResultAliasContext - { - private readonly SQLQueryReturnProcessor parent; - - public ResultAliasContext(SQLQueryReturnProcessor parent) - { - this.parent = parent; - } - - public ISqlLoadable GetEntityPersister(string alias) - { - ISqlLoadable result; - parent.alias2Persister.TryGetValue(alias, out result); - return result; - } - - public ISqlLoadableCollection GetCollectionPersister(string alias) - { - ISqlLoadableCollection result; - parent.alias2CollectionPersister.TryGetValue(alias, out result); - return result; - } - - public string GetEntitySuffix(string alias) - { - string result; - parent.alias2Suffix.TryGetValue(alias, out result); - return result; - } - - public string GetCollectionSuffix(string alias) - { - string result; - parent.alias2CollectionSuffix.TryGetValue(alias, out result); - return result; - } - - public string GetOwnerAlias(string alias) - { - string result; - parent.alias2OwnerAlias.TryGetValue(alias, out result); - return result; - } - - public IDictionary GetPropertyResultsMap(string alias) - { - return parent.InternalGetPropertyResultsMap(alias); - } - } - - private IDictionary InternalGetPropertyResultsMap(string alias) - { - NativeSQLQueryNonScalarReturn rtn = alias2Return[alias] as NativeSQLQueryNonScalarReturn; - return rtn != null ? rtn.PropertyResultsMap : null; - } - - private bool HasPropertyResultMap(string alias) - { - IDictionary propertyMaps = InternalGetPropertyResultsMap(alias); - return propertyMaps != null && propertyMaps.Count != 0; - } - - public ResultAliasContext Process() - { - // first, break down the returns into maps keyed by alias - // so that role returns can be more easily resolved to their owners - for (int i = 0; i < queryReturns.Length; i++) - { - var rtn = queryReturns[i] as NativeSQLQueryNonScalarReturn; - if (rtn != null) - { - alias2Return[rtn.Alias] = rtn; - var roleReturn = queryReturns[i] as NativeSQLQueryJoinReturn; - if (roleReturn != null) - { - alias2OwnerAlias[roleReturn.Alias] = roleReturn.OwnerAlias; - } - } - } - - // Now, process the returns - for (int i = 0; i < queryReturns.Length; i++) - { - ProcessReturn(queryReturns[i]); - } - - return new ResultAliasContext(this); - } - - private ISqlLoadable GetSQLLoadable(string entityName) - { - IEntityPersister persister = factory.GetEntityPersister(entityName); - var persisterAsSqlLoadable = persister as ISqlLoadable; - if (persisterAsSqlLoadable == null) - { - throw new MappingException("class persister is not ISqlLoadable: " + entityName); - } - return persisterAsSqlLoadable; - } - - private string GenerateEntitySuffix() - { - return BasicLoader.GenerateSuffixes(entitySuffixSeed++, 1)[0]; - } - - private string GenerateCollectionSuffix() - { - return collectionSuffixSeed++ + "__"; - } - - private void ProcessReturn(INativeSQLQueryReturn rtn) - { - if (rtn is NativeSQLQueryScalarReturn) - { - ProcessScalarReturn((NativeSQLQueryScalarReturn) rtn); - } - else if (rtn is NativeSQLQueryRootReturn) - { - ProcessRootReturn((NativeSQLQueryRootReturn) rtn); - } - else if (rtn is NativeSQLQueryCollectionReturn) - { - ProcessCollectionReturn((NativeSQLQueryCollectionReturn) rtn); - } - else - { - ProcessJoinReturn((NativeSQLQueryJoinReturn) rtn); - } - } - - private void ProcessScalarReturn(NativeSQLQueryScalarReturn typeReturn) {} - - private void ProcessRootReturn(NativeSQLQueryRootReturn rootReturn) - { - if (alias2Persister.ContainsKey(rootReturn.Alias)) - { - // already been processed... - return; - } - - ISqlLoadable persister = GetSQLLoadable(rootReturn.ReturnEntityName); - AddPersister(rootReturn.Alias, rootReturn.PropertyResultsMap, persister); - } - - private void AddPersister(string alias, IDictionary propertyResult, ISqlLoadable persister) - { - alias2Persister[alias] = persister; - string suffix = GenerateEntitySuffix(); - log.Debug("mapping alias [" + alias + "] to entity-suffix [" + suffix + "]"); - alias2Suffix[alias] = suffix; - entityPropertyResultMaps[alias] = propertyResult; - } - - private void AddCollection(string role, string alias, IDictionary propertyResults) - { - ISqlLoadableCollection collectionPersister = (ISqlLoadableCollection) Factory.GetCollectionPersister(role); - alias2CollectionPersister[alias] = collectionPersister; - string suffix = GenerateCollectionSuffix(); - log.Debug("mapping alias [" + alias + "] to collection-suffix [" + suffix + "]"); - alias2CollectionSuffix[alias] = suffix; - collectionPropertyResultMaps[alias] = propertyResults; - - if (collectionPersister.IsOneToMany) - { - ISqlLoadable persister = (ISqlLoadable) collectionPersister.ElementPersister; - AddPersister(alias, Filter(propertyResults), persister); - } - } - - private IDictionary Filter(IDictionary propertyResults) - { - Dictionary result = new Dictionary(propertyResults.Count); - - string keyPrefix = "element."; - - foreach (KeyValuePair element in propertyResults) - { - string path = element.Key; - if (path.StartsWith(keyPrefix)) - { - result[path.Substring(keyPrefix.Length)] = element.Value; - } - } - - return result; - } - - private void ProcessCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) - { - // we are initializing an owned collection - string role = collectionReturn.OwnerEntityName + '.' + collectionReturn.OwnerProperty; - AddCollection(role, collectionReturn.Alias, collectionReturn.PropertyResultsMap); - } - - private void ProcessJoinReturn(NativeSQLQueryJoinReturn fetchReturn) - { - string alias = fetchReturn.Alias; - if (alias2Persister.ContainsKey(alias) || alias2CollectionPersister.ContainsKey(alias)) - { - // already been processed... - return; - } - - string ownerAlias = fetchReturn.OwnerAlias; - - // Make sure the owner alias is known... - if (!alias2Return.ContainsKey(ownerAlias)) - { - throw new HibernateException(string.Format("Owner alias [{0}] is unknown for alias [{1}]", ownerAlias, alias)); - } - - // If this return's alias has not been processed yet, do so b4 further processing of this return - if (!alias2Persister.ContainsKey(ownerAlias)) - { - ProcessReturn(alias2Return[ownerAlias]); - } - - ISqlLoadable ownerPersister = alias2Persister[ownerAlias]; - IType returnType = ownerPersister.GetPropertyType(fetchReturn.OwnerProperty); - - if (returnType.IsCollectionType) - { - string role = ownerPersister.EntityName + '.' + fetchReturn.OwnerProperty; - AddCollection(role, alias, fetchReturn.PropertyResultsMap); - } - else if (returnType.IsEntityType) - { - EntityType eType = (EntityType) returnType; - string returnEntityName = eType.GetAssociatedEntityName(); - ISqlLoadable persister = GetSQLLoadable(returnEntityName); - AddPersister(alias, fetchReturn.PropertyResultsMap, persister); - } - } - - public IList GenerateCustomReturns(bool queryHadAliases) - { - IList customReturns = new List(); - IDictionary customReturnsByAlias = new Dictionary(); - for (int i = 0; i < queryReturns.Length; i++) - { - if (queryReturns[i] is NativeSQLQueryScalarReturn) - { - NativeSQLQueryScalarReturn rtn = (NativeSQLQueryScalarReturn) queryReturns[i]; - customReturns.Add(new ScalarReturn(rtn.Type, rtn.ColumnAlias)); - } - else if (queryReturns[i] is NativeSQLQueryRootReturn) - { - NativeSQLQueryRootReturn rtn = (NativeSQLQueryRootReturn) queryReturns[i]; - string alias = rtn.Alias; - IEntityAliases entityAliases; - if (queryHadAliases || HasPropertyResultMap(alias)) - { - entityAliases = - new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - else - { - entityAliases = - new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - RootReturn customReturn = new RootReturn(alias, rtn.ReturnEntityName, entityAliases, rtn.LockMode); - customReturns.Add(customReturn); - customReturnsByAlias[rtn.Alias] = customReturn; - } - else if (queryReturns[i] is NativeSQLQueryCollectionReturn) - { - NativeSQLQueryCollectionReturn rtn = (NativeSQLQueryCollectionReturn) queryReturns[i]; - string alias = rtn.Alias; - ISqlLoadableCollection persister = alias2CollectionPersister[alias]; - bool isEntityElements = persister.ElementType.IsEntityType; - ICollectionAliases collectionAliases; - IEntityAliases elementEntityAliases = null; - if (queryHadAliases || HasPropertyResultMap(alias)) - { - collectionAliases = - new GeneratedCollectionAliases(collectionPropertyResultMaps[alias], alias2CollectionPersister[alias], - alias2CollectionSuffix[alias]); - if (isEntityElements) - { - elementEntityAliases = - new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - } - else - { - collectionAliases = - new ColumnCollectionAliases(collectionPropertyResultMaps[alias], alias2CollectionPersister[alias]); - if (isEntityElements) - { - elementEntityAliases = - new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - } - CollectionReturn customReturn = - new CollectionReturn(alias, rtn.OwnerEntityName, rtn.OwnerProperty, collectionAliases, elementEntityAliases, - rtn.LockMode); - customReturns.Add(customReturn); - customReturnsByAlias[rtn.Alias] = customReturn; - } - else if (queryReturns[i] is NativeSQLQueryJoinReturn) - { - NativeSQLQueryJoinReturn rtn = (NativeSQLQueryJoinReturn) queryReturns[i]; - string alias = rtn.Alias; - FetchReturn customReturn; - NonScalarReturn ownerCustomReturn = (NonScalarReturn) customReturnsByAlias[rtn.OwnerAlias]; - ISqlLoadableCollection persister; - if (alias2CollectionPersister.TryGetValue(alias, out persister)) - { - bool isEntityElements = persister.ElementType.IsEntityType; - ICollectionAliases collectionAliases; - IEntityAliases elementEntityAliases = null; - if (queryHadAliases || HasPropertyResultMap(alias)) - { - collectionAliases = - new GeneratedCollectionAliases(collectionPropertyResultMaps[alias], persister, alias2CollectionSuffix[alias]); - if (isEntityElements) - { - elementEntityAliases = - new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - } - else - { - collectionAliases = new ColumnCollectionAliases(collectionPropertyResultMaps[alias], persister); - if (isEntityElements) - { - elementEntityAliases = - new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - } - customReturn = - new CollectionFetchReturn(alias, ownerCustomReturn, rtn.OwnerProperty, collectionAliases, elementEntityAliases, - rtn.LockMode); - } - else - { - IEntityAliases entityAliases; - if (queryHadAliases || HasPropertyResultMap(alias)) - { - entityAliases = - new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - else - { - entityAliases = - new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]); - } - customReturn = new EntityFetchReturn(alias, entityAliases, ownerCustomReturn, rtn.OwnerProperty, rtn.LockMode); - } - customReturns.Add(customReturn); - customReturnsByAlias[alias] = customReturn; - } - } - return customReturns; - } - } -} \ No newline at end of file diff --git a/src/NHibernate/Loader/DefaultEntityAliases.cs b/src/NHibernate/Loader/DefaultEntityAliases.cs index 29f6ee16a83..52b57f65272 100644 --- a/src/NHibernate/Loader/DefaultEntityAliases.cs +++ b/src/NHibernate/Loader/DefaultEntityAliases.cs @@ -20,15 +20,13 @@ public class DefaultEntityAliases : IEntityAliases private readonly IDictionary userProvidedAliases; public DefaultEntityAliases(ILoadable persister, string suffix) - : this(new CollectionHelper.EmptyMapClass(), persister, suffix) {} + : this(null, persister, suffix) {} /// /// Calculate and cache select-clause suffixes. /// public DefaultEntityAliases(IDictionary userProvidedAliases, ILoadable persister, string suffix) { - ValidateUserProvidedAliases(userProvidedAliases, persister); - this.suffix = suffix; this.userProvidedAliases = userProvidedAliases; @@ -51,21 +49,6 @@ public DefaultEntityAliases(IDictionary userProvidedAliases, I rowIdAlias = Loadable.RowIdAlias + suffix; // TODO: not visible to the user! } - private static void ValidateUserProvidedAliases(IDictionary userProvidedAliases, ILoadable persister) - { - if (userProvidedAliases != null && userProvidedAliases.Count > 0) - { - var missingPropertyNames = persister.PropertyNames.Except(userProvidedAliases.Keys).ToArray(); - if (missingPropertyNames.Length > 0) - { - throw new MappingException( - string.Format( - "User provided resulset mapping for entity '{0}' misses mappings for the following properties: {1}.", - persister.EntityName, string.Join(", ", missingPropertyNames))); - } - } - } - protected virtual string GetDiscriminatorAlias(ILoadable persister, string suffix) { return persister.GetDiscriminatorAlias(suffix); @@ -84,34 +67,21 @@ protected virtual string[] GetPropertyAliases(ILoadable persister, int j) private string[] GetUserProvidedAliases(string propertyPath, string[] defaultAliases) { string[] result = propertyPath == null ? null : GetUserProvidedAlias(propertyPath); - if (result == null) - { - return defaultAliases; - } - else - { - return result; - } + return result ?? defaultAliases; } private string[] GetUserProvidedAlias(string propertyPath) { string[] result; - userProvidedAliases.TryGetValue(propertyPath, out result); - return result; + return userProvidedAliases != null && userProvidedAliases.TryGetValue(propertyPath, out result) + ? result + : null; } private string GetUserProvidedAlias(string propertyPath, string defaultAlias) { string[] columns = propertyPath == null ? null : GetUserProvidedAlias(propertyPath); - if (columns == null) - { - return defaultAlias; - } - else - { - return columns[0]; - } + return columns == null ? defaultAlias : columns[0]; } public string[][] GetSuffixedPropertyAliases(ILoadable persister) diff --git a/src/NHibernate/NHibernate.csproj b/src/NHibernate/NHibernate.csproj index d51cb8e08f3..c9596691604 100644 --- a/src/NHibernate/NHibernate.csproj +++ b/src/NHibernate/NHibernate.csproj @@ -322,6 +322,7 @@ + @@ -1637,21 +1638,15 @@ - - - - - - - + @@ -1659,7 +1654,7 @@ - + From 62b99462bed351b4fa9929ba6a752bf309d351c6 Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Wed, 24 Oct 2012 13:59:09 +0200 Subject: [PATCH 6/9] Refactored some custom SQL tests and added test case for mixed use of column names and manual aliases in custom SQL queries. --- .../SqlTest/Query/NativeSQLQueries.hbm.xml | 91 +++++++------ .../SqlTest/Query/NativeSQLQueriesFixture.cs | 121 +++++++++++------- 2 files changed, 127 insertions(+), 85 deletions(-) diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml index 993b7fa1be1..42799492536 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml @@ -27,7 +27,6 @@ - @@ -116,7 +115,6 @@ - @@ -204,56 +202,75 @@ ORDER BY STARTDATE ASC, EMPLOYEE ASC - + - - + + - - - - - - - - + + + + + + + + - - + + - SELECT org.ORGID as orgid, - org.NAME as name, - emp.EMPLOYER as employer, - emp.EMPID as empid, - emp.EMPLOYEE as employee, - emp.EMPLOYER as employer, - emp.STARTDATE as xstartDate, - emp.ENDDATE as endDate, - emp.REGIONCODE as regionCode, - emp.AVALUE as AVALUE, - emp.CURRENCY as CURRENCY + SELECT org.ORGID as org_id, + org.NAME as org_name, + emp.EMPLOYER as emp_employer, + emp.EMPID as emp_id, + emp.EMPLOYEE as emp_employee, + emp.STARTDATE as emp_startDate, + emp.ENDDATE as emp_endDate, + emp.REGIONCODE as emp_regionCode, + emp.AVALUE as emp_avalue, + emp.CURRENCY as emp_currency FROM ORGANIZATION org LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER - - + - SELECT org.ORGID as orgid, - org.NAME as name, - emp.EMPLOYER as employer, - emp.EMPID as empid, - emp.EMPLOYEE as employee, - emp.EMPLOYER as employer, - emp.STARTDATE as startDate, - emp.ENDDATE as endDate, - emp.REGIONCODE as regionCode, + SELECT org.ORGID as ORGID, + org.NAME as NAME, + emp.EMPLOYER as EMPLOYER, + emp.EMPID as EMPID, + emp.EMPLOYEE as EMPLOYEE, + emp.STARTDATE as STARTDATE, + emp.ENDDATE as ENDDATE, + emp.REGIONCODE as REGIONCODE, emp.AVALUE as AVALUE, emp.CURRENCY as CURRENCY FROM ORGANIZATION org LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER + + + + + + + + + + SELECT org.ORGID as ORGANISATION_ID, + org.NAME as NAME, + emp.EMPID as EMPLOYMENT_ID, + emp.EMPLOYEE as EMPLOYEE, + emp.EMPLOYER as EMPLOYER, + emp.STARTDATE as STARTDATE, + emp.ENDDATE as ENDDATE, + emp.REGIONCODE as REGIONCODE, + emp.AVALUE as AVALUE, + emp.CURRENCY as CURRENCY + FROM ORGANIZATION org + LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER + diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs index 88bd7f63d0d..89b4970a78e 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -1,29 +1,14 @@ +using System; using System.Collections; +using NHibernate.Criterion; using NHibernate.Transform; using NUnit.Framework; -using NHibernate.Criterion; namespace NHibernate.Test.SqlTest.Query { [TestFixture] public class GeneralTest : TestCase { - protected const string OrganizationFetchJoinEmploymentSQL = - "SELECT org.ORGID as {org.id}, " + - " org.NAME as {org.name}, " + - " emp.EMPLOYER as {emp.key}, " + - " emp.EMPID as {emp.element}, " + - " {emp.element.*} " + - "FROM ORGANIZATION org " + - " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; - - protected const string OrganizationJoinEmploymentSQL = - "SELECT org.ORGID as {org.id}, " + - " org.NAME as {org.name}, " + - " {emp.*} " + - "FROM ORGANIZATION org " + - " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; - protected const string EmploymentSQL = "SELECT * FROM EMPLOYMENT"; protected string EmploymentSQLMixedScalarEntity = @@ -452,9 +437,9 @@ public void AutoDetectAliasing() // TODO H3: H3.2 can guess the return column type so they can use just addScalar("employerid"), // but NHibernate currently can't do it. - list = - s.CreateSQLQuery(EmploymentSQLMixedScalarEntity).AddScalar("employerid", NHibernateUtil.Int64).AddEntity( - typeof(Employment)).List(); + list = s.CreateSQLQuery(EmploymentSQLMixedScalarEntity) + .AddScalar("employerid", NHibernateUtil.Int64) + .AddEntity(typeof(Employment)).List(); Assert.AreEqual(1, list.Count); o = (object[]) list[0]; Assert.AreEqual(2, o.Length); @@ -467,34 +452,6 @@ public void AutoDetectAliasing() list = queryWithCollection.List(); Assert.AreEqual(list.Count, 1); - s.Clear(); - - list = s.CreateSQLQuery(OrganizationJoinEmploymentSQL) - .AddEntity("org", typeof(Organization)) - .AddJoin("emp", "org.employments") - .List(); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - list = s.CreateSQLQuery(OrganizationFetchJoinEmploymentSQL) - .AddEntity("org", typeof(Organization)) - .AddJoin("emp", "org.employments") - .List(); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - // TODO : why twice? - s.GetNamedQuery("organizationreturnproperty").List(); - list = s.GetNamedQuery("organizationreturnproperty").List(); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - list = s.GetNamedQuery("organizationautodetect").List(); - Assert.AreEqual(2, list.Count); - t.Commit(); s.Close(); @@ -538,6 +495,74 @@ public void AutoDetectAliasing() s.Close(); } + public void CanQueryWithGeneratedAliasesOnly_UsingWildcard() + { + const string SQL = + "SELECT org.ORGID as {org.id}, " + + " org.NAME as {org.name}, " + + " {emp.*} " + + "FROM ORGANIZATION org " + + " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; + + VerifyOrganisationQuery(session => session.CreateSQLQuery(SQL) + .AddEntity("org", typeof(Organization)) + .AddJoin("emp", "org.employments")); + } + + [Test] + public void CanQueryWithGeneratedAliasesOnly_UsingCollectionElementWildcard() + { + const string SQL = + "SELECT org.ORGID as {org.id}, " + + " org.NAME as {org.name}, " + + " emp.EMPLOYER as {emp.key}, " + + " emp.EMPID as {emp.element}, " + + " {emp.element.*} " + + "FROM ORGANIZATION org " + + " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER"; + + VerifyOrganisationQuery(session => session.CreateSQLQuery(SQL) + .AddEntity("org", typeof(Organization)) + .AddJoin("emp", "org.employments")); + } + + [Test] + public void CanQueryWithColumnNamesOnly() + { + VerifyOrganisationQuery(session => session.GetNamedQuery("organization-using-manual-aliases")); + } + + [Test] + public void CanQueryWithManualAliasesOnly() + { + VerifyOrganisationQuery(session => session.GetNamedQuery("organization-using-column-names")); + } + + [Test] + public void CanQueryWithMixOfColumnNamesAndManualAliases() + { + VerifyOrganisationQuery(session => session.GetNamedQuery("organization-using-column-names-and-manual-aliases")); + } + + private void VerifyOrganisationQuery(Func queryFactory, string message = null) + { + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + var ifa = new Organization("IFA"); + var jboss = new Organization("JBoss"); + var gavin = new Person("Gavin"); + var emp = new Employment(gavin, jboss, "AU"); + s.Save(jboss); + s.Save(ifa); + s.Save(gavin); + s.Save(emp); + + var list = queryFactory(s).List(); + Assert.AreEqual(2, list.Count, message); + } + } + [Test] public void MixAndMatchEntityScalar() { From c2d4de76ceb51ec22b61d6e7b54d7dd2e6db3311 Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Wed, 24 Oct 2012 13:59:45 +0200 Subject: [PATCH 7/9] Removing unused code. --- src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs | 4 +--- src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs b/src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs index fdc372f7e10..e99f4afcfa2 100644 --- a/src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs +++ b/src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs @@ -22,8 +22,6 @@ namespace NHibernate.Engine.Query [Serializable] public class NativeSQLQueryPlan { - private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(NativeSQLQueryPlan)); - private readonly string sourceQuery; private readonly SQLCustomQuery customQuery; @@ -119,7 +117,7 @@ public int PerformExecuteUpdate(QueryParameters queryParameters, ISessionImpleme private SqlString ExpandDynamicFilterParameters(SqlString sqlString, ICollection parameterSpecs, ISessionImplementor session) { var enabledFilters = session.EnabledFilters; - if (enabledFilters.Count == 0 || sqlString.ToString().IndexOf(ParserHelper.HqlVariablePrefix) < 0) + if (enabledFilters.Count == 0 || sqlString.IndexOf(ParserHelper.HqlVariablePrefix, 0, sqlString.Length, StringComparison.Ordinal) < 0) { return sqlString; } diff --git a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs index 108d950ed79..4d45c6551c7 100644 --- a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs @@ -23,7 +23,6 @@ public ColumnCollectionAliases(IDictionary userProvidedAliases this.keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); this.indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); - this.elementAliases = GetUserProvidedAliases("element", persister.ElementColumnNames); // NH-1612: Add aliases for all composite element properties to support access // to individual composite element properties in elements. From 1878ce430eb55b59267bb9992eecab9be1cf5a7d Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Wed, 24 Oct 2012 15:09:58 +0200 Subject: [PATCH 8/9] Optional parameter in test breaks .NET 3.5 build --- src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs index 89b4970a78e..9a2ee3bd943 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -544,7 +544,7 @@ public void CanQueryWithMixOfColumnNamesAndManualAliases() VerifyOrganisationQuery(session => session.GetNamedQuery("organization-using-column-names-and-manual-aliases")); } - private void VerifyOrganisationQuery(Func queryFactory, string message = null) + private void VerifyOrganisationQuery(Func queryFactory) { using (ISession s = OpenSession()) using (ITransaction t = s.BeginTransaction()) @@ -559,7 +559,7 @@ private void VerifyOrganisationQuery(Func queryFactory, string s.Save(emp); var list = queryFactory(s).List(); - Assert.AreEqual(2, list.Count, message); + Assert.AreEqual(2, list.Count); } } From 7541a8e111582a5399a9da5c81dba6ce0c91ede2 Mon Sep 17 00:00:00 2001 From: Gerke Geurts Date: Fri, 11 Jan 2013 14:34:39 +0100 Subject: [PATCH 9/9] Fixed named query usage in NativeSqlQueriesFixture. --- src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs index 9a2ee3bd943..538f2d8eb00 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -280,7 +280,7 @@ public void MappedAliasStrategy() s = OpenSession(); t = s.BeginTransaction(); - sqlQuery = s.GetNamedQuery("organizationreturnproperty"); + sqlQuery = s.GetNamedQuery("organization-using-manual-aliases"); sqlQuery.SetResultTransformer(CriteriaSpecification.AliasToEntityMap); list = sqlQuery.List(); Assert.AreEqual(2, list.Count);