Skip to content

Commit

Permalink
Implement Precompiled Query Tests (#3289)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisJollyAU authored Sep 23, 2024
1 parent 79608d6 commit 87a1a7e
Show file tree
Hide file tree
Showing 9 changed files with 2,434 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public virtual PgAnyExpression Update(SqlExpression item, SqlExpression array)
public override Expression Quote()
=> New(
_quotingConstructor ??= typeof(PgAnyExpression).GetConstructor(
[typeof(SqlExpression), typeof(SqlExpression), typeof(PgAllOperatorType), typeof(RelationalTypeMapping)])!,
[typeof(SqlExpression), typeof(SqlExpression), typeof(PgAnyOperatorType), typeof(RelationalTypeMapping)])!,
Item.Quote(),
Array.Quote(),
Constant(OperatorType),
Expand Down
23 changes: 22 additions & 1 deletion src/EFCore.PG/Query/Internal/NpgsqlQueryCompilationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,25 @@ public NpgsqlQueryCompilationContext(
QueryCompilationContextDependencies dependencies,
RelationalQueryCompilationContextDependencies relationalDependencies,
bool async)
: base(dependencies, relationalDependencies, async)
: this(
dependencies, relationalDependencies, async, precompiling: false,
nonNullableReferenceTypeParameters: null)
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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 NpgsqlQueryCompilationContext(
QueryCompilationContextDependencies dependencies,
RelationalQueryCompilationContextDependencies relationalDependencies,
bool async,
bool precompiling,
IReadOnlySet<string>? nonNullableReferenceTypeParameters)
: base(dependencies, relationalDependencies, async, precompiling, nonNullableReferenceTypeParameters)
{
}

Expand All @@ -30,4 +48,7 @@ public NpgsqlQueryCompilationContext(
/// </summary>
public override bool IsBuffering
=> base.IsBuffering || QuerySplittingBehavior == Microsoft.EntityFrameworkCore.QuerySplittingBehavior.SplitQuery;

/// <inheritdoc />
public override bool SupportsPrecompiledQuery => true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,15 @@ public NpgsqlQueryCompilationContextFactory(
/// </summary>
public virtual QueryCompilationContext Create(bool async)
=> new NpgsqlQueryCompilationContext(_dependencies, _relationalDependencies, async);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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 virtual QueryCompilationContext CreatePrecompiled(bool async, IReadOnlySet<string> nonNullableReferenceTypeParameters)
=> new NpgsqlQueryCompilationContext(
_dependencies, _relationalDependencies, async, precompiling: true,
nonNullableReferenceTypeParameters);
}
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,13 @@ when patternParameter.Name.StartsWith(QueryCompilationContext.QueryParameterPref
}
}

private static string? ConstructLikePatternParameter(
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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 string? ConstructLikePatternParameter(
QueryContext queryContext,
string baseParameterName,
StartsEndsWithContains methodType)
Expand All @@ -548,10 +554,36 @@ when patternParameter.Name.StartsWith(QueryCompilationContext.QueryParameterPref
_ => throw new UnreachableException()
};

private enum StartsEndsWithContains
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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 enum StartsEndsWithContains
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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>
StartsWith,

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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>
EndsWith,

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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>
Contains
}

Expand Down
5 changes: 0 additions & 5 deletions test/EFCore.PG.FunctionalTests/NpgsqlComplianceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ public class NpgsqlComplianceTest : RelationalComplianceTestBase
typeof(UdfDbFunctionTestBase<>),
typeof(UpdateSqlGeneratorTestBase),

// Precompiled query/NativeAOT (#3257)
typeof(AdHocPrecompiledQueryRelationalTestBase),
typeof(PrecompiledQueryRelationalTestBase),
typeof(PrecompiledSqlPregenerationQueryRelationalTestBase),

// Disabled
typeof(GraphUpdatesTestBase<>),
typeof(ProxyGraphUpdatesTestBase<>),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;

public class AdHocPrecompiledQueryNpgsqlTest(ITestOutputHelper testOutputHelper)
: AdHocPrecompiledQueryRelationalTestBase(testOutputHelper)
{
protected override bool AlwaysPrintGeneratedSources
=> false;

public override async Task Index_no_evaluatability()
{
await base.Index_no_evaluatability();

AssertSql(
"""
SELECT j."Id", j."IntList", j."JsonThing"
FROM "JsonEntities" AS j
WHERE j."IntList"[j."Id" + 1] = 2
""");
}

public override async Task Index_with_captured_variable()
{
await base.Index_with_captured_variable();

AssertSql(
"""
@__id_0='1'

SELECT j."Id", j."IntList", j."JsonThing"
FROM "JsonEntities" AS j
WHERE j."IntList"[@__id_0 + 1] = 2
""");
}

public override async Task JsonScalar()
{
await base.JsonScalar();

AssertSql(
"""
SELECT j."Id", j."IntList", j."JsonThing"
FROM "JsonEntities" AS j
WHERE (j."JsonThing" ->> 'StringProperty') = 'foo'
""");
}

public override async Task Materialize_non_public()
{
await base.Materialize_non_public();

AssertSql(
"""
@p0='10' (Nullable = true)
@p1='9' (Nullable = true)
@p2='8' (Nullable = true)

INSERT INTO "NonPublicEntities" ("PrivateAutoProperty", "PrivateProperty", "_privateField")
VALUES (@p0, @p1, @p2)
RETURNING "Id";
""",
//
"""
SELECT n."Id", n."PrivateAutoProperty", n."PrivateProperty", n."_privateField"
FROM "NonPublicEntities" AS n
LIMIT 2
""");
}

[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());

protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;

protected override PrecompiledQueryTestHelpers PrecompiledQueryTestHelpers
=> NpgsqlPrecompiledQueryTestHelpers.Instance;

protected override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
{
builder = base.AddOptions(builder);

// TODO: Figure out if there's a nice way to continue using the retrying strategy
var sqlServerOptionsBuilder = new NpgsqlDbContextOptionsBuilder(builder);
sqlServerOptionsBuilder.ExecutionStrategy(d => new NonRetryingExecutionStrategy(d));
return builder;
}
}
Loading

0 comments on commit 87a1a7e

Please sign in to comment.