Skip to content

Commit

Permalink
Pregenerate SQL for precompiled queries (#33510)
Browse files Browse the repository at this point in the history
Closes #29753
  • Loading branch information
roji authored May 3, 2024
1 parent a970d4f commit cd8e049
Show file tree
Hide file tree
Showing 44 changed files with 1,341 additions and 194 deletions.
3 changes: 3 additions & 0 deletions EFCore.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ The .NET Foundation licenses this file to you under the MIT license.
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgre/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precompilation/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precompiling/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregenerate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregenerated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregeneration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=prunable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precompilation/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pubternal/@EntryIndexedValue">True</s:Boolean>
Expand Down
26 changes: 24 additions & 2 deletions src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,15 @@ public override Expression VisitBinaryExpression(BinaryExpressionSyntax binary)
var left = Visit(binary.Left);
var right = Visit(binary.Right);

if (Nullable.GetUnderlyingType(left.Type) == right.Type)
{
right = Convert(right, left.Type);
}
else if (Nullable.GetUnderlyingType(right.Type) == left.Type)
{
left = Convert(left, right.Type);
}

// https://learn.microsoft.com/dotnet/api/Microsoft.CodeAnalysis.CSharp.Syntax.BinaryExpressionSyntax
return binary.Kind() switch
{
Expand Down Expand Up @@ -406,7 +415,8 @@ public override Expression VisitIdentifierName(IdentifierNameSyntax identifierNa
new FakeFieldInfo(
typeof(FakeClosureFrameClass),
ResolveType(localSymbol.Type),
localSymbol.Name)));
localSymbol.Name,
localSymbol.NullableAnnotation is NullableAnnotation.NotAnnotated)));
}

throw new InvalidOperationException(
Expand Down Expand Up @@ -1123,6 +1133,11 @@ private Type ResolveType(ITypeSymbol typeSymbol, Dictionary<string, Type>? gener

Type GetClrType(INamedTypeSymbol symbol)
{
if (symbol.SpecialType == SpecialType.System_Nullable_T)
{
return typeof(Nullable<>);
}

var name = symbol.ContainingType is null
? typeSymbol.ToDisplayString(QualifiedTypeNameSymbolDisplayFormat)
: typeSymbol.Name;
Expand Down Expand Up @@ -1205,8 +1220,15 @@ public int GetHashCode(T[] obj)
[CompilerGenerated]
private sealed class FakeClosureFrameClass;

private sealed class FakeFieldInfo(Type declaringType, Type fieldType, string name) : FieldInfo
private sealed class FakeFieldInfo(
Type declaringType,
Type fieldType,
string name,
bool isNonNullableReferenceType)
: FieldInfo, IParameterNullabilityInfo
{
public bool IsNonNullableReferenceType { get; } = isNonNullableReferenceType;

public override object[] GetCustomAttributes(bool inherit)
=> Array.Empty<object>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,10 @@ private void GenerateQueryExecutor(
.IncrementIndent()
.AppendLine("var relationalModel = dbContext.Model.GetRelationalModel();")
.AppendLine("var relationalTypeMappingSource = dbContext.GetService<IRelationalTypeMappingSource>();")
.AppendLine("var materializerLiftableConstantContext = new RelationalMaterializerLiftableConstantContext(dbContext.GetService<ShapedQueryCompilingExpressionVisitorDependencies>(), dbContext.GetService<RelationalShapedQueryCompilingExpressionVisitorDependencies>());");
.AppendLine("var materializerLiftableConstantContext = new RelationalMaterializerLiftableConstantContext(")
.AppendLine(" dbContext.GetService<ShapedQueryCompilingExpressionVisitorDependencies>(),")
.AppendLine(" dbContext.GetService<RelationalShapedQueryCompilingExpressionVisitorDependencies>(),")
.AppendLine(" dbContext.GetService<RelationalCommandBuilderDependencies>());");

HashSet<string> variableNames = ["relationalModel", "relationalTypeMappingSource", "materializerLiftableConstantContext"];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Internal;
/// 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>
public static class RelationCommandCacheExtensions
public static class RelationalCommandResolverExtensions
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -21,10 +21,10 @@ public static class RelationCommandCacheExtensions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static IRelationalCommand RentAndPopulateRelationalCommand(
this RelationalCommandCache relationalCommandCache,
this RelationalCommandResolver relationalCommandResolver,
RelationalQueryContext queryContext)
{
var relationalCommandTemplate = relationalCommandCache.GetRelationalCommandTemplate(queryContext.ParameterValues);
var relationalCommandTemplate = relationalCommandResolver(queryContext.ParameterValues);
var relationalCommand = queryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
return relationalCommand;
Expand Down
25 changes: 12 additions & 13 deletions src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static class FromSqlQueryingEnumerable
/// </summary>
public static FromSqlQueryingEnumerable<T> Create<T>(
RelationalQueryContext relationalQueryContext,
RelationalCommandCache relationalCommandCache,
RelationalCommandResolver relationalCommandResolver,
IReadOnlyList<ReaderColumn?>? readerColumns,
IReadOnlyList<string> columnNames,
Func<QueryContext, DbDataReader, int[], T> shaper,
Expand All @@ -32,7 +32,7 @@ public static FromSqlQueryingEnumerable<T> Create<T>(
bool threadSafetyChecksEnabled)
=> new(
relationalQueryContext,
relationalCommandCache,
relationalCommandResolver,
readerColumns,
columnNames,
shaper,
Expand All @@ -51,7 +51,7 @@ public static FromSqlQueryingEnumerable<T> Create<T>(
public class FromSqlQueryingEnumerable<T> : IEnumerable<T>, IAsyncEnumerable<T>, IRelationalQueryingEnumerable
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly IReadOnlyList<string> _columnNames;
private readonly Func<QueryContext, DbDataReader, int[], T> _shaper;
Expand All @@ -69,7 +69,7 @@ public class FromSqlQueryingEnumerable<T> : IEnumerable<T>, IAsyncEnumerable<T>,
/// </summary>
public FromSqlQueryingEnumerable(
RelationalQueryContext relationalQueryContext,
RelationalCommandCache relationalCommandCache,
RelationalCommandResolver relationalCommandResolver,
IReadOnlyList<ReaderColumn?>? readerColumns,
IReadOnlyList<string> columnNames,
Func<QueryContext, DbDataReader, int[], T> shaper,
Expand All @@ -79,7 +79,7 @@ public FromSqlQueryingEnumerable(
bool threadSafetyChecksEnabled)
{
_relationalQueryContext = relationalQueryContext;
_relationalCommandCache = relationalCommandCache;
_relationalCommandResolver = relationalCommandResolver;
_readerColumns = readerColumns;
_columnNames = columnNames;
_shaper = shaper;
Expand Down Expand Up @@ -128,8 +128,7 @@ IEnumerator IEnumerable.GetEnumerator()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual DbCommand CreateDbCommand()
=> _relationalCommandCache
.GetRelationalCommandTemplate(_relationalQueryContext.ParameterValues)
=> _relationalCommandResolver(_relationalQueryContext.ParameterValues)
.CreateDbCommand(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
Expand Down Expand Up @@ -187,7 +186,7 @@ public static int[] BuildIndexMap(IReadOnlyList<string> columnNames, DbDataReade
private sealed class Enumerator : IEnumerator<T>
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly IReadOnlyList<string> _columnNames;
private readonly Func<QueryContext, DbDataReader, int[], T> _shaper;
Expand All @@ -205,7 +204,7 @@ private sealed class Enumerator : IEnumerator<T>
public Enumerator(FromSqlQueryingEnumerable<T> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_relationalCommandResolver = queryingEnumerable._relationalCommandResolver;
_readerColumns = queryingEnumerable._readerColumns;
_columnNames = queryingEnumerable._columnNames;
_shaper = queryingEnumerable._shaper;
Expand Down Expand Up @@ -272,7 +271,7 @@ private static bool InitializeReader(Enumerator enumerator)
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommand =
enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);
enumerator._relationalCommandResolver.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -307,7 +306,7 @@ public void Reset()
private sealed class AsyncEnumerator : IAsyncEnumerator<T>
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly IReadOnlyList<string> _columnNames;
private readonly Func<QueryContext, DbDataReader, int[], T> _shaper;
Expand All @@ -325,7 +324,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<T>
public AsyncEnumerator(FromSqlQueryingEnumerable<T> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_relationalCommandResolver = queryingEnumerable._relationalCommandResolver;
_readerColumns = queryingEnumerable._readerColumns;
_columnNames = queryingEnumerable._columnNames;
_shaper = queryingEnumerable._shaper;
Expand Down Expand Up @@ -394,7 +393,7 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommand =
enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);
enumerator._relationalCommandResolver.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static class GroupBySingleQueryingEnumerable
/// </summary>
public static GroupBySingleQueryingEnumerable<TKey, TElement> Create<TKey, TElement>(
RelationalQueryContext relationalQueryContext,
RelationalCommandCache relationalCommandCache,
RelationalCommandResolver relationalCommandResolver,
IReadOnlyList<ReaderColumn?>? readerColumns,
Func<QueryContext, DbDataReader, TKey> keySelector,
Func<QueryContext, DbDataReader, object[]> keyIdentifier,
Expand All @@ -34,7 +34,7 @@ public static GroupBySingleQueryingEnumerable<TKey, TElement> Create<TKey, TElem
bool threadSafetyChecksEnabled)
=> new(
relationalQueryContext,
relationalCommandCache,
relationalCommandResolver,
readerColumns,
keySelector,
keyIdentifier,
Expand All @@ -56,7 +56,7 @@ public class GroupBySingleQueryingEnumerable<TKey, TElement>
: IEnumerable<IGrouping<TKey, TElement>>, IAsyncEnumerable<IGrouping<TKey, TElement>>, IRelationalQueryingEnumerable
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly Func<QueryContext, DbDataReader, TKey> _keySelector;
private readonly Func<QueryContext, DbDataReader, object[]> _keyIdentifier;
Expand All @@ -76,7 +76,7 @@ public class GroupBySingleQueryingEnumerable<TKey, TElement>
/// </summary>
public GroupBySingleQueryingEnumerable(
RelationalQueryContext relationalQueryContext,
RelationalCommandCache relationalCommandCache,
RelationalCommandResolver relationalCommandResolver,
IReadOnlyList<ReaderColumn?>? readerColumns,
Func<QueryContext, DbDataReader, TKey> keySelector,
Func<QueryContext, DbDataReader, object[]> keyIdentifier,
Expand All @@ -88,7 +88,7 @@ public GroupBySingleQueryingEnumerable(
bool threadSafetyChecksEnabled)
{
_relationalQueryContext = relationalQueryContext;
_relationalCommandCache = relationalCommandCache;
_relationalCommandResolver = relationalCommandResolver;
_readerColumns = readerColumns;
_keySelector = keySelector;
_keyIdentifier = keyIdentifier;
Expand Down Expand Up @@ -139,8 +139,7 @@ IEnumerator IEnumerable.GetEnumerator()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual DbCommand CreateDbCommand()
=> _relationalCommandCache
.GetRelationalCommandTemplate(_relationalQueryContext.ParameterValues)
=> _relationalCommandResolver(_relationalQueryContext.ParameterValues)
.CreateDbCommand(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
Expand Down Expand Up @@ -196,7 +195,7 @@ private static bool CompareIdentifiers(IReadOnlyList<Func<object, object, bool>>
private sealed class Enumerator : IEnumerator<IGrouping<TKey, TElement>>
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly Func<QueryContext, DbDataReader, TKey> _keySelector;
private readonly Func<QueryContext, DbDataReader, object[]> _keyIdentifier;
Expand All @@ -217,7 +216,7 @@ private sealed class Enumerator : IEnumerator<IGrouping<TKey, TElement>>
public Enumerator(GroupBySingleQueryingEnumerable<TKey, TElement> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_relationalCommandResolver = queryingEnumerable._relationalCommandResolver;
_readerColumns = queryingEnumerable._readerColumns;
_keySelector = queryingEnumerable._keySelector;
_keyIdentifier = queryingEnumerable._keyIdentifier;
Expand Down Expand Up @@ -342,7 +341,7 @@ private static bool InitializeReader(Enumerator enumerator)
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommand =
enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);
enumerator._relationalCommandResolver.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

var dataReader = enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -380,7 +379,7 @@ public void Reset()
private sealed class AsyncEnumerator : IAsyncEnumerator<IGrouping<TKey, TElement>>
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly Func<QueryContext, DbDataReader, TKey> _keySelector;
private readonly Func<QueryContext, DbDataReader, object[]> _keyIdentifier;
Expand All @@ -402,7 +401,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<IGrouping<TKey, TElement
public AsyncEnumerator(GroupBySingleQueryingEnumerable<TKey, TElement> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_relationalCommandResolver = queryingEnumerable._relationalCommandResolver;
_readerColumns = queryingEnumerable._readerColumns;
_keySelector = queryingEnumerable._keySelector;
_keyIdentifier = queryingEnumerable._keyIdentifier;
Expand Down Expand Up @@ -529,7 +528,7 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommand =
enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);
enumerator._relationalCommandResolver.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

var dataReader = enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Expand Down
Loading

0 comments on commit cd8e049

Please sign in to comment.