Skip to content

Commit

Permalink
Do not fold join conditions from nested loop to outer source
Browse files Browse the repository at this point in the history
Fixes #548
  • Loading branch information
MarkMpn committed Sep 14, 2024
1 parent 1028992 commit fd94f86
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 3 deletions.
33 changes: 33 additions & 0 deletions MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8463,6 +8463,7 @@ OUTER APPLY (

var select = AssertNode<SelectNode>(plans[0]);
var apply = AssertNode<NestedLoopNode>(select.Source);
Assert.AreEqual(QualifiedJoinType.Inner, apply.JoinType);
var join = AssertNode<MergeJoinNode>(apply.LeftSource);
var fetch1 = AssertNode<FetchXmlScan>(join.LeftSource);
var sort = AssertNode<SortNode>(join.RightSource);
Expand All @@ -8472,5 +8473,37 @@ OUTER APPLY (
var compute = AssertNode<ComputeScalarNode>(filter.Source);
var constant = AssertNode<ConstantScanNode>(compute.Source);
}

[TestMethod]
public void FilterOnOuterApply()
{
// https://github.com/MarkMpn/Sql4Cds/issues/548
var planBuilder = new ExecutionPlanBuilder(_localDataSources.Values, this);

var query = @"
SELECT *
FROM (
SELECT a.accountid,
a.name
FROM account a) AS q1
OUTER APPLY (
SELECT IIF(q1.name = 'Test1', 1, 0) AS [flag1],
IIF(q1.name = 'Test2', 1, 0) AS [flag2]
) AS q2
WHERE q2.flag1 = 1 OR q2.flag2 = 1";

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

Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var apply = AssertNode<NestedLoopNode>(select.Source);
Assert.AreEqual(QualifiedJoinType.LeftOuter, apply.JoinType);
Assert.AreEqual("q2.flag1 = 1 OR q2.flag2 = 1", apply.JoinCondition.ToSql());
var fetch = AssertNode<FetchXmlScan>(apply.LeftSource);
var alias = AssertNode<AliasNode>(apply.RightSource);
var compute = AssertNode<ComputeScalarNode>(alias.Source);
var constant = AssertNode<ConstantScanNode>(compute.Source);
}
}
}
9 changes: 6 additions & 3 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/NestedLoopNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,9 @@ public override IDataExecutionPlanNodeInternal FoldQuery(NodeCompilationContext
var joinColumns = JoinCondition.GetColumns().ToList();
var hasLeftColumn = joinColumns.Any(c => leftSchema.ContainsColumn(c, out _));
var hasRightColumn = joinColumns.Any(c => rightSchema.ContainsColumn(c, out _));
var foldedFilter = false;

if (!hasLeftColumn)
if (!hasLeftColumn && JoinType == QualifiedJoinType.Inner)
{
// Join condition doesn't reference columns from the left source, so we can remove it from the join
// and apply it as a filter to the right source. Inner source will often have a table spool - add the filter
Expand All @@ -206,17 +207,19 @@ public override IDataExecutionPlanNodeInternal FoldQuery(NodeCompilationContext

RightSource = RightSource.FoldQuery(innerContext, hints);
RightSource.Parent = this;
foldedFilter = true;
}

if (!hasRightColumn)
if (!hasRightColumn && (JoinType == QualifiedJoinType.Inner || JoinType == QualifiedJoinType.LeftOuter))
{
// Join condition doesn't reference columns from the right source, so we can remove it from the join
// and apply it as a filter to the left source
LeftSource = new FilterNode { Source = LeftSource, Filter = JoinCondition }.FoldQuery(context, hints);
LeftSource.Parent = this;
foldedFilter = true;
}

if (!hasLeftColumn || !hasRightColumn)
if (foldedFilter)
JoinCondition = null;
}

Expand Down

0 comments on commit fd94f86

Please sign in to comment.