Skip to content

Commit 1464058

Browse files
committed
Additional refactoring of Null Semantics:
- moving NullSemantics visitor after 2nd level cache - we need to know the parameter values to properly handle IN expressions wrt null semantics, - NullSemantics visitor needs to go before SqlExpressionOptimizer and SearchCondition, so those two are also moved after 2nd level cache, - moving optimizations that depend on knowing the nullability to NullSemantics visitor - optimizer now only contains optimizations that also work in 3-value logic, or when we know nulls can't happen, - merging InExpressionValuesExpandingExpressionVisitor int NullSemantics visitor, so that we don't apply the rewrite for UseRelationalNulls, - preventing NulSemantics from performing double visitation when computing non-nullable columns. Resolves #11464 Resolves #15722 Resolves #18338 Resolves #18597 Resolves #18689 Resolves #19019
1 parent 79d37f9 commit 1464058

30 files changed

+2353
-1662
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Data.Common;
7+
using System.Linq.Expressions;
8+
using Microsoft.EntityFrameworkCore.Internal;
9+
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
10+
using Microsoft.EntityFrameworkCore.Storage;
11+
using Microsoft.EntityFrameworkCore.Storage.Internal;
12+
13+
namespace Microsoft.EntityFrameworkCore.Query
14+
{
15+
public class FromSqlParameterApplyingExpressionVisitor : ExpressionVisitor
16+
{
17+
private readonly IDictionary<FromSqlExpression, Expression> _visitedFromSqlExpressions
18+
= new Dictionary<FromSqlExpression, Expression>(ReferenceEqualityComparer.Instance);
19+
20+
private readonly ISqlExpressionFactory _sqlExpressionFactory;
21+
private readonly ParameterNameGenerator _parameterNameGenerator;
22+
private readonly IReadOnlyDictionary<string, object> _parametersValues;
23+
24+
public FromSqlParameterApplyingExpressionVisitor(
25+
ISqlExpressionFactory sqlExpressionFactory,
26+
ParameterNameGenerator parameterNameGenerator,
27+
IReadOnlyDictionary<string, object> parametersValues)
28+
{
29+
_sqlExpressionFactory = sqlExpressionFactory;
30+
_parameterNameGenerator = parameterNameGenerator;
31+
_parametersValues = parametersValues;
32+
}
33+
34+
public override Expression Visit(Expression expression)
35+
{
36+
if (expression is FromSqlExpression fromSql)
37+
{
38+
if (!_visitedFromSqlExpressions.TryGetValue(fromSql, out var updatedFromSql))
39+
{
40+
switch (fromSql.Arguments)
41+
{
42+
case ParameterExpression parameterExpression:
43+
var parameterValues = (object[])_parametersValues[parameterExpression.Name];
44+
45+
var subParameters = new List<IRelationalParameter>(parameterValues.Length);
46+
// ReSharper disable once ForCanBeConvertedToForeach
47+
for (var i = 0; i < parameterValues.Length; i++)
48+
{
49+
var parameterName = _parameterNameGenerator.GenerateNext();
50+
if (parameterValues[i] is DbParameter dbParameter)
51+
{
52+
if (string.IsNullOrEmpty(dbParameter.ParameterName))
53+
{
54+
dbParameter.ParameterName = parameterName;
55+
}
56+
else
57+
{
58+
parameterName = dbParameter.ParameterName;
59+
}
60+
61+
subParameters.Add(new RawRelationalParameter(parameterName, dbParameter));
62+
}
63+
else
64+
{
65+
subParameters.Add(
66+
new TypeMappedRelationalParameter(
67+
parameterName,
68+
parameterName,
69+
_sqlExpressionFactory.GetTypeMappingForValue(parameterValues[i]),
70+
parameterValues[i]?.GetType().IsNullableType()));
71+
}
72+
}
73+
74+
updatedFromSql = new FromSqlExpression(
75+
fromSql.Sql,
76+
Expression.Constant(
77+
new CompositeRelationalParameter(
78+
parameterExpression.Name,
79+
subParameters)),
80+
fromSql.Alias);
81+
82+
_visitedFromSqlExpressions[fromSql] = updatedFromSql;
83+
break;
84+
85+
case ConstantExpression constantExpression:
86+
var existingValues = (object[])constantExpression.Value;
87+
var constantValues = new object[existingValues.Length];
88+
for (var i = 0; i < existingValues.Length; i++)
89+
{
90+
var value = existingValues[i];
91+
if (value is DbParameter dbParameter)
92+
{
93+
var parameterName = _parameterNameGenerator.GenerateNext();
94+
if (string.IsNullOrEmpty(dbParameter.ParameterName))
95+
{
96+
dbParameter.ParameterName = parameterName;
97+
}
98+
else
99+
{
100+
parameterName = dbParameter.ParameterName;
101+
}
102+
103+
constantValues[i] = new RawRelationalParameter(parameterName, dbParameter);
104+
}
105+
else
106+
{
107+
constantValues[i] = _sqlExpressionFactory.Constant(
108+
value, _sqlExpressionFactory.GetTypeMappingForValue(value));
109+
}
110+
}
111+
112+
updatedFromSql = new FromSqlExpression(
113+
fromSql.Sql,
114+
Expression.Constant(constantValues, typeof(object[])),
115+
fromSql.Alias);
116+
117+
_visitedFromSqlExpressions[fromSql] = updatedFromSql;
118+
break;
119+
}
120+
}
121+
122+
return updatedFromSql;
123+
}
124+
125+
return base.Visit(expression);
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)