From 5c3134c394659ec0965e187ef4650ca7d4e19629 Mon Sep 17 00:00:00 2001 From: Mark Carrington Date: Wed, 6 Apr 2022 08:54:40 +0100 Subject: [PATCH] Fold conditional query for IF/WHILE statements Remove unnecessary table spool for EXISTS --- .../ExecutionPlanTests.cs | 42 +++++++++++++++++++ .../ExecutionPlan/ConditionalNode.cs | 30 +++++++------ .../ExecutionPlan/NestedLoopNode.cs | 11 +++++ 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs b/MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs index e588098a..c80181e5 100644 --- a/MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs +++ b/MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs @@ -4212,5 +4212,47 @@ public void InsertParameters() var compute = AssertNode(insert.Source); var constant = AssertNode(compute.Source); } + + [TestMethod] + public void NotExistsParameters() + { + var metadata = new AttributeMetadataCache(_service); + var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this); + + var query = @"DECLARE @firstname AS VARCHAR (100) = 'Mark', @lastname AS VARCHAR (100) = 'Carrington'; + +IF NOT EXISTS (SELECT * FROM contact WHERE firstname = @firstname AND lastname = @lastname) +BEGIN + INSERT INTO contact (firstname, lastname) + VALUES (@firstname, @lastname); +END"; + + var plans = planBuilder.Build(query, null, out _); + + Assert.AreEqual(4, plans.Length); + + AssertNode(plans[0]); + AssertNode(plans[1]); + AssertNode(plans[2]); + var cond = AssertNode(plans[3]); + var compute = AssertNode(cond.Source); + var loop = AssertNode(compute.Source); + var constant = AssertNode(loop.LeftSource); + var fetch = AssertNode(loop.RightSource); + var insert = AssertNode(cond.TrueStatements[0]); + var insertCompute = AssertNode(insert.Source); + var insertConstant = AssertNode(insertCompute.Source); + + AssertFetchXml(fetch, @" + + + + + + + + + "); + } } } diff --git a/MarkMpn.Sql4Cds.Engine/ExecutionPlan/ConditionalNode.cs b/MarkMpn.Sql4Cds.Engine/ExecutionPlan/ConditionalNode.cs index e1d9914f..c147d3fc 100644 --- a/MarkMpn.Sql4Cds.Engine/ExecutionPlan/ConditionalNode.cs +++ b/MarkMpn.Sql4Cds.Engine/ExecutionPlan/ConditionalNode.cs @@ -69,24 +69,24 @@ public string Execute(IDictionary dataSources, IQueryExecuti public IRootExecutionPlanNodeInternal[] FoldQuery(IDictionary dataSources, IQueryExecutionOptions options, IDictionary parameterTypes, IList hints) { - if (hints != null && hints.OfType().Any()) - { - TrueStatements = TrueStatements - .SelectMany(s => s.FoldQuery(dataSources, options, parameterTypes, hints)) - .ToArray(); + TrueLabel = Guid.NewGuid().ToString(); + FalseLabel = Guid.NewGuid().ToString(); - FalseStatements = FalseStatements - ?.SelectMany(s => s.FoldQuery(dataSources, options, parameterTypes, hints)) - ?.ToArray(); + Source = Source?.FoldQuery(dataSources, options, parameterTypes, hints); + TrueStatements = TrueStatements + .SelectMany(s => s.FoldQuery(dataSources, options, parameterTypes, hints)) + .ToArray(); + + FalseStatements = FalseStatements + ?.SelectMany(s => s.FoldQuery(dataSources, options, parameterTypes, hints)) + ?.ToArray(); + + if (hints != null && hints.OfType().Any()) return new[] { this }; - } var statements = new List(); - TrueLabel = Guid.NewGuid().ToString(); - FalseLabel = Guid.NewGuid().ToString(); - statements.AddRange( new GoToNode { @@ -104,8 +104,7 @@ public IRootExecutionPlanNodeInternal[] FoldQuery(IDictionary