Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public static IServiceCollection AddEntityFrameworkClickHouse([NotNull] this ISe
.TryAdd<IEvaluatableExpressionFilter, ClickHouseEvaluatableExpressionFilter>()
.TryAdd<IQuerySqlGeneratorFactory, ClickHouseQuerySqlGeneratorFactory>()
.TryAdd<IQueryableMethodTranslatingExpressionVisitorFactory, ClickHouseQueryableMethodTranslatingExpressionVisitorFactory>()
.TryAdd<IQueryTranslationPreprocessorFactory, ClickHouseQueryTranslationPreprocessorFactory>()
.TryAdd<IRelationalSqlTranslatingExpressionVisitorFactory, ClickHouseSqlTranslatingExpressionVisitorFactory>()
.TryAdd<ISqlExpressionFactory, ClickHouseSqlExpressionFactory>()
.TryAdd<IRelationalParameterBasedSqlProcessorFactory, ClickHouseParameterBasedSqlProcessorFactory>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;

// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore;

public static class ClickHouseEnumerable
{
public static TResult Any<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
throw new InvalidOperationException();
}

public static TResult? AnyRespectNulls<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult?> selector)
{
throw new InvalidOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ public class ClickHouseAggregateMethodCallTranslatorProvider : RelationalAggrega
{
public ClickHouseAggregateMethodCallTranslatorProvider(RelationalAggregateMethodCallTranslatorProviderDependencies dependencies) : base(dependencies)
{
AddTranslators([new ClickHouseQueryableAggregateMethodTranslator(dependencies.SqlExpressionFactory)]);
AddTranslators([
new ClickHouseAggregateMethodTranslator(dependencies.SqlExpressionFactory),
new ClickHouseQueryableAggregateMethodTranslator(dependencies.SqlExpressionFactory)
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace ClickHouse.EntityFrameworkCore.Query.Internal;

public class ClickHouseAggregateMethodTranslator : IAggregateMethodCallTranslator
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;

public ClickHouseAggregateMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}

public SqlExpression? Translate(
MethodInfo method,
EnumerableExpression source,
IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
if (method.DeclaringType == typeof(ClickHouseEnumerable))
{
switch (method.Name)
{
case nameof(ClickHouseEnumerable.Any):
throw new NotImplementedException();

case nameof(ClickHouseEnumerable.AnyRespectNulls):
throw new NotImplementedException();
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using System.Linq.Expressions;

namespace ClickHouse.EntityFrameworkCore.Query.Internal;

public class ClickHouseAggregateMethodVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof(ClickHouseEnumerable) && node.Arguments.Count > 1)
{
return new EnumerableExpression(Visit(node.Arguments[1]));
}

return base.VisitMethodCall(node);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Microsoft.EntityFrameworkCore.Query;
using System.Linq.Expressions;

namespace ClickHouse.EntityFrameworkCore.Query.Internal;

public class ClickHouseQueryTranslationPreprocessor : RelationalQueryTranslationPreprocessor
{
public ClickHouseQueryTranslationPreprocessor(
QueryTranslationPreprocessorDependencies dependencies,
RelationalQueryTranslationPreprocessorDependencies relationalDependencies, QueryCompilationContext queryCompilationContext)
: base(dependencies, relationalDependencies, queryCompilationContext)
{
}

public override Expression Process(Expression query)
{
query = new ClickHouseAggregateMethodVisitor().Visit(query);

return base.Process(query);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace ClickHouse.EntityFrameworkCore.Query.Internal;

public class ClickHouseQueryTranslationPreprocessorFactory : RelationalQueryTranslationPreprocessorFactory
{
public ClickHouseQueryTranslationPreprocessorFactory(
QueryTranslationPreprocessorDependencies dependencies,
RelationalQueryTranslationPreprocessorDependencies relationalDependencies)
: base(dependencies, relationalDependencies)
{
}

public override QueryTranslationPreprocessor Create(QueryCompilationContext queryCompilationContext)
{
return new ClickHouseQueryTranslationPreprocessor(Dependencies, RelationalDependencies, queryCompilationContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Microsoft.EntityFrameworkCore.TestUtilities;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace EntityFrameworkCore.ClickHouse.FunctionalTests.Query;

public class AggregateDbFunctionsExtensionsClickHouseTest : IClassFixture<NorthwindQueryClickHouseFixture<NoopModelCustomizer>>
{
public AggregateDbFunctionsExtensionsClickHouseTest(
NorthwindQueryClickHouseFixture<NoopModelCustomizer> fixture,
ITestOutputHelper testOutputHelper)
{
Fixture = fixture;
Fixture.TestSqlLoggerFactory.Clear();
Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}

[Fact]
public async Task Any()
{
var context = CreateContext();

await context.Customers
.GroupBy(c => c.Country)
.Select(g => new { Key = g.Key, AnyCity = g.Any(e => e.City) })
.ToListAsync();

AssertSql(
"""
SELECT "c"."Country" AS "Key", any("c"."City") AS "AnyCity"
FROM "Customers" AS "c"
GROUP BY "c"."Country"
""");
}

[Fact]
public async Task AnyRespectNulls()
{
var context = CreateContext();

await context.Customers
.GroupBy(c => c.Country)
.Select(g => new { Key = g.Key, AnyRegion = g.AnyRespectNulls(e => e.Region) })
.ToListAsync();

AssertSql(
"""
SELECT "c"."Country" AS "Key", anyRespectNulls("c"."Region") AS "AnyRegion"
FROM "Customers" AS "c"
GROUP BY "c"."Country"
""");
}

protected NorthwindQueryClickHouseFixture<NoopModelCustomizer> Fixture { get; }

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);

protected NorthwindContext CreateContext()
=> Fixture.CreateContext();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.EntityFrameworkCore;

namespace EntityFrameworkCore.ClickHouse.Tests.Extensions.DbFunctionsExtensions;

public sealed class ClickHouseEnumerableTest
{
[Fact]
public void Any_ThrowsException()
{
Assert.Throws<InvalidOperationException>(() => EF.Functions.Any("test"));
}

[Fact]
public void AnyRespectNulls_ThrowsException()
{
Assert.Throws<InvalidOperationException>(() => EF.Functions.AnyRespectNulls("test"));

Check failure on line 16 in test/EntityFrameworkCore.ClickHouse.Tests/Extensions/DbFunctionsExtensions/ClickHouseEnumerableTest.cs

View workflow job for this annotation

GitHub Actions / build

'DbFunctions' does not contain a definition for 'AnyRespectNulls' and no accessible extension method 'AnyRespectNulls' accepting a first argument of type 'DbFunctions' could be found (are you missing a using directive or an assembly reference?)
}
}
Loading