Skip to content

Commit

Permalink
Avoid folding aggregates on audit entity using inner joins
Browse files Browse the repository at this point in the history
Fixes #488
  • Loading branch information
MarkMpn committed Jun 22, 2024
1 parent e1c416c commit 1844287
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 0 deletions.
83 changes: 83 additions & 0 deletions MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6062,6 +6062,62 @@ public void FilterAuditOnLeftJoinColumn()
</fetch>");
}

[TestMethod]
public void FilterAuditOnUserId()
{
var planBuilder = new ExecutionPlanBuilder(_dataSources.Values, new OptionsWrapper(this) { PrimaryDataSource = "prod" });
var query = "SELECT * FROM audit WHERE userid = 'B52C0694-E16F-40CD-9F27-800023C47A98'";
var plans = planBuilder.Build(query, null, out _);

Assert.AreEqual(1, plans.Length);
var select = AssertNode<SelectNode>(plans[0]);
var fetch = AssertNode<FetchXmlScan>(select.Source);
AssertFetchXml(fetch, @"
<fetch xmlns:generator='MarkMpn.SQL4CDS'>
<entity name='audit'>
<all-attributes />
<filter>
<condition attribute='userid' operator='eq' value='B52C0694-E16F-40CD-9F27-800023C47A98' />
</filter>
</entity>
</fetch>");
}

[TestMethod]
public void FilterAuditOnUserIdAggregate()
{
// Can do aggregates on audit table when any filtering is done only on the table itself,
// not on any joins
var planBuilder = new ExecutionPlanBuilder(_dataSources.Values, new OptionsWrapper(this) { PrimaryDataSource = "prod" });
var query = "SELECT COUNT(*) FROM audit WHERE userid = 'B52C0694-E16F-40CD-9F27-800023C47A98'";
var plans = planBuilder.Build(query, null, out _);

Assert.AreEqual(1, plans.Length);
var select = AssertNode<SelectNode>(plans[0]);
var tryCatch = AssertNode<TryCatchNode>(select.Source);
var aggregateFetch = AssertNode<FetchXmlScan>(tryCatch.TrySource);
AssertFetchXml(aggregateFetch, @"
<fetch xmlns:generator='MarkMpn.SQL4CDS' aggregate='true'>
<entity name='audit'>
<attribute name='auditid' aggregate='count' alias='count' />
<filter>
<condition attribute='userid' operator='eq' value='B52C0694-E16F-40CD-9F27-800023C47A98' />
</filter>
</entity>
</fetch>");
var streamAggregate = AssertNode<StreamAggregateNode>(tryCatch.CatchSource);
var fetch = AssertNode<FetchXmlScan>(streamAggregate.Source);
AssertFetchXml(fetch, @"
<fetch xmlns:generator='MarkMpn.SQL4CDS'>
<entity name='audit'>
<attribute name='auditid' />
<filter>
<condition attribute='userid' operator='eq' value='B52C0694-E16F-40CD-9F27-800023C47A98' />
</filter>
</entity>
</fetch>");
}

[TestMethod]
public void FilterAuditOnInnerJoinColumn()
{
Expand All @@ -6087,6 +6143,33 @@ public void FilterAuditOnInnerJoinColumn()
</fetch>");
}

[TestMethod]
public void FilterAuditOnInnerJoinColumnAggregate()
{
// Can't do aggregates on audit table when filtering is done on a join
// https://github.com/MarkMpn/Sql4Cds/issues/488
var planBuilder = new ExecutionPlanBuilder(_dataSources.Values, new OptionsWrapper(this) { PrimaryDataSource = "prod" });
var query = "SELECT COUNT(*) FROM audit INNER JOIN systemuser ON audit.userid = systemuser.systemuserid WHERE systemuser.domainname <> 'SYSTEM'";
var plans = planBuilder.Build(query, null, out _);

Assert.AreEqual(1, plans.Length);
var select = AssertNode<SelectNode>(plans[0]);
var aggregate = AssertNode<StreamAggregateNode>(select.Source);
var fetch = AssertNode<FetchXmlScan>(aggregate.Source);
AssertFetchXml(fetch, @"
<fetch xmlns:generator='MarkMpn.SQL4CDS'>
<entity name='audit'>
<attribute name='auditid' />
<link-entity name='systemuser' from='systemuserid' to='userid' link-type='inner' alias='systemuser'>
<filter>
<condition attribute='domainname' operator='ne' value='SYSTEM' />
<condition attribute='domainname' operator='not-null' />
</filter>
</link-entity>
</entity>
</fetch>");
}

[TestMethod]
public void SortAuditOnJoinColumn()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@ Source is FetchXmlScan fetch &&
(fetchXml.Entity.name == "audit" || metadata[fetchXml.Entity.name].DataProviderId == DataProviders.ElasticDataProvider))
canUseFetchXmlAggregate = false;

// Aggregates on audit entity seem to transform inner joins to left outer joins, so can't
// use FetchXML aggregates here
if (fetchXml.Entity.name == "audit" && fetchXml.Entity.GetLinkEntities(true).Any())
canUseFetchXmlAggregate = false;

var serializer = new XmlSerializer(typeof(FetchXml.FetchType));

if (canUseFetchXmlAggregate)
Expand Down

0 comments on commit 1844287

Please sign in to comment.