Skip to content

Commit

Permalink
Avoid useraworderby to fix paging issues
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkMpn committed Jun 2, 2024
1 parent 18199f9 commit b240cbd
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 29 deletions.
79 changes: 55 additions & 24 deletions MarkMpn.Sql4Cds.Engine.Tests/ExecutionPlanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5330,13 +5330,14 @@ public void DistinctOrderByOptionSet()

Assert.AreEqual(1, plans.Length);
var select = AssertNode<SelectNode>(plans[0]);
var fetch = AssertNode<FetchXmlScan>(select.Source);
var sort = AssertNode<SortNode>(select.Source);
Assert.AreEqual("new_customentity.new_optionsetvalue", sort.Sorts[0].ToSql());
var fetch = AssertNode<FetchXmlScan>(sort.Source);

AssertFetchXml(fetch, @"
<fetch xmlns:generator='MarkMpn.SQL4CDS' distinct='true' useraworderby='1'>
<fetch xmlns:generator='MarkMpn.SQL4CDS' distinct='true'>
<entity name='new_customentity'>
<attribute name='new_optionsetvalue' />
<order attribute='new_optionsetvalue' />
</entity>
</fetch>");
}
Expand Down Expand Up @@ -7213,54 +7214,84 @@ public void OrderByOptionSetName()
}

[TestMethod]
public void OrderByOptionSetValue()
public void OrderByOptionSetValueWithUseRawOrderBy()
{
var planBuilder = new ExecutionPlanBuilder(_localDataSources.Values, this);
using (_localDataSource.SetUseRawOrderByReliable(true))
{
var planBuilder = new ExecutionPlanBuilder(_localDataSources.Values, this);

var query = @"SELECT new_customentityid FROM new_customentity ORDER BY new_optionsetvalue";
var query = @"SELECT new_customentityid FROM new_customentity ORDER BY new_optionsetvalue";

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

Assert.AreEqual(1, plans.Length);
Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var fetch = AssertNode<FetchXmlScan>(select.Source);
AssertFetchXml(fetch, @"
<fetch useraworderby='1'>
<entity name='new_customentity'>
<attribute name='new_customentityid' />
<order attribute='new_optionsetvalue' />
</entity>
</fetch>");
var select = AssertNode<SelectNode>(plans[0]);
var fetch = AssertNode<FetchXmlScan>(select.Source);
AssertFetchXml(fetch, @"
<fetch useraworderby='1'>
<entity name='new_customentity'>
<attribute name='new_customentityid' />
<order attribute='new_optionsetvalue' />
</entity>
</fetch>");
}
}

[TestMethod]
public void OrderByOptionSetValueAndName()
public void OrderByOptionSetValueWithoutUseRawOrderBy()
{
var planBuilder = new ExecutionPlanBuilder(_localDataSources.Values, this);

var query = @"SELECT new_customentityid FROM new_customentity ORDER BY new_optionsetvalue, new_optionsetvaluename";
var query = @"SELECT new_customentityid FROM new_customentity ORDER BY new_optionsetvalue";

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

Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var sort = AssertNode<SortNode>(select.Source);
Assert.AreEqual(1, sort.PresortedCount);
Assert.AreEqual(2, sort.Sorts.Count);
Assert.AreEqual("new_customentity.new_optionsetvaluename", sort.Sorts[1].Expression.ToSql());
Assert.AreEqual("new_customentity.new_optionsetvalue", sort.Sorts[0].Expression.ToSql());
var fetch = AssertNode<FetchXmlScan>(sort.Source);
AssertFetchXml(fetch, @"
<fetch useraworderby='1'>
<fetch>
<entity name='new_customentity'>
<attribute name='new_customentityid' />
<attribute name='new_optionsetvalue' />
<order attribute='new_optionsetvalue' />
</entity>
</fetch>");
}

[TestMethod]
public void OrderByOptionSetValueAndName()
{
using (_localDataSource.SetUseRawOrderByReliable(true))
{
var planBuilder = new ExecutionPlanBuilder(_localDataSources.Values, this);

var query = @"SELECT new_customentityid FROM new_customentity ORDER BY new_optionsetvalue, new_optionsetvaluename";

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

Assert.AreEqual(1, plans.Length);

var select = AssertNode<SelectNode>(plans[0]);
var sort = AssertNode<SortNode>(select.Source);
Assert.AreEqual(1, sort.PresortedCount);
Assert.AreEqual(2, sort.Sorts.Count);
Assert.AreEqual("new_customentity.new_optionsetvaluename", sort.Sorts[1].Expression.ToSql());
var fetch = AssertNode<FetchXmlScan>(sort.Source);
AssertFetchXml(fetch, @"
<fetch useraworderby='1'>
<entity name='new_customentity'>
<attribute name='new_customentityid' />
<attribute name='new_optionsetvalue' />
<order attribute='new_optionsetvalue' />
</entity>
</fetch>");
}
}

[TestMethod]
public void ExistsOrInAndColumnComparisonOrderByEntityName()
{
Expand Down
9 changes: 9 additions & 0 deletions MarkMpn.Sql4Cds.Engine.Tests/FakeXrmDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public void Dispose()

private bool _columnComparisonAvailable = true;
private bool _orderByEntityNameAvailable = false;
private bool _useRawOrderByReliable = false;
private List<JoinOperator> _joinOperators;

public FakeXrmDataSource()
Expand All @@ -42,6 +43,8 @@ public FakeXrmDataSource()

public override List<JoinOperator> JoinOperatorsAvailable => _joinOperators;

public override bool UseRawOrderByReliable => _useRawOrderByReliable;

public IDisposable SetColumnComparison(bool enable)
{
var original = _columnComparisonAvailable;
Expand All @@ -54,6 +57,12 @@ public IDisposable SetOrderByEntityName(bool enable)
return new Reset(this, x => x._orderByEntityNameAvailable = enable, x => x._orderByEntityNameAvailable = original);
}

public IDisposable SetUseRawOrderByReliable(bool enable)
{
var original = _useRawOrderByReliable;
return new Reset(this, x => x._useRawOrderByReliable = enable, x => x._useRawOrderByReliable = original);
}

public IDisposable EnableJoinOperator(JoinOperator op)
{
var add = !JoinOperatorsAvailable.Contains(op);
Expand Down
4 changes: 1 addition & 3 deletions MarkMpn.Sql4Cds.Engine.Tests/Sql2FetchXmlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1723,17 +1723,15 @@ public void CompareDateFields()
var queries = planBuilder.Build(query, null, out _);

AssertFetchXml(queries, @"
<fetch>
<fetch distinct='true'>
<entity name='contact'>
<attribute name='contactid' />
<link-entity name='contact' to='parentcustomerid' from='parentcustomerid' alias='c2' link-type='inner'>
<attribute name='contactid' />
<order attribute='contactid' />
</link-entity>
<filter>
<condition attribute='createdon' operator='lt' valueof='c2.createdon' />
</filter>
<order attribute='contactid' />
</entity>
</fetch>");

Expand Down
5 changes: 5 additions & 0 deletions MarkMpn.Sql4Cds.Engine/DataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ public DataSource()
/// </summary>
public virtual bool OrderByEntityNameAvailable { get; }

/// <summary>
/// Indicates if the server can reliably page results when using <see cref="FetchXml.FetchType.UseRawOrderBy"/>
/// </summary>
public virtual bool UseRawOrderByReliable { get; }

/// <summary>
/// Returns a list of join operators that are supported by the server
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/SortNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ private IDataExecutionPlanNodeInternal FoldSorts(NodeCompilationContext context)

if (attribute is EnumAttributeMetadata || attribute is BooleanAttributeMetadata)
{
if (useRawOrderBy == false)
if (useRawOrderBy == false || !dataSource.UseRawOrderByReliable)
return this;

useRawOrderBy = true;
Expand Down Expand Up @@ -437,7 +437,7 @@ private IDataExecutionPlanNodeInternal FoldSorts(NodeCompilationContext context)

if (attribute is EnumAttributeMetadata || attribute is BooleanAttributeMetadata)
{
if (useRawOrderBy == false)
if (useRawOrderBy == false || !dataSource.UseRawOrderByReliable)
return this;

useRawOrderBy = true;
Expand Down

0 comments on commit b240cbd

Please sign in to comment.