From bf21e9e95b17935cca266ccd85d5f42744072ee3 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Wed, 21 Aug 2019 12:02:59 -0700 Subject: [PATCH] Query: Rename Lateral joins to Cross/Outer Apply Since they don't represent lateral join truly --- .../Query/QuerySqlGenerator.cs | 16 +++++------ ...yableMethodTranslatingExpressionVisitor.cs | 4 +-- .../Query/SqlExpressionVisitor.cs | 12 ++++---- ...lExpression.cs => CrossApplyExpression.cs} | 18 ++++++------ ...lExpression.cs => OuterApplyExpression.cs} | 18 ++++++------ .../Query/SqlExpressions/SelectExpression.cs | 28 +++++++++---------- ...rchConditionConvertingExpressionVisitor.cs | 4 +-- .../Internal/SqlServerQuerySqlGenerator.cs | 16 ----------- .../Query/GearsOfWarQuerySqliteTest.cs | 2 +- .../Query/SimpleQuerySqliteTest.cs | 2 +- 10 files changed, 52 insertions(+), 68 deletions(-) rename src/EFCore.Relational/Query/SqlExpressions/{LeftJoinLateralExpression.cs => CrossApplyExpression.cs} (58%) rename src/EFCore.Relational/Query/SqlExpressions/{InnerJoinLateralExpression.cs => OuterApplyExpression.cs} (58%) diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs index 885e13e4c67..7650650e156 100644 --- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs @@ -674,20 +674,20 @@ protected override Expression VisitCrossJoin(CrossJoinExpression crossJoinExpres return crossJoinExpression; } - protected override Expression VisitInnerJoinLateral(InnerJoinLateralExpression innerJoinLateralExpression) + protected override Expression VisitCrossApply(CrossApplyExpression crossApplyExpression) { - _relationalCommandBuilder.Append("INNER JOIN LATERAL "); - Visit(innerJoinLateralExpression.Table); + _relationalCommandBuilder.Append("CROSS APPLY "); + Visit(crossApplyExpression.Table); - return innerJoinLateralExpression; + return crossApplyExpression; } - protected override Expression VisitLeftJoinLateral(LeftJoinLateralExpression leftJoinLateralExpression) + protected override Expression VisitOuterApply(OuterApplyExpression outerApplyExpression) { - _relationalCommandBuilder.Append("LEFT JOIN LATERAL "); - Visit(leftJoinLateralExpression.Table); + _relationalCommandBuilder.Append("OUTER APPLY "); + Visit(outerApplyExpression.Table); - return leftJoinLateralExpression; + return outerApplyExpression; } protected override Expression VisitInnerJoin(InnerJoinExpression innerJoinExpression) diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index dac06beb796..0d6249ffd8a 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -742,13 +742,13 @@ protected override ShapedQueryExpression TranslateSelectMany( var innerShaperExpression = inner.ShaperExpression; if (defaultIfEmpty) { - ((SelectExpression)source.QueryExpression).AddLeftJoinLateral( + ((SelectExpression)source.QueryExpression).AddOuterApply( (SelectExpression)inner.QueryExpression, transparentIdentifierType); innerShaperExpression = MarkShaperNullable(innerShaperExpression); } else { - ((SelectExpression)source.QueryExpression).AddInnerJoinLateral( + ((SelectExpression)source.QueryExpression).AddCrossApply( (SelectExpression)inner.QueryExpression, transparentIdentifierType); } diff --git a/src/EFCore.Relational/Query/SqlExpressionVisitor.cs b/src/EFCore.Relational/Query/SqlExpressionVisitor.cs index 1a48b6bf809..b6837f83489 100644 --- a/src/EFCore.Relational/Query/SqlExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/SqlExpressionVisitor.cs @@ -21,11 +21,11 @@ protected override Expression VisitExtension(Expression extensionExpression) case CrossJoinExpression crossJoinExpression: return VisitCrossJoin(crossJoinExpression); - case InnerJoinLateralExpression innerJoinLateralExpression: - return VisitInnerJoinLateral(innerJoinLateralExpression); + case CrossApplyExpression crossApplyExpression: + return VisitCrossApply(crossApplyExpression); - case LeftJoinLateralExpression leftJoinLateralExpression: - return VisitLeftJoinLateral(leftJoinLateralExpression); + case OuterApplyExpression outerApplyExpression: + return VisitOuterApply(outerApplyExpression); case ExistsExpression existsExpression: return VisitExists(existsExpression); @@ -101,8 +101,8 @@ protected override Expression VisitExtension(Expression extensionExpression) protected abstract Expression VisitExists(ExistsExpression existsExpression); protected abstract Expression VisitIn(InExpression inExpression); protected abstract Expression VisitCrossJoin(CrossJoinExpression crossJoinExpression); - protected abstract Expression VisitInnerJoinLateral(InnerJoinLateralExpression innerJoinLateralExpression); - protected abstract Expression VisitLeftJoinLateral(LeftJoinLateralExpression leftJoinLateralExpression); + protected abstract Expression VisitCrossApply(CrossApplyExpression crossApplyExpression); + protected abstract Expression VisitOuterApply(OuterApplyExpression outerApplyExpression); protected abstract Expression VisitFromSql(FromSqlExpression fromSqlExpression); protected abstract Expression VisitInnerJoin(InnerJoinExpression innerJoinExpression); protected abstract Expression VisitLeftJoin(LeftJoinExpression leftJoinExpression); diff --git a/src/EFCore.Relational/Query/SqlExpressions/LeftJoinLateralExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs similarity index 58% rename from src/EFCore.Relational/Query/SqlExpressions/LeftJoinLateralExpression.cs rename to src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs index 799c0264d65..1c6ef9df6cb 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/LeftJoinLateralExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs @@ -5,9 +5,9 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions { - public class LeftJoinLateralExpression : JoinExpressionBase + public class CrossApplyExpression : JoinExpressionBase { - public LeftJoinLateralExpression(TableExpressionBase table) + public CrossApplyExpression(TableExpressionBase table) : base(table) { } @@ -15,25 +15,25 @@ public LeftJoinLateralExpression(TableExpressionBase table) protected override Expression VisitChildren(ExpressionVisitor visitor) => Update((TableExpressionBase)visitor.Visit(Table)); - public virtual LeftJoinLateralExpression Update(TableExpressionBase table) + public virtual CrossApplyExpression Update(TableExpressionBase table) => table != Table - ? new LeftJoinLateralExpression(table) + ? new CrossApplyExpression(table) : this; public override void Print(ExpressionPrinter expressionPrinter) { - expressionPrinter.Append("LEFT JOIN LATERAL "); + expressionPrinter.Append("CROSS APPLY "); expressionPrinter.Visit(Table); } public override bool Equals(object obj) => obj != null && (ReferenceEquals(this, obj) - || obj is LeftJoinLateralExpression leftJoinLateralExpression - && Equals(leftJoinLateralExpression)); + || obj is CrossApplyExpression crossApplyExpression + && Equals(crossApplyExpression)); - private bool Equals(LeftJoinLateralExpression leftJoinLateralExpression) - => base.Equals(leftJoinLateralExpression); + private bool Equals(CrossApplyExpression crossApplyExpression) + => base.Equals(crossApplyExpression); public override int GetHashCode() => base.GetHashCode(); } diff --git a/src/EFCore.Relational/Query/SqlExpressions/InnerJoinLateralExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs similarity index 58% rename from src/EFCore.Relational/Query/SqlExpressions/InnerJoinLateralExpression.cs rename to src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs index 880bb4733be..5c48cca4c44 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/InnerJoinLateralExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs @@ -5,9 +5,9 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions { - public class InnerJoinLateralExpression : JoinExpressionBase + public class OuterApplyExpression : JoinExpressionBase { - public InnerJoinLateralExpression(TableExpressionBase table) + public OuterApplyExpression(TableExpressionBase table) : base(table) { } @@ -15,25 +15,25 @@ public InnerJoinLateralExpression(TableExpressionBase table) protected override Expression VisitChildren(ExpressionVisitor visitor) => Update((TableExpressionBase)visitor.Visit(Table)); - public virtual InnerJoinLateralExpression Update(TableExpressionBase table) + public virtual OuterApplyExpression Update(TableExpressionBase table) => table != Table - ? new InnerJoinLateralExpression(table) + ? new OuterApplyExpression(table) : this; public override void Print(ExpressionPrinter expressionPrinter) { - expressionPrinter.Append("INNER JOIN LATERAL "); + expressionPrinter.Append("OUTER APPLY "); expressionPrinter.Visit(Table); } public override bool Equals(object obj) => obj != null && (ReferenceEquals(this, obj) - || obj is InnerJoinLateralExpression innerJoinLateralExpression - && Equals(innerJoinLateralExpression)); + || obj is OuterApplyExpression outerApplyExpression + && Equals(outerApplyExpression)); - private bool Equals(InnerJoinLateralExpression innerJoinLateralExpression) - => base.Equals(innerJoinLateralExpression); + private bool Equals(OuterApplyExpression outerApplyExpression) + => base.Equals(outerApplyExpression); public override int GetHashCode() => base.GetHashCode(); } diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 0167873acaa..91da73899b6 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -715,7 +715,7 @@ public Expression AddSingleProjection(ShapedQueryExpression shapedQueryExpressio var innerSelectExpression = (SelectExpression)shapedQueryExpression.QueryExpression; innerSelectExpression.ApplyProjection(); var projectionCount = innerSelectExpression.Projection.Count; - AddLeftJoinLateral(innerSelectExpression, null); + AddOuterApply(innerSelectExpression, null); // Joined SelectExpression may different based on left join or outer apply // And it will always be SelectExpression because of presence of Take(1) @@ -786,7 +786,7 @@ public Expression ApplyCollectionJoin( } var joinExpression = joinPredicate == null - ? (TableExpressionBase)new LeftJoinLateralExpression(innerSelectExpression.Tables.Single()) + ? (TableExpressionBase)new OuterApplyExpression(innerSelectExpression.Tables.Single()) : new LeftJoinExpression(innerSelectExpression.Tables.Single(), joinPredicate); _tables.Add(joinExpression); @@ -1028,8 +1028,8 @@ private enum JoinType InnerJoin, LeftJoin, CrossJoin, - InnerJoinLateral, - LeftJoinLateral + CrossApply, + OuterApply } private void AddJoin( @@ -1038,8 +1038,8 @@ private void AddJoin( Type transparentIdentifierType, SqlExpression joinPredicate = null) { - // Try to convert lateral join to normal join - if (joinType == JoinType.InnerJoinLateral || joinType == JoinType.LeftJoinLateral) + // Try to convert Apply to normal join + if (joinType == JoinType.CrossApply || joinType == JoinType.OuterApply) { // Doing for limit only since limit + offset may need sum var limit = innerSelectExpression.Limit; @@ -1080,7 +1080,7 @@ private void AddJoin( innerSelectExpression.ApplyPredicate(predicate); } - AddJoin(joinType == JoinType.InnerJoinLateral ? JoinType.InnerJoin : JoinType.LeftJoin, + AddJoin(joinType == JoinType.CrossApply ? JoinType.InnerJoin : JoinType.LeftJoin, innerSelectExpression, transparentIdentifierType, joinPredicate); return; } @@ -1122,8 +1122,8 @@ private void AddJoin( JoinType.InnerJoin => new InnerJoinExpression(innerTable, joinPredicate), JoinType.LeftJoin => new LeftJoinExpression(innerTable, joinPredicate), JoinType.CrossJoin => new CrossJoinExpression(innerTable), - JoinType.InnerJoinLateral => new InnerJoinLateralExpression(innerTable), - JoinType.LeftJoinLateral => new LeftJoinLateralExpression(innerTable), + JoinType.CrossApply => new CrossApplyExpression(innerTable), + JoinType.OuterApply => new OuterApplyExpression(innerTable), _ => throw new InvalidOperationException($"Invalid {nameof(joinType)}: {joinType}") }); @@ -1139,7 +1139,7 @@ private void AddJoin( } var innerMemberInfo = transparentIdentifierType.GetTypeInfo().GetDeclaredField("Inner"); - var innerNullable = joinType == JoinType.LeftJoin || joinType == JoinType.LeftJoinLateral; + var innerNullable = joinType == JoinType.LeftJoin || joinType == JoinType.OuterApply; foreach (var projection in innerSelectExpression._projectionMapping) { var projectionToAdd = projection.Value; @@ -1170,11 +1170,11 @@ public void AddLeftJoin(SelectExpression innerSelectExpression, SqlExpression jo public void AddCrossJoin(SelectExpression innerSelectExpression, Type transparentIdentifierType) => AddJoin(JoinType.CrossJoin, innerSelectExpression, transparentIdentifierType); - public void AddInnerJoinLateral(SelectExpression innerSelectExpression, Type transparentIdentifierType) - => AddJoin(JoinType.InnerJoinLateral, innerSelectExpression, transparentIdentifierType); + public void AddCrossApply(SelectExpression innerSelectExpression, Type transparentIdentifierType) + => AddJoin(JoinType.CrossApply, innerSelectExpression, transparentIdentifierType); - public void AddLeftJoinLateral(SelectExpression innerSelectExpression, Type transparentIdentifierType) - => AddJoin(JoinType.LeftJoinLateral, innerSelectExpression, transparentIdentifierType); + public void AddOuterApply(SelectExpression innerSelectExpression, Type transparentIdentifierType) + => AddJoin(JoinType.OuterApply, innerSelectExpression, transparentIdentifierType); private class SqlRemappingVisitor : ExpressionVisitor { diff --git a/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs index 890d66170ab..ffeb0b052f2 100644 --- a/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SearchConditionConvertingExpressionVisitor.cs @@ -309,7 +309,7 @@ protected override Expression VisitCrossJoin(CrossJoinExpression crossJoinExpres return crossJoinExpression.Update(table); } - protected override Expression VisitInnerJoinLateral(InnerJoinLateralExpression crossApplyExpression) + protected override Expression VisitCrossApply(CrossApplyExpression crossApplyExpression) { var parentSearchCondition = _isSearchCondition; _isSearchCondition = false; @@ -319,7 +319,7 @@ protected override Expression VisitInnerJoinLateral(InnerJoinLateralExpression c return crossApplyExpression.Update(table); } - protected override Expression VisitLeftJoinLateral(LeftJoinLateralExpression outerApplyExpression) + protected override Expression VisitOuterApply(OuterApplyExpression outerApplyExpression) { var parentSearchCondition = _isSearchCondition; _isSearchCondition = false; diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs index bfb22843c59..8c36f64d379 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs @@ -67,21 +67,5 @@ protected override Expression VisitSqlFunction(SqlFunctionExpression sqlFunction return base.VisitSqlFunction(sqlFunctionExpression); } - - protected override Expression VisitInnerJoinLateral(InnerJoinLateralExpression innerJoinLateralExpression) - { - Sql.Append("CROSS APPLY "); - Visit(innerJoinLateralExpression.Table); - - return innerJoinLateralExpression; - } - - protected override Expression VisitLeftJoinLateral(LeftJoinLateralExpression leftJoinLateralExpression) - { - Sql.Append("OUTER APPLY "); - Visit(leftJoinLateralExpression.Table); - - return leftJoinLateralExpression; - } } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs index 38b4bae6835..515ca679368 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs @@ -140,7 +140,7 @@ public override async Task DateTimeOffset_Contains_Less_than_Greater_than(bool i private string RemoveNewLines(string message) => message.Replace("\n", "").Replace("\r", ""); - // Sqlite does not support lateral joins + // Sqlite does not support cross/outer apply public override Task Correlated_collections_inner_subquery_predicate_references_outer_qsre(bool isAsync) => null; public override Task Correlated_collections_inner_subquery_selector_references_outer_qsre(bool isAsync) => null; diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs index 0b55c29186d..b0002d3f818 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs @@ -1309,7 +1309,7 @@ LIMIT @__p_0 public override Task Project_single_element_from_collection_with_multiple_OrderBys_Take_and_FirstOrDefault_2(bool isAsync) => base.Project_single_element_from_collection_with_multiple_OrderBys_Take_and_FirstOrDefault_2(isAsync); - // Sqlite does not support lateral joins + // Sqlite does not support cross/outer apply public override void Select_nested_collection_multi_level() { }