Skip to content

Commit

Permalink
Handle converting sort orders on lookup and optionset fields to sort …
Browse files Browse the repository at this point in the history
…by name or value according to fetch useraworderby attribute
  • Loading branch information
MarkMpn committed Mar 26, 2024
1 parent 2c3d5c9 commit 2978607
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 9 deletions.
36 changes: 36 additions & 0 deletions MarkMpn.Sql4Cds.Engine.FetchXml.Tests/FetchXml2SqlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,42 @@ public void Order()
Assert.AreEqual("SELECT firstname, lastname FROM contact ORDER BY firstname ASC", NormalizeWhitespace(converted));
}

[TestMethod]
public void OrderLookup()
{
var metadata = new AttributeMetadataCache(_service);
var fetch = @"
<fetch>
<entity name='contact'>
<attribute name='firstname' />
<attribute name='lastname' />
<order attribute='parentcustomerid' />
</entity>
</fetch>";

var converted = FetchXml2Sql.Convert(_service, metadata, fetch, new FetchXml2SqlOptions(), out _);

Assert.AreEqual("SELECT firstname, lastname FROM contact ORDER BY parentcustomeridname ASC", NormalizeWhitespace(converted));
}

[TestMethod]
public void OrderLookupRaw()
{
var metadata = new AttributeMetadataCache(_service);
var fetch = @"
<fetch useraworderby='1'>
<entity name='contact'>
<attribute name='firstname' />
<attribute name='lastname' />
<order attribute='parentcustomerid' />
</entity>
</fetch>";

var converted = FetchXml2Sql.Convert(_service, metadata, fetch, new FetchXml2SqlOptions(), out _);

Assert.AreEqual("SELECT firstname, lastname FROM contact ORDER BY parentcustomerid ASC", NormalizeWhitespace(converted));
}

[TestMethod]
public void OrderDescending()
{
Expand Down
4 changes: 4 additions & 0 deletions MarkMpn.Sql4Cds.Engine/FetchXml.Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ partial class FetchType

[XmlAttribute("datasource")]
public string DataSource { get; set; }

[XmlAttribute("useraworderby")]
[DefaultValue(false)]
public bool UseRawOrderBy { get; set; }
}

partial class FetchOrderType
Expand Down
31 changes: 22 additions & 9 deletions MarkMpn.Sql4Cds.Engine/FetchXml2Sql.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public static string Convert(IOrganizationService org, IAttributeMetadataCache m
}

// ORDER BY
AddOrderBy(entity.name, entity.Items, query);
AddOrderBy(entity.name, entity.Items, query, fetch.UseRawOrderBy, metadata, aliasToLogicalName);

// For single-table queries, don't bother qualifying the column names to make the query easier to read
if (query.FromClause.TableReferences[0] is NamedTableReference)
Expand Down Expand Up @@ -2556,7 +2556,7 @@ private static int GetUserLanguageCode(IOrganizationService org)
/// <param name="name">The name or alias of the &lt;entity&gt; or &lt;link-entity&gt; that the sorts are from</param>
/// <param name="items">The items within the &lt;entity&gt; or &lt;link-entity&gt; to take the sorts from</param>
/// <param name="query">The SQL query to apply the sorts to</param>
private static void AddOrderBy(string name, object[] items, QuerySpecification query)
private static void AddOrderBy(string name, object[] items, QuerySpecification query, bool useRawOrderBy, IAttributeMetadataCache metadata, Dictionary<string,string> aliasToLogicalName)
{
if (items == null)
return;
Expand All @@ -2577,7 +2577,7 @@ private static void AddOrderBy(string name, object[] items, QuerySpecification q
{
Identifiers =
{
new Identifier{Value = sort.alias}
new Identifier { Value = sort.alias }
}
}
},
Expand All @@ -2586,7 +2586,20 @@ private static void AddOrderBy(string name, object[] items, QuerySpecification q
}
else
{
var entityName = sort.entityname ?? name;
var entityAlias = sort.entityname ?? name;
var attributeName = sort.attribute;

if (!aliasToLogicalName.TryGetValue(entityAlias, out var entityLogicalName))
entityLogicalName = entityAlias;

if (!useRawOrderBy)
{
var entityMetadata = metadata[entityLogicalName];
var attr = entityMetadata.Attributes.SingleOrDefault(a => a.LogicalName == attributeName);

if (attr is LookupAttributeMetadata || attr is EnumAttributeMetadata)
attributeName += "name";
}

query.OrderByClause.OrderByElements.Add(new ExpressionWithSortOrder
{
Expand All @@ -2595,10 +2608,10 @@ private static void AddOrderBy(string name, object[] items, QuerySpecification q
MultiPartIdentifier = new MultiPartIdentifier
{
Identifiers =
{
new Identifier{Value = entityName},
new Identifier{Value = sort.attribute}
}
{
new Identifier { Value = entityAlias },
new Identifier { Value = attributeName }
}
}
},
SortOrder = sort.descending ? SortOrder.Descending : SortOrder.Ascending
Expand All @@ -2608,7 +2621,7 @@ private static void AddOrderBy(string name, object[] items, QuerySpecification q

// Recurse into link entities
foreach (var link in items.OfType<FetchLinkEntityType>())
AddOrderBy(link.alias ?? link.name, link.Items, query);
AddOrderBy(link.alias ?? link.name, link.Items, query, useRawOrderBy, metadata, aliasToLogicalName);
}

private class SimplifyMultiPartIdentifierVisitor : TSqlFragmentVisitor
Expand Down

0 comments on commit 2978607

Please sign in to comment.