Skip to content

Commit

Permalink
Do not fold filters through IndexSpool nodes
Browse files Browse the repository at this point in the history
Fixes #314
  • Loading branch information
MarkMpn committed Jun 28, 2023
1 parent 0c27af7 commit 9e0b4e0
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
68 changes: 68 additions & 0 deletions MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5411,5 +5411,73 @@ public void OuterJoinWithFiltersConvertedToInnerJoin()

AssertFetchXml(outerJoinFetch, innerJoinFetch.FetchXmlString);
}

[TestMethod]
public void CorrelatedSubqueryWithMultipleConditions()
{
// https://github.com/MarkMpn/Sql4Cds/issues/316
var planBuilder = new ExecutionPlanBuilder(_dataSources.Values, new OptionsWrapper(this) { PrimaryDataSource = "prod" });
var query = @"
SELECT r.accountid,
r.employees,
r.ownerid,
(SELECT count(*)
FROM account AS sub
WHERE
sub.employees <= r.employees AND
sub.ownerid = r.ownerid) AS cnt
FROM account AS r;";

var plans = planBuilder.Build(query, null, out _);

Assert.AreEqual(1, plans.Length);
var select = AssertNode<SelectNode>(plans[0]);
Assert.AreEqual("r.accountid", select.ColumnSet[0].SourceColumn);
Assert.AreEqual("accountid", select.ColumnSet[0].OutputColumn);
Assert.AreEqual("r.employees", select.ColumnSet[1].SourceColumn);
Assert.AreEqual("employees", select.ColumnSet[1].OutputColumn);
Assert.AreEqual("r.ownerid", select.ColumnSet[2].SourceColumn);
Assert.AreEqual("ownerid", select.ColumnSet[2].OutputColumn);
Assert.AreEqual("Expr3", select.ColumnSet[3].SourceColumn);
Assert.AreEqual("cnt", select.ColumnSet[3].OutputColumn);

var loop = AssertNode<NestedLoopNode>(select.Source);
Assert.IsNull(loop.JoinCondition);
Assert.AreEqual("@Expr1", loop.OuterReferences["r.employees"]);
Assert.AreEqual("@Expr2", loop.OuterReferences["r.ownerid"]);
Assert.AreEqual("count", loop.DefinedValues["Expr3"]);

var outerFetch = AssertNode<FetchXmlScan>(loop.LeftSource);
AssertFetchXml(outerFetch, @"
<fetch xmlns:generator='MarkMpn.SQL4CDS'>
<entity name='account'>
<attribute name='accountid' />
<attribute name='employees' />
<attribute name='ownerid' />
</entity>
</fetch>");

var aggregate = AssertNode<StreamAggregateNode>(loop.RightSource);
Assert.AreEqual(AggregateType.CountStar, aggregate.Aggregates["count"].AggregateType);

var filter = AssertNode<FilterNode>(aggregate.Source);
Assert.AreEqual("sub.employees <= @Expr1", filter.Filter.ToSql());

var indexSpool = AssertNode<IndexSpoolNode>(filter.Source);
Assert.AreEqual("@Expr2", indexSpool.SeekValue);
Assert.AreEqual("sub.ownerid", indexSpool.KeyColumn);

var innerFetch = AssertNode<FetchXmlScan>(indexSpool.Source);
AssertFetchXml(innerFetch, @"
<fetch xmlns:generator='MarkMpn.SQL4CDS'>
<entity name='account'>
<attribute name='employees' />
<attribute name='ownerid' />
<filter>
<condition attribute=""ownerid"" operator=""not-null"" />
</filter>
</entity>
</fetch>");
}
}
}
2 changes: 1 addition & 1 deletion MarkMpn.Sql4Cds.Engine/ExecutionPlan/FilterNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ private IEnumerable<IDataExecutionPlanNodeInternal> GetFoldableSources(IDataExec
if (source is HashMatchAggregateNode)
yield break;

if (source is TableSpoolNode)
if (source is TableSpoolNode || source is IndexSpoolNode)
yield break;

foreach (var subSource in source.GetSources().OfType<IDataExecutionPlanNodeInternal>())
Expand Down

0 comments on commit 9e0b4e0

Please sign in to comment.