Skip to content

Commit

Permalink
Pregenerate SQL for precompiled queries
Browse files Browse the repository at this point in the history
Closes #29753
  • Loading branch information
roji committed Apr 11, 2024
1 parent f9095d8 commit d53215a
Show file tree
Hide file tree
Showing 44 changed files with 1,343 additions and 191 deletions.
5 changes: 5 additions & 0 deletions EFCore.sln.DotSettings
Expand Up @@ -216,6 +216,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=042e7460_002D3ec6_002D4f1c_002D87c3_002Dfc91240d4c90/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Public" Description="Test Methods"&gt;&lt;ElementKinds&gt;&lt;Kind Name="TEST_MEMBER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="Aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EF/@EntryIndexedValue">EF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
Expand Down Expand Up @@ -280,6 +281,7 @@ The .NET Foundation licenses this file to you under the MIT license.&#xD;
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsCodeFormatterSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsParsFormattingSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsWrapperSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
Expand Down Expand Up @@ -326,6 +328,9 @@ The .NET Foundation licenses this file to you under the MIT license.&#xD;
<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
27 changes: 25 additions & 2 deletions src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs
Expand Up @@ -229,6 +229,16 @@ public override Expression VisitBinaryExpression(BinaryExpressionSyntax binary)
var left = Visit(binary.Left);
var right = Visit(binary.Right);

// TODO: This most probably needs to be more general, not just for binary
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 @@ -413,7 +423,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 @@ -1131,6 +1142,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 @@ -1215,8 +1231,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
Expand Up @@ -851,7 +851,10 @@ void ProcessCapturedVariables()
.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
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
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 class FromSqlQueryingEnumerable
bool threadSafetyChecksEnabled)
=> new(
relationalQueryContext,
relationalCommandCache,
relationalCommandResolver,
readerColumns,
columnNames,
shaper,
Expand All @@ -51,7 +51,7 @@ public static class FromSqlQueryingEnumerable
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 class FromSqlQueryingEnumerable<T> : IEnumerable<T>, IAsyncEnumerable<T>,
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

0 comments on commit d53215a

Please sign in to comment.