Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "11.0.100-preview.5.26227.104",
"version": "11.0.100-preview.5.26279.113",
"allowPrerelease": true,
"rollForward": "latestMajor",
"paths": [
Expand All @@ -13,7 +13,7 @@
"runner": "Microsoft.Testing.Platform"
},
"tools": {
"dotnet": "11.0.100-preview.5.26227.104",
"dotnet": "11.0.100-preview.5.26279.113",
"runtimes": {
"dotnet": [
"$(MicrosoftNETCorePlatformsVersion)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,23 @@ protected override ShapedQueryExpression TranslateCast(ShapedQueryExpression sou
return null;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected override ShapedQueryExpression? TranslateFullJoin(
ShapedQueryExpression outer,
ShapedQueryExpression inner,
LambdaExpression outerKeySelector,
LambdaExpression innerKeySelector,
LambdaExpression resultSelector)
{
AddTranslationErrorDetails(CosmosStrings.CrossDocumentJoinNotSupported);
return null;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,20 @@ static bool IsConvertedToNullable(Expression outer, Expression inner)
LambdaExpression resultSelector)
=> null;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected override ShapedQueryExpression? TranslateFullJoin(
ShapedQueryExpression outer,
ShapedQueryExpression inner,
LambdaExpression outerKeySelector,
LambdaExpression innerKeySelector,
LambdaExpression resultSelector)
=> null;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
41 changes: 41 additions & 0 deletions src/EFCore.Relational/EFCore.Relational.baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -3039,6 +3039,35 @@
}
]
},
{
"Type": "class Microsoft.EntityFrameworkCore.Query.SqlExpressions.FullJoinExpression : Microsoft.EntityFrameworkCore.Query.SqlExpressions.PredicateJoinExpressionBase",
"Methods": [
{
"Member": "FullJoinExpression(Microsoft.EntityFrameworkCore.Query.SqlExpressions.TableExpressionBase table, Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlExpression joinPredicate, bool prunable = false);"
},
{
"Member": "override bool Equals(object? obj);"
},
{
"Member": "override int GetHashCode();"
},
{
"Member": "override void Print(Microsoft.EntityFrameworkCore.Query.ExpressionPrinter expressionPrinter);"
},
{
"Member": "override System.Linq.Expressions.Expression Quote();"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Query.SqlExpressions.FullJoinExpression Update(Microsoft.EntityFrameworkCore.Query.SqlExpressions.TableExpressionBase table, Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlExpression joinPredicate);"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Query.SqlExpressions.FullJoinExpression Update(Microsoft.EntityFrameworkCore.Query.SqlExpressions.TableExpressionBase table);"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Query.SqlExpressions.FullJoinExpression WithAnnotations(System.Collections.Generic.IReadOnlyDictionary<string, Microsoft.EntityFrameworkCore.Infrastructure.IAnnotation> annotations);"
}
]
},
{
"Type": "class Microsoft.EntityFrameworkCore.Storage.GuidTypeMapping : Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping",
"Methods": [
Expand Down Expand Up @@ -9456,6 +9485,9 @@
{
"Member": "virtual System.Linq.Expressions.Expression VisitFromSql(Microsoft.EntityFrameworkCore.Query.SqlExpressions.FromSqlExpression fromSqlExpression);"
},
{
"Member": "virtual System.Linq.Expressions.Expression VisitFullJoin(Microsoft.EntityFrameworkCore.Query.SqlExpressions.FullJoinExpression fullJoinExpression);"
},
{
"Member": "virtual System.Linq.Expressions.Expression VisitInnerJoin(Microsoft.EntityFrameworkCore.Query.SqlExpressions.InnerJoinExpression innerJoinExpression);"
},
Expand Down Expand Up @@ -15291,6 +15323,9 @@
{
"Member": "override Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression? TranslateFirstOrDefault(Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression source, System.Linq.Expressions.LambdaExpression? predicate, System.Type returnType, bool returnDefault);"
},
{
"Member": "override Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression? TranslateFullJoin(Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression outer, Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression inner, System.Linq.Expressions.LambdaExpression outerKeySelector, System.Linq.Expressions.LambdaExpression innerKeySelector, System.Linq.Expressions.LambdaExpression resultSelector);"
},
{
"Member": "override Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression? TranslateGroupBy(Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression source, System.Linq.Expressions.LambdaExpression keySelector, System.Linq.Expressions.LambdaExpression? elementSelector, System.Linq.Expressions.LambdaExpression? resultSelector);"
},
Expand Down Expand Up @@ -18326,6 +18361,12 @@
{
"Member": "System.Linq.Expressions.Expression AddCrossJoin(Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression innerSource, System.Linq.Expressions.Expression outerShaper);"
},
{
"Member": "void AddFullJoin(Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression innerSelectExpression, Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlExpression joinPredicate);"
},
{
"Member": "System.Linq.Expressions.Expression AddFullJoin(Microsoft.EntityFrameworkCore.Query.ShapedQueryExpression innerSource, Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlExpression joinPredicate, System.Linq.Expressions.Expression outerShaper);"
},
{
"Member": "void AddInnerJoin(Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression innerSelectExpression, Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlExpression joinPredicate);"
},
Expand Down
15 changes: 15 additions & 0 deletions src/EFCore.Relational/Query/QuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ protected override Expression VisitExtension(Expression expression)
ProjectionExpression e => VisitProjection(e),
TableValuedFunctionExpression e => VisitTableValuedFunction(e),
RightJoinExpression e => VisitRightJoin(e),
FullJoinExpression e => VisitFullJoin(e),
RowNumberExpression e => VisitRowNumber(e),
RowValueExpression e => VisitRowValue(e),
ScalarSubqueryExpression e => VisitScalarSubquery(e),
Expand Down Expand Up @@ -1299,6 +1300,20 @@ protected virtual Expression VisitRightJoin(RightJoinExpression rightJoinExpress
return rightJoinExpression;
}

/// <summary>
/// Generates SQL for a full join.
/// </summary>
/// <param name="fullJoinExpression">The <see cref="FullJoinExpression" /> for which to generate SQL.</param>
protected virtual Expression VisitFullJoin(FullJoinExpression fullJoinExpression)
{
_relationalCommandBuilder.Append("FULL JOIN ");
Visit(fullJoinExpression.Table);
_relationalCommandBuilder.Append(" ON ");
Visit(fullJoinExpression.JoinPredicate);

return fullJoinExpression;
}

/// <summary>
/// Generates SQL for a scalar subquery.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,27 @@ static bool HasMatchingRequiredForeignKey(
return null;
}

/// <inheritdoc />
protected override ShapedQueryExpression? TranslateFullJoin(
ShapedQueryExpression outer,
ShapedQueryExpression inner,
LambdaExpression outerKeySelector,
LambdaExpression innerKeySelector,
LambdaExpression resultSelector)
{
var joinPredicate = CreateJoinPredicate(outer, outerKeySelector, inner, innerKeySelector);
if (joinPredicate != null)
{
var outerSelectExpression = (SelectExpression)outer.QueryExpression;
var outerShaperExpression = outerSelectExpression.AddFullJoin(inner, joinPredicate, outer.ShaperExpression);
outer = outer.UpdateShaperExpression(outerShaperExpression);

return TranslateTwoParameterSelector(outer, resultSelector);
}

return null;
}

private SqlExpression CreateJoinPredicate(
ShapedQueryExpression outer,
LambdaExpression outerKeySelector,
Expand Down
106 changes: 106 additions & 0 deletions src/EFCore.Relational/Query/SqlExpressions/FullJoinExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;

/// <summary>
/// <para>
/// An expression that represents a FULL JOIN in a SQL tree.
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
public class FullJoinExpression : PredicateJoinExpressionBase
{
private static ConstructorInfo? _quotingConstructor;

/// <summary>
/// Creates a new instance of the <see cref="FullJoinExpression" /> class.
/// </summary>
/// <param name="table">A table source to FULL JOIN with.</param>
/// <param name="joinPredicate">A predicate to use for the join.</param>
/// <param name="prunable">Whether this join expression may be pruned if nothing references a column on it.</param>
public FullJoinExpression(TableExpressionBase table, SqlExpression joinPredicate, bool prunable = false)
: this(table, joinPredicate, prunable, annotations: null)
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal] // For precompiled queries
public FullJoinExpression(
TableExpressionBase table,
SqlExpression joinPredicate,
bool prunable,
IReadOnlyDictionary<string, IAnnotation>? annotations = null)
: base(table, joinPredicate, prunable, annotations)
{
}

/// <summary>
/// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will
/// return this expression.
/// </summary>
/// <param name="table">The <see cref="JoinExpressionBase.Table" /> property of the result.</param>
/// <param name="joinPredicate">The <see cref="PredicateJoinExpressionBase.JoinPredicate" /> property of the result.</param>
/// <returns>This expression if no children changed, or an expression with the updated children.</returns>
public override FullJoinExpression Update(TableExpressionBase table, SqlExpression joinPredicate)
=> table != Table || joinPredicate != JoinPredicate
? new FullJoinExpression(table, joinPredicate, IsPrunable, Annotations)
: this;

/// <summary>
/// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will
/// return this expression.
/// </summary>
/// <param name="table">The <see cref="JoinExpressionBase.Table" /> property of the result.</param>
/// <returns>This expression if no children changed, or an expression with the updated children.</returns>
public override FullJoinExpression Update(TableExpressionBase table)
=> table != Table
? new FullJoinExpression(table, JoinPredicate, IsPrunable, Annotations)
: this;

/// <inheritdoc />
protected override FullJoinExpression WithAnnotations(IReadOnlyDictionary<string, IAnnotation> annotations)
=> new(Table, JoinPredicate, IsPrunable, annotations);

/// <inheritdoc />
public override Expression Quote()
=> New(
_quotingConstructor ??= typeof(FullJoinExpression).GetConstructor(
[typeof(TableExpressionBase), typeof(SqlExpression), typeof(bool), typeof(IReadOnlyDictionary<string, IAnnotation>)])!,
Table.Quote(),
JoinPredicate.Quote(),
Constant(IsPrunable),
RelationalExpressionQuotingUtilities.QuoteAnnotations(Annotations));

/// <inheritdoc />
protected override void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.Append("FULL JOIN ");
expressionPrinter.Visit(Table);
expressionPrinter.Append(" ON ");
expressionPrinter.Visit(JoinPredicate);
PrintAnnotations(expressionPrinter);
}

/// <inheritdoc />
public override bool Equals(object? obj)
=> obj != null
&& (ReferenceEquals(this, obj)
|| obj is FullJoinExpression fullJoinExpression
&& Equals(fullJoinExpression));

private bool Equals(FullJoinExpression fullJoinExpression)
=> base.Equals(fullJoinExpression);

/// <inheritdoc />
public override int GetHashCode()
=> base.GetHashCode();
}
Loading
Loading