Skip to content

Commit

Permalink
Use query hints
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkMpn committed Apr 11, 2022
1 parent 14eff9b commit 7866772
Show file tree
Hide file tree
Showing 22 changed files with 324 additions and 164 deletions.
180 changes: 151 additions & 29 deletions MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ public class ExecutionPlanTests : FakeXrmEasyTestsBase, IQueryExecutionOptions

bool IQueryExecutionOptions.UseTDSEndpoint => false;

bool IQueryExecutionOptions.UseRetrieveTotalRecordCount => true;

int IQueryExecutionOptions.MaxDegreeOfParallelism => 10;

bool IQueryExecutionOptions.ColumnComparisonAvailable => true;
Expand All @@ -54,19 +52,16 @@ public class ExecutionPlanTests : FakeXrmEasyTestsBase, IQueryExecutionOptions

bool IQueryExecutionOptions.BypassCustomPlugins => false;

bool IQueryExecutionOptions.ConfirmInsert(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmInsert(ConfirmDmlStatementEventArgs e)
{
return true;
}

bool IQueryExecutionOptions.ConfirmDelete(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmDelete(ConfirmDmlStatementEventArgs e)
{
return true;
}

bool IQueryExecutionOptions.ConfirmUpdate(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmUpdate(ConfirmDmlStatementEventArgs e)
{
return true;
}

bool IQueryExecutionOptions.ContinueRetrieve(int count)
Expand Down Expand Up @@ -859,27 +854,6 @@ public void PartialWhere()
</fetch>");
}

[TestMethod]
public void RetrieveTotalRecordCountRequest()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"
SELECT count(*) FROM account";

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

Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var computeScalar = AssertNode<ComputeScalarNode>(select.Source);
Assert.AreEqual(1, computeScalar.Columns.Count);
Assert.AreEqual("account_count", computeScalar.Columns["count"].ToSql());
var count = AssertNode<RetrieveTotalRecordCountNode>(computeScalar.Source);
Assert.AreEqual("account", count.EntityName);
}

[TestMethod]
public void ComputeScalarSelect()
{
Expand Down Expand Up @@ -4286,5 +4260,153 @@ public void UpdateParameters()
</entity>
</fetch>");
}

[TestMethod]
public void CountUsesAggregateByDefault()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"SELECT count(*) FROM account";

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

Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var tryCatch1 = AssertNode<TryCatchNode>(select.Source);
var tryCatch2 = AssertNode<TryCatchNode>(tryCatch1.TrySource);
var fetch = AssertNode<FetchXmlScan>(tryCatch2.TrySource);

AssertFetchXml(fetch, @"
<fetch aggregate='true'>
<entity name='account'>
<attribute name='accountid' aggregate='count' alias='count' />
</entity>
</fetch>");
}

[TestMethod]
public void CountUsesRetrieveTotalRecordCountWithHint()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"SELECT count(*) FROM account OPTION (USE HINT ('RETRIEVE_TOTAL_RECORD_COUNT'))";

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

Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var computeScalar = AssertNode<ComputeScalarNode>(select.Source);
Assert.AreEqual(1, computeScalar.Columns.Count);
Assert.AreEqual("account_count", computeScalar.Columns["count"].ToSql());
var count = AssertNode<RetrieveTotalRecordCountNode>(computeScalar.Source);
Assert.AreEqual("account", count.EntityName);
}

[TestMethod]
public void MaxDOPUsesDefault()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"UPDATE account SET name = 'test'";

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

Assert.AreEqual(1, plans.Length);

var update = AssertNode<UpdateNode>(plans[0]);
Assert.AreEqual(((IQueryExecutionOptions)this).MaxDegreeOfParallelism, update.MaxDOP);
}

[TestMethod]
public void MaxDOPUsesHint()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"UPDATE account SET name = 'test' OPTION (MAXDOP 7)";

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

Assert.AreEqual(1, plans.Length);

var update = AssertNode<UpdateNode>(plans[0]);
Assert.AreEqual(7, update.MaxDOP);
}

[TestMethod]
public void SubqueryUsesSpoolByDefault()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"SELECT accountid, (SELECT TOP 1 fullname FROM contact) FROM account";

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

Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var compute = AssertNode<ComputeScalarNode>(select.Source);
var loop = AssertNode<NestedLoopNode>(compute.Source);
var accountFetch = AssertNode<FetchXmlScan>(loop.LeftSource);
var spool = AssertNode<TableSpoolNode>(loop.RightSource);
var contactFetch = AssertNode<FetchXmlScan>(spool.Source);
}

[TestMethod]
public void SubqueryDoesntUseSpoolWithHint()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"SELECT accountid, (SELECT TOP 1 fullname FROM contact) FROM account OPTION (NO_PERFORMANCE_SPOOL)";

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

Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var compute = AssertNode<ComputeScalarNode>(select.Source);
var loop = AssertNode<NestedLoopNode>(compute.Source);
var accountFetch = AssertNode<FetchXmlScan>(loop.LeftSource);
var contactFetch = AssertNode<FetchXmlScan>(loop.RightSource);
}

[TestMethod]
public void BypassPluginExecutionUsesDefault()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"UPDATE account SET name = 'test'";

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

Assert.AreEqual(1, plans.Length);

var update = AssertNode<UpdateNode>(plans[0]);
Assert.AreEqual(false, update.BypassCustomPluginExecution);
}

[TestMethod]
public void BypassPluginExecutionUsesHint()
{
var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);

var query = @"UPDATE account SET name = 'test' OPTION (USE HINT ('BYPASS_CUSTOM_PLUGIN_EXECUTION'))";

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

Assert.AreEqual(1, plans.Length);

var update = AssertNode<UpdateNode>(plans[0]);
Assert.AreEqual(true, update.BypassCustomPluginExecution);
}
}
}
10 changes: 3 additions & 7 deletions MarkMpn.Sql4Cds.Engine.Tests/OptionsWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public OptionsWrapper(IQueryExecutionOptions options)
UseBulkDelete = options.UseBulkDelete;
BatchSize = options.BatchSize;
UseTDSEndpoint = options.UseTDSEndpoint;
UseRetrieveTotalRecordCount = options.UseRetrieveTotalRecordCount;
MaxDegreeOfParallelism = options.MaxDegreeOfParallelism;
ColumnComparisonAvailable = options.ColumnComparisonAvailable;
UseLocalTimeZone = options.UseLocalTimeZone;
Expand Down Expand Up @@ -64,19 +63,16 @@ public OptionsWrapper(IQueryExecutionOptions options)

public bool QuotedIdentifiers { get; set; }

public bool ConfirmDelete(int count, EntityMetadata meta)
public void ConfirmDelete(ConfirmDmlStatementEventArgs e)
{
return _options.ConfirmDelete(count, meta);
}

public bool ConfirmInsert(int count, EntityMetadata meta)
public void ConfirmInsert(ConfirmDmlStatementEventArgs e)
{
return _options.ConfirmInsert(count, meta);
}

public bool ConfirmUpdate(int count, EntityMetadata meta)
public void ConfirmUpdate(ConfirmDmlStatementEventArgs e)
{
return _options.ConfirmUpdate(count, meta);
}

public bool ContinueRetrieve(int count)
Expand Down
13 changes: 4 additions & 9 deletions MarkMpn.Sql4Cds.Engine.Tests/Sql2FetchXmlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ public class Sql2FetchXmlTests : FakeXrmEasyTestsBase, IQueryExecutionOptions

bool IQueryExecutionOptions.UseTDSEndpoint => false;

bool IQueryExecutionOptions.UseRetrieveTotalRecordCount => true;

int IQueryExecutionOptions.MaxDegreeOfParallelism => 10;

bool IQueryExecutionOptions.ColumnComparisonAvailable => true;
Expand Down Expand Up @@ -1843,7 +1841,7 @@ public void FilterExpressionConstantValueToFetchXml()
[TestMethod]
public void Count1ConvertedToCountStar()
{
var query = "SELECT COUNT(1) FROM contact";
var query = "SELECT COUNT(1) FROM contact OPTION(USE HINT('RETRIEVE_TOTAL_RECORD_COUNT'))";

var metadata = new AttributeMetadataCache(_service);
var planBuilder = new ExecutionPlanBuilder(metadata, new StubTableSizeCache(), this);
Expand Down Expand Up @@ -2667,19 +2665,16 @@ bool IQueryExecutionOptions.ContinueRetrieve(int count)
return true;
}

bool IQueryExecutionOptions.ConfirmInsert(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmInsert(ConfirmDmlStatementEventArgs e)
{
return true;
}

bool IQueryExecutionOptions.ConfirmUpdate(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmUpdate(ConfirmDmlStatementEventArgs e)
{
return true;
}

bool IQueryExecutionOptions.ConfirmDelete(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmDelete(ConfirmDmlStatementEventArgs e)
{
return true;
}

private class RetrieveMetadataChangesHandler : IFakeMessageExecutor
Expand Down
11 changes: 3 additions & 8 deletions MarkMpn.Sql4Cds.Engine.Tests/StubOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ class StubOptions : IQueryExecutionOptions

bool IQueryExecutionOptions.UseTDSEndpoint => false;

bool IQueryExecutionOptions.UseRetrieveTotalRecordCount => true;

int IQueryExecutionOptions.MaxDegreeOfParallelism => 10;

bool IQueryExecutionOptions.ColumnComparisonAvailable => true;
Expand All @@ -35,19 +33,16 @@ class StubOptions : IQueryExecutionOptions

bool IQueryExecutionOptions.BypassCustomPlugins => false;

bool IQueryExecutionOptions.ConfirmInsert(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmInsert(ConfirmDmlStatementEventArgs e)
{
return true;
}

bool IQueryExecutionOptions.ConfirmDelete(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmDelete(ConfirmDmlStatementEventArgs e)
{
return true;
}

bool IQueryExecutionOptions.ConfirmUpdate(int count, EntityMetadata meta)
void IQueryExecutionOptions.ConfirmUpdate(ConfirmDmlStatementEventArgs e)
{
return true;
}

bool IQueryExecutionOptions.ContinueRetrieve(int count)
Expand Down
14 changes: 6 additions & 8 deletions MarkMpn.Sql4Cds.Engine/Ado/CancellationTokenOptionsWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ public CancellationTokenOptionsWrapper(IQueryExecutionOptions options, Cancellat

public bool UseTDSEndpoint => _options.UseTDSEndpoint;

public bool UseRetrieveTotalRecordCount => _options.UseRetrieveTotalRecordCount;

public int MaxDegreeOfParallelism => _options.MaxDegreeOfParallelism;

public bool ColumnComparisonAvailable => _options.ColumnComparisonAvailable;
Expand All @@ -49,19 +47,19 @@ public CancellationTokenOptionsWrapper(IQueryExecutionOptions options, Cancellat

public bool QuotedIdentifiers => _options.QuotedIdentifiers;

public bool ConfirmDelete(int count, EntityMetadata meta)
public void ConfirmDelete(ConfirmDmlStatementEventArgs e)
{
return _options.ConfirmDelete(count, meta);
_options.ConfirmDelete(e);
}

public bool ConfirmInsert(int count, EntityMetadata meta)
public void ConfirmInsert(ConfirmDmlStatementEventArgs e)
{
return _options.ConfirmInsert(count, meta);
_options.ConfirmInsert(e);
}

public bool ConfirmUpdate(int count, EntityMetadata meta)
public void ConfirmUpdate(ConfirmDmlStatementEventArgs e)
{
return _options.ConfirmUpdate(count, meta);
_options.ConfirmUpdate(e);
}

public bool ContinueRetrieve(int count)
Expand Down
Loading

0 comments on commit 7866772

Please sign in to comment.