Skip to content

Commit

Permalink
Temporal table support for owned and table splitting scenarios
Browse files Browse the repository at this point in the history
Fix to #26858 - Query: improve TableExpressionBase extensibility by adding annotations
Fix to #26469 - Query: enable temporal tables for table splitting scenarios
Fix to #26451 - Temporal Table: Owned Entities support?

Added annotation infra for TableExpressionBase and annotations for temporal table information. Removed (now unnecessary) temporal specific table expressions.
Also added temporal table support for owned typed and table splitting in general using the annotations to store the temporal information (no need for provider specific logic in places where we didn't have good extensibility)
For table splitting, every entity must explicitly define period start/end columns. They all need to be the same, but if not explicitly provided we try to uniquefy them. We should fix this so that you only need to specify it in one place but it's currently hard to do (hopefully convention layering will make it easier)
  • Loading branch information
maumar committed Feb 5, 2022
1 parent a170463 commit ac35072
Show file tree
Hide file tree
Showing 47 changed files with 5,704 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,10 @@ private sealed class SharedTypeEntityExpandingExpressionVisitor : ExpressionVisi
{
private static readonly MethodInfo _objectEqualsMethodInfo
= typeof(object).GetRequiredRuntimeMethod(nameof(object.Equals), typeof(object), typeof(object));
private static readonly bool _useOldBehavior = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue26592", out var enabled)
private static readonly bool _useOldBehavior26592 = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue26592", out var enabled)
&& enabled;

private static readonly bool _useOldBehavior26469 = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue26469", out var enabled)
&& enabled;

private readonly RelationalSqlTranslatingExpressionVisitor _sqlTranslator;
Expand All @@ -1178,7 +1181,7 @@ public Expression Expand(SelectExpression selectExpression, Expression lambdaBod
{
_selectExpression = selectExpression;

if (_useOldBehavior)
if (_useOldBehavior26592)
{
return Visit(lambdaBody);
}
Expand Down Expand Up @@ -1225,7 +1228,7 @@ protected override Expression VisitExtension(Expression extensionExpression)

private Expression? TryExpand(Expression? source, MemberIdentity member)
{
if (_useOldBehavior)
if (_useOldBehavior26592)
{
source = source.UnwrapTypeConversion(out var convertedType);
if (source is not EntityShaperExpression entityShaperExpression)
Expand Down Expand Up @@ -1435,11 +1438,26 @@ outerKey is NewArrayExpression newArrayExpression
return null;
}

var entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression);
var foreignKey = navigation.ForeignKey;
if (navigation.IsCollection)
{
SelectExpression innerSelectExpression;


if (!_useOldBehavior26469)
{
innerSelectExpression = BuildInnerSelectExpressionForOwnedTypeMappedToDifferentTable(
entityProjectionExpression,
navigation);
}
else
{
innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);
}

var innerShapedQuery = CreateShapedQueryExpression(
targetEntityType, _sqlExpressionFactory.Select(targetEntityType));
targetEntityType, innerSelectExpression);

var makeNullable = foreignKey.PrincipalKey.Properties
.Concat(foreignKey.Properties)
Expand Down Expand Up @@ -1487,7 +1505,6 @@ outerKey is NewArrayExpression newArrayExpression
Expression.Quote(correlationPredicate));
}

var entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression);
var innerShaper = entityProjectionExpression.BindNavigation(navigation);
if (innerShaper == null)
{
Expand Down Expand Up @@ -1530,7 +1547,19 @@ outerKey is NewArrayExpression newArrayExpression
// Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630
// So there is no handling for dependent having TPT
table = targetEntityType.GetViewOrTableMappings().Single().Table;
var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);

SelectExpression innerSelectExpression;
if (!_useOldBehavior26469)
{
innerSelectExpression = BuildInnerSelectExpressionForOwnedTypeMappedToDifferentTable(
entityProjectionExpression,
navigation);
}
else
{
innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);
}

var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);

var makeNullable = foreignKey.PrincipalKey.Properties
Expand Down Expand Up @@ -1584,6 +1613,50 @@ outerKey is NewArrayExpression newArrayExpression
: new DeferredOwnedExpansionExpression(targetEntityType,
(ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression,
navigation);

SelectExpression BuildInnerSelectExpressionForOwnedTypeMappedToDifferentTable(
EntityProjectionExpression entityProjectionExpression,
INavigation navigation)
{
// just need any column - we use it only to extract the table it originated from
var sourceColumn = entityProjectionExpression
.BindProperty(
navigation.IsOnDependent
? foreignKey.Properties[0]
: foreignKey.PrincipalKey.Properties[0]);

var sourceTable = FindRootTableExpressionForColumn(sourceColumn);
var ownedTable = new TableExpression(targetEntityType.GetTableMappings().Single().Table);

foreach (var annotation in sourceTable.GetAnnotations())
{
ownedTable.SetAnnotation(annotation.Name, annotation.Value);
}

return _sqlExpressionFactory.Select(targetEntityType, ownedTable);
}

static TableExpressionBase FindRootTableExpressionForColumn(ColumnExpression column)
{
var table = column.Table;
if (table is JoinExpressionBase joinExpressionBase)
{
table = joinExpressionBase.Table;
}
else if (table is SetOperationBase setOperationBase)
{
table = setOperationBase.Source1;
}

if (table is SelectExpression selectExpression)
{
var matchingProjection = (ColumnExpression)selectExpression.Projection.Where(p => p.Alias == column.Name).Single().Expression;

return FindRootTableExpressionForColumn(matchingProjection);
}

return table;
}
}
}

Expand Down
12 changes: 10 additions & 2 deletions src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
Expand All @@ -22,7 +24,12 @@ public class CrossApplyExpression : JoinExpressionBase
/// </summary>
/// <param name="table">A table source to CROSS APPLY with.</param>
public CrossApplyExpression(TableExpressionBase table)
: base(table)
: this(table, annotations: null)
{
}

private CrossApplyExpression(TableExpressionBase table, IEnumerable<IAnnotation>? annotations)
: base(table, annotations)
{
}

Expand All @@ -45,7 +52,7 @@ public virtual CrossApplyExpression Update(TableExpressionBase table)
Check.NotNull(table, nameof(table));

return table != Table
? new CrossApplyExpression(table)
? new CrossApplyExpression(table, GetAnnotations())
: this;
}

Expand All @@ -56,6 +63,7 @@ protected override void Print(ExpressionPrinter expressionPrinter)

expressionPrinter.Append("CROSS APPLY ");
expressionPrinter.Visit(Table);
PrintAnnotations(expressionPrinter);
}

/// <inheritdoc />
Expand Down
12 changes: 10 additions & 2 deletions src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
Expand All @@ -22,7 +24,12 @@ public class CrossJoinExpression : JoinExpressionBase
/// </summary>
/// <param name="table">A table source to CROSS JOIN with.</param>
public CrossJoinExpression(TableExpressionBase table)
: base(table)
: this(table, annotations: null)
{
}

private CrossJoinExpression(TableExpressionBase table, IEnumerable<IAnnotation>? annotations)
: base(table, annotations)
{
}

Expand All @@ -45,7 +52,7 @@ public virtual CrossJoinExpression Update(TableExpressionBase table)
Check.NotNull(table, nameof(table));

return table != Table
? new CrossJoinExpression(table)
? new CrossJoinExpression(table, GetAnnotations())
: this;
}

Expand All @@ -56,6 +63,7 @@ protected override void Print(ExpressionPrinter expressionPrinter)

expressionPrinter.Append("CROSS JOIN ");
expressionPrinter.Visit(Table);
PrintAnnotations(expressionPrinter);
}

/// <inheritdoc />
Expand Down
22 changes: 18 additions & 4 deletions src/EFCore.Relational/Query/SqlExpressions/ExceptExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
Expand Down Expand Up @@ -30,7 +32,17 @@ public ExceptExpression(
SelectExpression source1,
SelectExpression source2,
bool distinct)
: base(alias, source1, source2, distinct)
: this(alias, source1, source2, distinct, annotations: null)
{
}

private ExceptExpression(
string alias,
SelectExpression source1,
SelectExpression source2,
bool distinct,
IEnumerable<IAnnotation>? annotations)
: base(alias, source1, source2, distinct, annotations)
{
}

Expand Down Expand Up @@ -58,7 +70,7 @@ public virtual ExceptExpression Update(SelectExpression source1, SelectExpressio
Check.NotNull(source2, nameof(source2));

return source1 != Source1 || source2 != Source2
? new ExceptExpression(Alias, source1, source2, IsDistinct)
? new ExceptExpression(Alias, source1, source2, IsDistinct, GetAnnotations())
: this;
}

Expand All @@ -81,8 +93,10 @@ protected override void Print(ExpressionPrinter expressionPrinter)
expressionPrinter.Visit(Source2);
}

expressionPrinter.AppendLine()
.AppendLine($") AS {Alias}");
expressionPrinter.AppendLine();
expressionPrinter.Append(")");
PrintAnnotations(expressionPrinter);
expressionPrinter.AppendLine($" AS {Alias}");
}

/// <inheritdoc />
Expand Down
19 changes: 16 additions & 3 deletions src/EFCore.Relational/Query/SqlExpressions/FromSqlExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
Expand Down Expand Up @@ -43,7 +45,16 @@ public FromSqlExpression(string sql, Expression arguments, string alias)
/// <param name="sql">A user-provided custom SQL for the table source.</param>
/// <param name="arguments">A user-provided parameters to pass to the custom SQL.</param>
public FromSqlExpression(string alias, string sql, Expression arguments)
: base(alias)
: this(alias, sql, arguments, annotations: null)
{
}

private FromSqlExpression(
string alias,
string sql,
Expression arguments,
IEnumerable<IAnnotation>? annotations)
: base(alias, annotations)
{
Check.NotEmpty(sql, nameof(sql));
Check.NotNull(arguments, nameof(arguments));
Expand All @@ -52,6 +63,7 @@ public FromSqlExpression(string alias, string sql, Expression arguments)
Arguments = arguments;
}


/// <summary>
/// The alias assigned to this table source.
/// </summary>
Expand Down Expand Up @@ -83,7 +95,7 @@ public virtual FromSqlExpression Update(Expression arguments)
Check.NotNull(arguments, nameof(arguments));

return arguments != Arguments
? new FromSqlExpression(Alias, Sql, arguments)
? new FromSqlExpression(Alias, Sql, arguments, GetAnnotations())
: this;
}

Expand All @@ -97,14 +109,15 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)

/// <inheritdoc />
public virtual TableExpressionBase Clone()
=> new FromSqlExpression(Alias, Sql, Arguments);
=> new FromSqlExpression(Alias, Sql, Arguments, GetAnnotations());

/// <inheritdoc />
protected override void Print(ExpressionPrinter expressionPrinter)
{
Check.NotNull(expressionPrinter, nameof(expressionPrinter));

expressionPrinter.Append(Sql);
PrintAnnotations(expressionPrinter);
}

/// <inheritdoc />
Expand Down
15 changes: 13 additions & 2 deletions src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
Expand All @@ -23,7 +25,15 @@ public class InnerJoinExpression : PredicateJoinExpressionBase
/// <param name="table">A table source to INNER JOIN with.</param>
/// <param name="joinPredicate">A predicate to use for the join.</param>
public InnerJoinExpression(TableExpressionBase table, SqlExpression joinPredicate)
: base(table, joinPredicate)
: this(table, joinPredicate, annotations: null)
{
}

private InnerJoinExpression(
TableExpressionBase table,
SqlExpression joinPredicate,
IEnumerable<IAnnotation>? annotations)
: base(table, joinPredicate, annotations)
{
}

Expand Down Expand Up @@ -51,7 +61,7 @@ public virtual InnerJoinExpression Update(TableExpressionBase table, SqlExpressi
Check.NotNull(joinPredicate, nameof(joinPredicate));

return table != Table || joinPredicate != JoinPredicate
? new InnerJoinExpression(table, joinPredicate)
? new InnerJoinExpression(table, joinPredicate, GetAnnotations())
: this;
}

Expand All @@ -64,6 +74,7 @@ protected override void Print(ExpressionPrinter expressionPrinter)
expressionPrinter.Visit(Table);
expressionPrinter.Append(" ON ");
expressionPrinter.Visit(JoinPredicate);
PrintAnnotations(expressionPrinter);
}

/// <inheritdoc />
Expand Down
Loading

0 comments on commit ac35072

Please sign in to comment.