Skip to content

Commit

Permalink
Enable LINQ ThenBy operator after OrderBy (#801)
Browse files Browse the repository at this point in the history
* Add type check functions and update changelog

* Enable ThenBy after OrderBy operator

* Refactor negative tests

* Add error message, update the changelog

* Revert type check functions - need to reimplement with static methods instead of extensions

* Update changelog.md

Remove accidental change

* Update changelog.md

Add "LINQ"

* Update tests initialization
  • Loading branch information
khdang authored and kirankumarkolli committed Oct 8, 2019
1 parent 6772d39 commit 8427f52
Show file tree
Hide file tree
Showing 28 changed files with 1,492 additions and 598 deletions.
16 changes: 16 additions & 0 deletions Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ public static class LinqMethods
public const string Max = "Max";
public const string Min = "Min";
public const string OrderBy = "OrderBy";
public const string ThenBy = "ThenBy";
public const string OrderByDescending = "OrderByDescending";
public const string ThenByDescending = "ThenByDescending";
public const string Select = "Select";
public const string SelectMany = "SelectMany";
public const string Sum = "Sum";
Expand Down Expand Up @@ -1192,6 +1194,18 @@ private static Collection VisitMethodCall(MethodCallExpression inputExpression,
context.currentQuery = context.currentQuery.AddOrderByClause(orderBy, context);
break;
}
case LinqMethods.ThenBy:
{
SqlOrderbyClause thenBy = ExpressionToSql.VisitOrderBy(inputExpression.Arguments, false, context);
context.currentQuery = context.currentQuery.UpdateOrderByClause(thenBy, context);
break;
}
case LinqMethods.ThenByDescending:
{
SqlOrderbyClause thenBy = ExpressionToSql.VisitOrderBy(inputExpression.Arguments, true, context);
context.currentQuery = context.currentQuery.UpdateOrderByClause(thenBy, context);
break;
}
case LinqMethods.Skip:
{
SqlOffsetSpec offsetSpec = ExpressionToSql.VisitSkip(inputExpression.Arguments, context);
Expand Down Expand Up @@ -1344,6 +1358,8 @@ private static bool IsSubqueryScalarExpression(Expression expression, out SqlObj
case LinqMethods.Where:
case LinqMethods.OrderBy:
case LinqMethods.OrderByDescending:
case LinqMethods.ThenBy:
case LinqMethods.ThenByDescending:
case LinqMethods.Skip:
case LinqMethods.Take:
case LinqMethods.Distinct:
Expand Down
13 changes: 13 additions & 0 deletions Microsoft.Azure.Cosmos/src/Linq/QueryUnderConstruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ public bool ShouldBeOnNewQuery(string methodName, int argumentCount)
case LinqMethods.Any:
case LinqMethods.OrderBy:
case LinqMethods.OrderByDescending:
case LinqMethods.ThenBy:
case LinqMethods.ThenByDescending:
case LinqMethods.Distinct:
// New query is needed when there is already a Take or a non-distinct Select
shouldPackage = (this.topSpec != null) ||
Expand Down Expand Up @@ -578,6 +580,17 @@ public QueryUnderConstruction AddOrderByClause(SqlOrderbyClause orderBy, Transla
return result;
}

public QueryUnderConstruction UpdateOrderByClause(SqlOrderbyClause thenBy, TranslationContext context)
{
List<SqlOrderByItem> items = new List<SqlOrderByItem>(context.currentQuery.orderByClause.OrderbyItems);
items.AddRange(thenBy.OrderbyItems);
context.currentQuery.orderByClause = SqlOrderbyClause.Create(items);

foreach (Binding binding in context.CurrentSubqueryBinding.TakeBindings()) context.currentQuery.AddBinding(binding);

return context.currentQuery;
}

public QueryUnderConstruction AddOffsetSpec(SqlOffsetSpec offsetSpec, TranslationContext context)
{
QueryUnderConstruction result = context.PackageCurrentQueryIfNeccessary();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ JOIN (
<Input>
<Description><![CDATA[Select number -> Skip -> Avg]]></Description>
<Expression><![CDATA[query.Select(f => f.Int).Skip(90).Average(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand All @@ -112,13 +111,13 @@ FROM (
OFFSET 90 LIMIT 2147483647
) AS r0
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":62,"end":88},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Select number -> Skip -> Take -> Avg]]></Description>
<Expression><![CDATA[query.Select(f => f.Int).Skip(90).Take(5).Average(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand All @@ -129,13 +128,13 @@ FROM (
OFFSET 90 LIMIT 5
) AS r0
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":62,"end":79},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Skip -> Take -> Select number -> Avg]]></Description>
<Expression><![CDATA[query.Skip(5).Take(5).Select(f => f.Int).Average(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand All @@ -146,13 +145,13 @@ FROM (
OFFSET 5 LIMIT 5
) AS r0
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":62,"end":78},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Skip -> Take -> SelectMany(Select) -> Skip -> Take -> Avg]]></Description>
<Expression><![CDATA[query.Skip(5).Take(5).SelectMany(f => f.Children.Select(c => c.Grade)).Skip(10).Take(20).Average(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand All @@ -168,13 +167,13 @@ FROM (
OFFSET 10 LIMIT 20
) AS r1
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":86,"end":102},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":137,"end":155},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Skip -> Take -> Select(new() -> Skip -> Take)]]></Description>
<Expression><![CDATA[query.Skip(1).Take(20).Where(f => (f.Children.Count() > 2)).Select(f => new AnonymousType(v0 = f.Children.Skip(1).Select(c => c.Grade).Average(), v1 = f.Children.Skip(1).Take(3).Select(c => c.Grade).Average(), v2 = f.Children.Take(3).Skip(1).Select(c => c.Grade).Average(), v3 = f.Records.Transactions.Select(t => t.Amount).OrderBy(a => a).Skip(10).Take(20).Average(), v4 = f.Children.Where(c => (c.Grade > 20)).OrderBy(c => c.Grade).Select(c => c.Grade).Skip(1).Average())).Skip(1).Take(10).Select(f => (((((f.v0 + f.v1) + f.v2) + f.v3) + f.v4) / 5)).Average(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand Down Expand Up @@ -254,6 +253,7 @@ FROM (
) AS r8
) AS r9
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":230,"end":247},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":367,"end":392},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":522,"end":538},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":648,"end":653},"code":"SC2203","message":"'TOP' is not supported in subqueries."},{"severity":"Error","location":{"start":707,"end":732},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":879,"end":904},"code":"SC2202","message":"'ORDER BY' is not supported in subqueries."},{"severity":"Error","location":{"start":905,"end":923},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":1079,"end":1103},"code":"SC2202","message":"'ORDER BY' is not supported in subqueries."},{"severity":"Error","location":{"start":1104,"end":1129},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":1190,"end":1207},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
</Results>
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ WHERE (root["Number"] < -13) ]]></SqlQuery>
<Input>
<Description><![CDATA[Select number -> Skip -> Count]]></Description>
<Expression><![CDATA[query.Select(f => f.Int).Skip(90).Count(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand All @@ -109,13 +108,13 @@ FROM (
OFFSET 90 LIMIT 2147483647
) AS r0
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":63,"end":89},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Select number -> Skip -> Take -> Count]]></Description>
<Expression><![CDATA[query.Select(f => f.Int).Skip(90).Take(5).Count(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand All @@ -126,13 +125,13 @@ FROM (
OFFSET 90 LIMIT 5
) AS r0
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":63,"end":80},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Skip -> Take -> Select number -> Count]]></Description>
<Expression><![CDATA[query.Skip(5).Take(5).Select(f => f.Int).Count(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand All @@ -143,13 +142,13 @@ FROM (
OFFSET 5 LIMIT 5
) AS r0
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":63,"end":79},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Skip -> Take -> SelectMany(Select) -> Skip -> Take -> Count]]></Description>
<Expression><![CDATA[query.Skip(5).Take(5).SelectMany(f => f.Children.Select(c => c.Grade)).Skip(10).Take(20).Count(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand All @@ -165,13 +164,13 @@ FROM (
OFFSET 10 LIMIT 20
) AS r1
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":87,"end":103},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":138,"end":156},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Skip -> Take -> Select(new(Skip -> Select -> Count, Skip -> Take -> Select -> Count, Take -> Skip -> Select -> Count) -> Skip -> Take)]]></Description>
<Expression><![CDATA[query.Skip(1).Take(20).Select(f => new AnonymousType(v0 = f.Children.Skip(1).Select(c => c.Grade).Count(), v1 = f.Children.Skip(1).Take(3).Select(c => c.Grade).Count(), v2 = f.Children.Take(3).Skip(1).Select(c => c.Grade).Count())).Skip(1).Take(10).Count(), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand Down Expand Up @@ -216,13 +215,13 @@ FROM (
OFFSET 1 LIMIT 10
) AS r5
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":202,"end":227},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":341,"end":357},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":446,"end":451},"code":"SC2203","message":"'TOP' is not supported in subqueries."},{"severity":"Error","location":{"start":507,"end":532},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":550,"end":567},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":576,"end":593},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[Skip -> Take -> Select(new() -> Skip -> Take)]]></Description>
<Expression><![CDATA[query.Skip(1).Take(20).Select(f => new AnonymousType(v0 = f.Children.Skip(1).Count(c => (c.Grade > 50)), v1 = f.Children.Skip(1).Take(3).Count(c => (c.Grade > 50)), v2 = f.Children.Take(3).Skip(1).Count(c => (c.Grade > 50)), v3 = f.Records.Transactions.Select(t => t.Amount).OrderBy(a => a).Skip(10).Take(20).Count(), v4 = f.Children.Where(c => (c.Grade > 20)).OrderBy(c => c.Grade).Select(c => c.Grade).Skip(1).Count())).Skip(1).Take(10).Count(f => ((f.v0 + f.v1) > (f.v2 + f.v3))), Object)]]></Expression>
<ErrorMessage><![CDATA['OFFSET LIMIT' clause is not supported in subqueries.]]></ErrorMessage>
</Input>
<Output>
<SqlQuery><![CDATA[
Expand Down Expand Up @@ -292,6 +291,7 @@ FROM (
) AS r8
WHERE ((r8["v0"] + r8["v1"]) > (r8["v2"] + r8["v3"]))
]]></SqlQuery>
<ErrorMessage><![CDATA[Response status code does not indicate success: 400 Substatus: 0 Reason: (Message: {"errors":[{"severity":"Error","location":{"start":215,"end":240},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":370,"end":386},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":491,"end":496},"code":"SC2203","message":"'TOP' is not supported in subqueries."},{"severity":"Error","location":{"start":552,"end":577},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":733,"end":758},"code":"SC2202","message":"'ORDER BY' is not supported in subqueries."},{"severity":"Error","location":{"start":759,"end":777},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":917,"end":941},"code":"SC2202","message":"'ORDER BY' is not supported in subqueries."},{"severity":"Error","location":{"start":942,"end":967},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":985,"end":1002},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."},{"severity":"Error","location":{"start":1011,"end":1028},"code":"SC2204","message":"'OFFSET LIMIT' clause is not supported in subqueries."}]}).]]></ErrorMessage>
</Output>
</Result>
</Results>
Loading

0 comments on commit 8427f52

Please sign in to comment.