diff --git a/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs index 33f0f9b318d..4dcafbfea68 100644 --- a/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -8,34 +8,20 @@ //------------------------------------------------------------------------------ +using System; using System.Collections; using System.Linq; +using NHibernate.Criterion; using NHibernate.Transform; using NUnit.Framework; -using NHibernate.Criterion; namespace NHibernate.Test.SqlTest.Query { using System.Threading.Tasks; + using System.Threading; [TestFixture] public class GeneralTestAsync : 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 = @@ -306,24 +292,6 @@ public async Task MappedAliasStrategyAsync() await (t.CommitAsync()); s.Close(); - if (TestDialect.SupportsDuplicatedColumnAliases) - { - s = OpenSession(); - t = s.BeginTransaction(); - sqlQuery = s.GetNamedQuery("organizationreturnproperty"); - sqlQuery.SetResultTransformer(CriteriaSpecification.AliasToEntityMap); - list = await (sqlQuery.ListAsync()); - Assert.AreEqual(2, list.Count); - m = (IDictionary) list[0]; - Assert.IsTrue(m.Contains("org")); - AssertClassAssignability(m["org"].GetType(), typeof(Organization)); - Assert.IsTrue(m.Contains("emp")); - AssertClassAssignability(m["emp"].GetType(), typeof(Employment)); - Assert.AreEqual(2, m.Count); - await (t.CommitAsync()); - s.Close(); - } - s = OpenSession(); t = s.BeginTransaction(); namedQuery = s.GetNamedQuery("EmploymentAndPerson"); @@ -469,9 +437,9 @@ public async Task AutoDetectAliasingAsync() // 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 = - await (s.CreateSQLQuery(EmploymentSQLMixedScalarEntity).AddScalar("employerid", NHibernateUtil.Int64).AddEntity( - typeof(Employment)).ListAsync()); + list = await (s.CreateSQLQuery(EmploymentSQLMixedScalarEntity) + .AddScalar("employerid", NHibernateUtil.Int64) + .AddEntity(typeof(Employment)).ListAsync()); Assert.AreEqual(1, list.Count); o = (object[]) list[0]; Assert.AreEqual(2, o.Length); @@ -484,37 +452,6 @@ public async Task AutoDetectAliasingAsync() list = await (queryWithCollection.ListAsync()); Assert.AreEqual(list.Count, 1); - s.Clear(); - - list = await (s.CreateSQLQuery(OrganizationJoinEmploymentSQL) - .AddEntity("org", typeof(Organization)) - .AddJoin("emp", "org.employments") - .ListAsync()); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - list = await (s.CreateSQLQuery(OrganizationFetchJoinEmploymentSQL) - .AddEntity("org", typeof(Organization)) - .AddJoin("emp", "org.employments") - .ListAsync()); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - if (TestDialect.SupportsDuplicatedColumnAliases) - { - // TODO : why twice? - await (s.GetNamedQuery("organizationreturnproperty").ListAsync()); - list = await (s.GetNamedQuery("organizationreturnproperty").ListAsync()); - Assert.AreEqual(2, list.Count); - - s.Clear(); - - list = await (s.GetNamedQuery("organizationautodetect").ListAsync()); - Assert.AreEqual(2, list.Count); - } - await (t.CommitAsync()); s.Close(); @@ -558,6 +495,106 @@ public async Task AutoDetectAliasingAsync() s.Close(); } + public Task CanQueryWithGeneratedAliasesOnly_UsingWildcardAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + 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"; + + return VerifyOrganisationQueryAsync(session => session.CreateSQLQuery(SQL) + .AddEntity("org", typeof(Organization)) + .AddJoin("emp", "org.employments"), cancellationToken); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + [Test] + public async Task CanQueryWithGeneratedAliasesOnly_UsingCollectionElementWildcardAsync() + { + 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"; + + await (VerifyOrganisationQueryAsync(session => session.CreateSQLQuery(SQL) + .AddEntity("org", typeof(Organization)) + .AddJoin("emp", "org.employments"))); + } + + [Test] + public async Task CanQueryWithColumnNamesOnlyAsync() + { + await (VerifyOrganisationQueryAsync(session => session.GetNamedQuery("organization-using-manual-aliases"))); + } + + [Test] + public async Task CanQueryWithManualAliasesOnlyAsync() + { + await (VerifyOrganisationQueryAsync(session => session.GetNamedQuery("organization-using-column-names"))); + } + + [Test] + public async Task CanQueryWithMixOfColumnNamesAndManualAliasesAsync() + { + await (VerifyOrganisationQueryAsync(session => session.GetNamedQuery("organization-using-column-names-and-manual-aliases"))); + } + + private async Task VerifyOrganisationQueryAsync(Func queryFactory, CancellationToken cancellationToken = default(CancellationToken)) + { + 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"); + await (s.SaveAsync(jboss, cancellationToken)); + await (s.SaveAsync(ifa, cancellationToken)); + await (s.SaveAsync(gavin, cancellationToken)); + await (s.SaveAsync(emp, cancellationToken)); + + var list = await (queryFactory(s).ListAsync(cancellationToken)); + Assert.AreEqual(2, list.Count); + } + } + + [Test] + public async Task CanQueryWithManualComponentPropertyAliasesAsync() + { + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + SpaceShip enterprise = new SpaceShip(); + enterprise.Model = "USS"; + enterprise.Name = "Entreprise"; + enterprise.Speed = 50d; + Dimension d = new Dimension(45, 10); + enterprise.Dimensions = d; + await (s.SaveAsync(enterprise)); + + await (s.FlushAsync()); + s.Clear(); + + object[] result = (object[])await (s.GetNamedQuery("spaceship").UniqueResultAsync()); + enterprise = (SpaceShip)result[0]; + Assert.AreEqual(50d, enterprise.Speed, "Speed"); + Assert.AreEqual(45, enterprise.Dimensions.Length, "Dimensions.Length"); + Assert.AreEqual(10, enterprise.Dimensions.Width, "Dimensions.Width"); + } + } + [Test] public async Task MixAndMatchEntityScalarAsync() { diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml index 08590d63b2a..929bd86fa7d 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueries.hbm.xml @@ -1,13 +1,13 @@  + namespace="NHibernate.Test.SqlTest" + assembly="NHibernate.Test"> @@ -27,7 +27,6 @@ - @@ -116,7 +115,6 @@ - @@ -149,14 +147,14 @@ fld_name as name, fld_model as model, fld_speed as speed, - fld_length as flength, + fld_length as length, fld_width as width, fld_length * fld_width as surface, fld_length * fld_width *10 as volume from SpaceShip - + SELECT org.NAME FROM ORGANIZATION org @@ -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 e89bd35775f..d59cf3ebfa2 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -1,30 +1,15 @@ +using System; using System.Collections; using System.Linq; +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 = @@ -295,24 +280,6 @@ public void MappedAliasStrategy() t.Commit(); s.Close(); - if (TestDialect.SupportsDuplicatedColumnAliases) - { - s = OpenSession(); - t = s.BeginTransaction(); - sqlQuery = s.GetNamedQuery("organizationreturnproperty"); - sqlQuery.SetResultTransformer(CriteriaSpecification.AliasToEntityMap); - list = sqlQuery.List(); - Assert.AreEqual(2, list.Count); - m = (IDictionary) list[0]; - Assert.IsTrue(m.Contains("org")); - AssertClassAssignability(m["org"].GetType(), typeof(Organization)); - Assert.IsTrue(m.Contains("emp")); - AssertClassAssignability(m["emp"].GetType(), typeof(Employment)); - Assert.AreEqual(2, m.Count); - t.Commit(); - s.Close(); - } - s = OpenSession(); t = s.BeginTransaction(); namedQuery = s.GetNamedQuery("EmploymentAndPerson"); @@ -458,9 +425,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); @@ -473,37 +440,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(); - - if (TestDialect.SupportsDuplicatedColumnAliases) - { - // 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(); @@ -547,6 +483,99 @@ 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) + { + 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); + } + } + + [Test] + public void CanQueryWithManualComponentPropertyAliases() + { + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + SpaceShip enterprise = new SpaceShip(); + enterprise.Model = "USS"; + enterprise.Name = "Entreprise"; + enterprise.Speed = 50d; + Dimension d = new Dimension(45, 10); + enterprise.Dimensions = d; + s.Save(enterprise); + + s.Flush(); + s.Clear(); + + object[] result = (object[])s.GetNamedQuery("spaceship").UniqueResult(); + enterprise = (SpaceShip)result[0]; + Assert.AreEqual(50d, enterprise.Speed, "Speed"); + Assert.AreEqual(45, enterprise.Dimensions.Length, "Dimensions.Length"); + Assert.AreEqual(10, enterprise.Dimensions.Width, "Dimensions.Width"); + } + } + [Test] public void MixAndMatchEntityScalar() { diff --git a/src/NHibernate/Loader/BasicLoader.cs b/src/NHibernate/Loader/BasicLoader.cs index 2d3ddafd877..29ac680ce44 100644 --- a/src/NHibernate/Loader/BasicLoader.cs +++ b/src/NHibernate/Loader/BasicLoader.cs @@ -1,4 +1,5 @@ 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 da62c80955c..00000000000 --- a/src/NHibernate/Loader/Custom/CollectionFetchReturn.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace NHibernate.Loader.Custom -{ - /// Specifically 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 de45101f5cf..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 22d6e406455..60ac660955f 100644 --- a/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/ColumnCollectionAliases.cs @@ -22,14 +22,36 @@ public ColumnCollectionAliases(IDictionary userProvidedAliases this.userProvidedAliases = userProvidedAliases; keyAliases = GetUserProvidedAliases("key", persister.KeyColumnNames); - indexAliases = GetUserProvidedAliases("index", persister.IndexColumnNames); - elementAliases = GetUserProvidedAliases("element", persister.ElementColumnNames); + // NH-1612: Add aliases for all composite element properties to support access + // to individual composite element properties in elements. + elementAliases = persister.ElementType.IsComponentType + ? GetUserProvidedCompositeElementAliases(persister.ElementColumnNames) + : GetUserProvidedAliases("element", persister.ElementColumnNames); 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; + } + /// /// Returns the suffixed result-set column-aliases for columns making up the key for this collection (i.e., its FK to /// its owner). @@ -96,27 +118,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 bfcf2cb2096..5a4ba94b7b9 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 73dadab5f0a..288d71b01ef 100644 --- a/src/NHibernate/Loader/Custom/CustomLoader.cs +++ b/src/NHibernate/Loader/Custom/CustomLoader.cs @@ -25,13 +25,13 @@ public partial class CustomLoader : Loader private readonly SqlString sql; private readonly ISet querySpaces = new HashSet(); - 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; @@ -48,19 +48,18 @@ public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory querySpaces.UnionWith(customQuery.QuerySpaces); parametersSpecifications = customQuery.CollectedParametersSpecifications.ToList(); - List entitypersisters = new List(); + List entitypersisters = new List(); List entityowners = new List(); List entityaliases = new List(); - List collectionpersisters = 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(); + List transformeraliases = new List(); List includeInResultRowList = new List(); @@ -69,106 +68,56 @@ public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory 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)); includeInResultRowList.Add(true); 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.UnionWith(persister.QuerySpaces); - includeInResultRowList.Add(true); - } - 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.UnionWith(elementPersister.QuerySpaces); + entitypersisters.Add(nonScalarRtn.EntityPersister); + entityaliases.Add(nonScalarRtn.EntityAliases); + entityowners.Add(ownerIndex); + querySpaces.UnionWith(nonScalarRtn.EntityPersister.QuerySpaces); } - includeInResultRowList.Add(true); - } - 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.UnionWith(persister.QuerySpaces); - includeInResultRowList.Add(false); - } - 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.UnionWith(elementPersister.QuerySpaces); + collectionpersisters.Add(nonScalarRtn.CollectionPersister); + collectionaliases.Add(nonScalarRtn.CollectionAliases); + collectionowners.Add(ownerIndex); } - includeInResultRowList.Add(false); - } - else - { - throw new HibernateException("unexpected custom query return type : " + rtn.GetType().FullName); + if (nonScalarRtn.Owner == null) + { + resulttypes.Add(nonScalarRtn.Type); + resultColumnProcessors.Add(new NonScalarResultColumnProcessor(returnableCounter++)); + includeInResultRowList.Add(true); + } + else + { + includeInResultRowList.Add(false); + } + + continue; } + + throw new HibernateException("unexpected custom query return type : " + rtn.GetType().FullName); } - entityPersisters = entitypersisters.ToArray(); + entityPersisters = entitypersisters.Cast().ToArray(); entityOwners = entityowners.ToArray(); entityAliases = entityaliases.ToArray(); collectionPersisters = collectionpersisters.ToArray(); @@ -176,7 +125,7 @@ public CustomLoader(ICustomQuery customQuery, ISessionFactoryImplementor factory collectionAliases = collectionaliases.ToArray(); lockModes = lockmodes.ToArray(); ResultTypes = resulttypes.ToArray(); - transformerAliases = specifiedAliases.ToArray(); + transformerAliases = transformeraliases.ToArray(); rowProcessor = new ResultRowProcessor(hasScalars, resultColumnProcessors.ToArray()); includeInResultRow = includeInResultRowList.ToArray(); } @@ -191,6 +140,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; } @@ -211,49 +164,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(); } diff --git a/src/NHibernate/Loader/Custom/EntityFetchReturn.cs b/src/NHibernate/Loader/Custom/EntityFetchReturn.cs deleted file mode 100644 index 516fd5b347b..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 dc92f040d26..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 84% rename from src/NHibernate/Loader/GeneratedCollectionAliases.cs rename to src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs index dfed501a48c..295e0c8ca12 100644 --- a/src/NHibernate/Loader/GeneratedCollectionAliases.cs +++ b/src/NHibernate/Loader/Custom/GeneratedCollectionAliases.cs @@ -3,7 +3,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), @@ -42,16 +42,22 @@ 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.", StringComparison.Ordinal)) + var aliases = new List(); + foreach (var userProvidedAlias in userProvidedAliases) { - aliases.AddRange(userProvidedAlias.Value); + if (userProvidedAlias.Key.StartsWith("element.", StringComparison.Ordinal)) + { + aliases.AddRange(userProvidedAlias.Value); + } } + + if (aliases.Count > 0) + return aliases.ToArray(); } - return aliases.Count > 0 ? aliases.ToArray() : defaultAliases; + return defaultAliases; } /// @@ -116,27 +122,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/IReturn.cs b/src/NHibernate/Loader/Custom/IReturn.cs index 5012044b696..1d13f8910e5 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 1776cbc8fca..a603748b4ca 100644 --- a/src/NHibernate/Loader/Custom/NonScalarReturn.cs +++ b/src/NHibernate/Loader/Custom/NonScalarReturn.cs @@ -1,19 +1,57 @@ +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 ISqlLoadableCollection _collectionPersister; + + 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(nameof(context)); + } if (string.IsNullOrEmpty(alias)) { throw new HibernateException("alias must be specified"); } + + this.alias = alias; this.lockMode = lockMode; + Owner = owner; + + _collectionPersister = context.GetCollectionPersister(alias); + if (_collectionPersister != null) + { + var collectionPropertyResultMap = context.GetCollectionPropertyResultsMap(alias); + CollectionAliases = queryHasAliases + ? new GeneratedCollectionAliases(collectionPropertyResultMap, _collectionPersister, context.GetCollectionSuffix(alias)) + : (ICollectionAliases)new ColumnCollectionAliases(collectionPropertyResultMap, _collectionPersister); + } + + if (_collectionPersister == null || CollectionPersister.ElementType.IsEntityType) + { + EntityPersister = context.GetEntityPersister(alias); + if (EntityPersister != null) + { + var entityPropertyResultMap = context.GetEntityPropertyResultsMap(alias); + EntityAliases = queryHasAliases + ? new DefaultEntityAliases(entityPropertyResultMap, EntityPersister, context.GetEntitySuffix(alias)) + : new ColumnEntityAliases(entityPropertyResultMap, EntityPersister); + } + } } public string Alias @@ -25,5 +63,25 @@ public LockMode LockMode { get { return lockMode; } } + + public NonScalarReturn Owner { get; } + + public ICollectionPersister CollectionPersister => _collectionPersister; + + public ICollectionAliases CollectionAliases { get; } + + public ISqlLoadable EntityPersister { get; } + + public IEntityAliases EntityAliases { get; } + + public IType Type + { + get + { + if (_collectionPersister != null) + return _collectionPersister.CollectionType; + return EntityPersister?.EntityMetamodel.EntityType; + } + } } -} \ 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 4337934a56d..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 5193d4fbbd0..9d30538d2f2 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 57ca5afb81c..0945fa9f45a 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLCustomQuery.cs @@ -4,10 +4,7 @@ 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 { @@ -17,7 +14,7 @@ public class SQLCustomQuery : ICustomQuery { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof (SQLCustomQuery)); - private readonly List customQueryReturns = new List(); + private readonly List customQueryReturns; private readonly ISet querySpaces = new HashSet(); private readonly SqlString sql; private readonly List parametersSpecifications; @@ -26,12 +23,11 @@ public class SQLCustomQuery : ICustomQuery ISessionFactoryImplementor factory) { log.Debug("starting processing of sql query [{0}]", 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)); + var parser = new SQLQueryParser(factory, sqlQuery, processor); sql = parser.Process(); - ArrayHelper.AddAll(customQueryReturns, processor.GenerateCustomReturns(parser.QueryHasAliases)); + customQueryReturns = GenerateCustomReturns(queryReturns, parser.QueryHasAliases, processor).ToList(); parametersSpecifications = parser.CollectedParametersSpecifications.ToList(); if (additionalQuerySpaces != null) @@ -64,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; - } - - 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); - } + #region private methods - 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 } } diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs new file mode 100644 index 00000000000..56c62fd60a6 --- /dev/null +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryContext.cs @@ -0,0 +1,336 @@ +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 INHibernateLogger Log = NHibernateLogger.For(typeof (SQLQueryContext)); + + #region Instance fields + + private readonly Dictionary _alias2Return = + 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) + { + _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 (var rtn in queryReturns) + { + if (rtn is NativeSQLQueryNonScalarReturn nonScalarRtn) + { + _alias2Return[nonScalarRtn.Alias] = rtn; + } + } + + // Now, process the returns + foreach (var rtn in queryReturns) + { + ProcessReturn(rtn); + } + } + + #endregion + + #region ISQLQueryAliasContext implementation + + public bool IsEntityAlias(string alias) + { + return _alias2EntityPersister.ContainsKey(alias); + } + + public ISqlLoadable GetEntityPersister(string alias) + { + _alias2EntityPersister.TryGetValue(alias, out var result); + return result; + } + + public string GetEntitySuffix(string alias) + { + _alias2EntitySuffix.TryGetValue(alias, out var result); + return result; + } + + public IDictionary GetEntityPropertyResultsMap(string alias) + { + _entityPropertyResultMaps.TryGetValue(alias, out var result); + return result; + } + + public bool IsCollectionAlias(string alias) + { + return _alias2CollectionPersister.ContainsKey(alias); + } + + public ISqlLoadableCollection GetCollectionPersister(string alias) + { + _alias2CollectionPersister.TryGetValue(alias, out var result); + return result; + } + + public string GetCollectionSuffix(string alias) + { + _alias2CollectionSuffix.TryGetValue(alias, out var result); + return result; + } + + public IDictionary GetCollectionPropertyResultsMap(string alias) + { + _collectionPropertyResultMaps.TryGetValue(alias, out var result); + return result; + } + + #endregion + + #region Private methods + + private ISqlLoadable GetSQLLoadable(string entityName) + { + var persister = _factory.GetEntityPersister(entityName); + if (!(persister is ISqlLoadable persisterAsSqlLoadable)) + { + 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) + { + switch (rtn) + { + case NativeSQLQueryScalarReturn _: + break; + case NativeSQLQueryRootReturn root: + ProcessRootReturn(root); + break; + case NativeSQLQueryCollectionReturn collection: + ProcessCollectionReturn(collection); + break; + default: + ProcessJoinReturn((NativeSQLQueryJoinReturn) rtn); + break; + } + } + + private void ProcessRootReturn(NativeSQLQueryRootReturn rootReturn) + { + if (_alias2EntityPersister.ContainsKey(rootReturn.Alias)) + { + // already been processed... + return; + } + + var persister = GetSQLLoadable(rootReturn.ReturnEntityName); + AddPersister(rootReturn.Alias, rootReturn.PropertyResultsMap, persister); + } + + private void AddPersister(string alias, IDictionary propertyResultMap, ISqlLoadable persister) + { + _alias2EntityPersister[alias] = persister; + var suffix = GenerateEntitySuffix(); + Log.Debug("mapping alias [" + alias + "] to entity-suffix [" + suffix + "]"); + _alias2EntitySuffix[alias] = suffix; + if (propertyResultMap != null && propertyResultMap.Count > 0) + { + _entityPropertyResultMaps[alias] = GroupComponentAliases(propertyResultMap, persister); + } + } + + private void AddCollection(string role, string alias, IDictionary propertyResultMap) + { + var collectionPersister = (ISqlLoadableCollection) _factory.GetCollectionPersister(role); + _alias2CollectionPersister[alias] = collectionPersister; + var suffix = GenerateCollectionSuffix(); + Log.Debug("mapping alias [" + alias + "] to collection-suffix [" + suffix + "]"); + _alias2CollectionSuffix[alias] = suffix; + + if (propertyResultMap != null && propertyResultMap.Count > 0) + { + _collectionPropertyResultMaps[alias] = FilterCollectionProperties(propertyResultMap); + } + if (collectionPersister.IsOneToMany) + { + var persister = (ISqlLoadable) collectionPersister.ElementPersister; + AddPersister(alias, FilterElementProperties(propertyResultMap), persister); + } + } + + private static IDictionary FilterCollectionProperties(IDictionary propertyResults) + { + if (propertyResults.Count == 0) return propertyResults; + + var result = new Dictionary(propertyResults.Count); + foreach (var element in propertyResults) + { + if (element.Key.IndexOf('.') < 0) + { + result.Add(element.Key, element.Value); + } + } + return result; + } + + private static IDictionary FilterElementProperties(IDictionary propertyResults) + { + const string prefix = "element."; + + if (propertyResults.Count == 0) return propertyResults; + + var result = new Dictionary(propertyResults.Count); + foreach (var element in propertyResults) + { + var path = element.Key; + if (path.StartsWith(prefix)) + { + result.Add(path.Substring(prefix.Length), element.Value); + } + } + return result; + } + + private IDictionary GroupComponentAliases(IDictionary propertyResults, ILoadable persister) + { + if (propertyResults.Count == 0) return propertyResults; + + var result = new Dictionary(propertyResults.Count); + + foreach (var propertyResult in propertyResults) + { + var path = propertyResult.Key; + var dotIndex = path.IndexOf('.'); + if (dotIndex >= 0) + { + var propertyPath = path.Substring(0, dotIndex); + if (!result.ContainsKey(propertyPath)) + { + var aliases = GetUserProvidedAliases(propertyResults, propertyPath, persister.GetPropertyType(propertyPath)); + if (aliases != null) result.Add(propertyPath, aliases); + } + continue; + } + result.Add(propertyResult.Key, propertyResult.Value); + } + return result; + } + + private string[] GetUserProvidedAliases(IDictionary propertyResults, string propertyPath, IType propertyType) + { + if (propertyResults.TryGetValue(propertyPath, out var result)) + return result; + + var aliases = new List(); + AppendUserProvidedAliases(propertyResults, propertyPath, propertyType, aliases); + return aliases.Count > 0 + ? aliases.ToArray() + : null; + } + + private void AppendUserProvidedAliases(IDictionary propertyResults, string propertyPath, IType propertyType, List result) + { + if (propertyResults.TryGetValue(propertyPath, out var aliases)) + { + result.AddRange(aliases); + return; + } + + // TODO: throw exception when no mapping is found for property name + if (!(propertyType is IAbstractComponentType componentType)) + return; + + for (var i = 0; i < componentType.PropertyNames.Length; i++) + { + AppendUserProvidedAliases(propertyResults, propertyPath + '.' + componentType.PropertyNames[i], componentType.Subtypes[i], result); + } + } + + private void ProcessCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) + { + // we are initializing an owned collection + var role = collectionReturn.OwnerEntityName + '.' + collectionReturn.OwnerProperty; + AddCollection(role, collectionReturn.Alias, collectionReturn.PropertyResultsMap); + } + + private void ProcessJoinReturn(NativeSQLQueryJoinReturn fetchReturn) + { + var alias = fetchReturn.Alias; + if (_alias2EntityPersister.ContainsKey(alias) || _alias2CollectionPersister.ContainsKey(alias)) + { + // already been processed... + return; + } + + var ownerAlias = fetchReturn.OwnerAlias; + + // Make sure the owner alias is known... + if (!_alias2Return.TryGetValue(ownerAlias, out var ownerReturn)) + { + throw new HibernateException($"Owner alias [{ownerAlias}] is unknown for alias [{alias}]"); + } + + // If this return's alias has not been processed yet, do so before further processing of this return + if (!_alias2EntityPersister.ContainsKey(ownerAlias)) + { + ProcessReturn(ownerReturn); + } + + var ownerPersister = _alias2EntityPersister[ownerAlias]; + var returnType = ownerPersister.GetPropertyType(fetchReturn.OwnerProperty); + + if (returnType.IsCollectionType) + { + var role = ownerPersister.EntityName + '.' + fetchReturn.OwnerProperty; + AddCollection(role, alias, fetchReturn.PropertyResultsMap); + } + else if (returnType.IsEntityType) + { + var eType = (EntityType) returnType; + var returnEntityName = eType.GetAssociatedEntityName(); + var persister = GetSQLLoadable(returnEntityName); + AddPersister(alias, fetchReturn.PropertyResultsMap, persister); + } + } + + #endregion + } +} diff --git a/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs b/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs index 95943273a32..ca01769f80b 100644 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs +++ b/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,27 +11,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; @@ -54,7 +42,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 referred to - and if we // don't get'em'all we throw an exception! Way better than trial and error ;) private string SubstituteBrackets() { @@ -132,16 +120,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); @@ -162,7 +150,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."); } @@ -171,7 +159,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); } @@ -194,44 +182,44 @@ 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 94ce34aa2d9..00000000000 --- a/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System; -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 INHibernateLogger log = NHibernateLogger.For(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 [{0}] to entity-suffix [{1}]", alias, 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 [{0}] to collection-suffix [{1}]", alias, 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, StringComparison.Ordinal)) - { - 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; - } - } -}