Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Add support for TPC #27961

Merged
merged 1 commit into from
May 6, 2022
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
9 changes: 7 additions & 2 deletions src/EFCore.Relational/Query/EntityProjectionExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,13 @@ public virtual EntityProjectionExpression MakeNullable()
propertyExpressionMap[property] = columnExpression.MakeNullable();
}

// We don't need to process DiscriminatorExpression because they are already nullable
return new EntityProjectionExpression(EntityType, propertyExpressionMap, DiscriminatorExpression);
var discriminatorExpression = DiscriminatorExpression;
if (discriminatorExpression is ColumnExpression ce)
{
// if discriminator is column then we need to make it nullable
discriminatorExpression = ce.MakeNullable();
}
return new EntityProjectionExpression(EntityType, propertyExpressionMap, discriminatorExpression);
}

/// <summary>
Expand Down
11 changes: 10 additions & 1 deletion src/EFCore.Relational/Query/ISqlExpressionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -435,9 +435,18 @@ SqlFunctionExpression NiladicFunction(
/// </summary>
/// <param name="value">A value.</param>
/// <param name="typeMapping">The <see cref="RelationalTypeMapping" /> associated with the expression.</param>
/// <returns>An expression representing a LIKE in a SQL tree.</returns>
/// <returns>An expression representing a constant in a SQL tree.</returns>
SqlConstantExpression Constant(object? value, RelationalTypeMapping? typeMapping = null);

/// <summary>
/// Creates a new <see cref="SqlConstantExpression" /> which represents a constant in a SQL tree.
/// </summary>
/// <param name="value">A value.</param>
/// <param name="type">The type for the constant. Useful when value is null.</param>
/// <param name="typeMapping">The <see cref="RelationalTypeMapping" /> associated with the expression.</param>
/// <returns>An expression representing a constant in a SQL tree.</returns>
SqlConstantExpression Constant(object? value, Type type, RelationalTypeMapping? typeMapping = null);

/// <summary>
/// Creates a new <see cref="SqlFragmentExpression" /> which represents a SQL token.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@ private static readonly MethodInfo CollectionAccessorAddMethodInfo
private readonly IDictionary<ParameterExpression, IDictionary<IProperty, int>> _materializationContextBindings
= new Dictionary<ParameterExpression, IDictionary<IProperty, int>>();

private readonly IDictionary<ParameterExpression, int> _entityTypeIdentifyingExpressionOffsets
= new Dictionary<ParameterExpression, int>();
private readonly IDictionary<ParameterExpression, object> _entityTypeIdentifyingExpressionInfo
= new Dictionary<ParameterExpression, object>();
private readonly IDictionary<ProjectionBindingExpression, string> _singleEntityTypeDiscriminatorValues
= new Dictionary<ProjectionBindingExpression, string>();

public ShaperProcessingExpressionVisitor(
RelationalShapedQueryCompilingExpressionVisitor parentVisitor,
Expand Down Expand Up @@ -359,7 +361,13 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)

var propertyMap = (IDictionary<IProperty, int>)GetProjectionIndex(projectionBindingExpression);
_materializationContextBindings[parameterExpression] = propertyMap;
_entityTypeIdentifyingExpressionOffsets[parameterExpression] = propertyMap.Values.Max() + 1;
_entityTypeIdentifyingExpressionInfo[parameterExpression] =
// If single entity type is being selected in hierarchy then we use the value directly else we store the offset to
// read discriminator value.
_singleEntityTypeDiscriminatorValues.TryGetValue(projectionBindingExpression, out var value)
? value
: propertyMap.Values.Max() + 1;


var updatedExpression = newExpression.Update(
new[] { Expression.Constant(ValueBuffer.Empty), newExpression.Arguments[1] });
Expand Down Expand Up @@ -388,6 +396,16 @@ protected override Expression VisitExtension(Expression extensionExpression)
{
var entityParameter = Expression.Parameter(entityShaperExpression.Type);
_variables.Add(entityParameter);
if (entityShaperExpression.EntityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy)
{
var concreteTypes = entityShaperExpression.EntityType.GetDerivedTypesInclusive().Where(e => !e.IsAbstract()).ToArray();
// Single concrete TPC entity type won't have discriminator column.
// We store the value here and inject it directly rather than reading from server.
if (concreteTypes.Length == 1)
_singleEntityTypeDiscriminatorValues[(ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression]
= concreteTypes[0].ShortName();
}

var entityMaterializationExpression = _parentVisitor.InjectEntityMaterializers(entityShaperExpression);
entityMaterializationExpression = Visit(entityMaterializationExpression);

Expand Down Expand Up @@ -867,12 +885,27 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
{
var property = methodCallExpression.Arguments[2].GetConstantValue<IProperty?>();
var mappingParameter = (ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object!;
var projectionIndex = property == null
? _entityTypeIdentifyingExpressionOffsets[mappingParameter]
+ methodCallExpression.Arguments[1].GetConstantValue<int>()
: _materializationContextBindings[mappingParameter][property];
var projection = _selectExpression.Projection[projectionIndex];
int projectionIndex;
if (property == null)
{
// This is trying to read the computed discriminator value
var storedInfo = _entityTypeIdentifyingExpressionInfo[mappingParameter];
if (storedInfo is string s)
{
// If the value is fixed then there is single entity type and discriminator is not present in query
// We just return the value as-is.
return Expression.Constant(s);
}

projectionIndex = (int)_entityTypeIdentifyingExpressionInfo[mappingParameter]
+ methodCallExpression.Arguments[1].GetConstantValue<int>();
}
else
{
projectionIndex = _materializationContextBindings[mappingParameter][property];
}

var projection = _selectExpression.Projection[projectionIndex];
var nullable = IsNullableProjection(projection);

Check.DebugAssert(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,13 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp
var discriminatorProperty = entityType.FindDiscriminatorProperty();
if (discriminatorProperty == null)
{
// TPT
if (entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy
&& entityType.GetDerivedTypesInclusive().Count(e => !e.IsAbstract()) == 1)
{
return _sqlExpressionFactory.Constant(true);
}

// TPT or TPC
var discriminatorValues = derivedType.GetTptDiscriminatorValues();
if (entityReferenceExpression.SubqueryEntity != null)
{
Expand Down
4 changes: 4 additions & 0 deletions src/EFCore.Relational/Query/SqlExpressionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,10 @@ public virtual SqlFragmentExpression Fragment(string sql)
public virtual SqlConstantExpression Constant(object? value, RelationalTypeMapping? typeMapping = null)
=> new(Expression.Constant(value), typeMapping);

/// <inheritdoc />
public virtual SqlConstantExpression Constant(object? value, Type type, RelationalTypeMapping? typeMapping = null)
=> new(Expression.Constant(value, type), typeMapping);

/// <inheritdoc />
public virtual SelectExpression Select(SqlExpression? projection)
=> new(projection);
Expand Down
Loading