Skip to content

Commit e50071f

Browse files
committed
Tests [2/?]
Added several functions to work with math and date
1 parent 1babd7a commit e50071f

38 files changed

+2666
-74
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using EntityFrameworkCore.Ydb.Utilities;
6+
using Microsoft.EntityFrameworkCore;
7+
using Microsoft.EntityFrameworkCore.Diagnostics;
8+
using Microsoft.EntityFrameworkCore.Query;
9+
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
10+
11+
namespace EntityFrameworkCore.Ydb.Query.Internal.Translators;
12+
13+
public class YdbByteArrayMethodTranslator : IMethodCallTranslator
14+
{
15+
private readonly ISqlExpressionFactory _sqlExpressionFactory;
16+
17+
public YdbByteArrayMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
18+
=> _sqlExpressionFactory = sqlExpressionFactory;
19+
20+
private static MethodInfo Contains => typeof(Enumerable)
21+
.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
22+
.Where(m => m.Name == nameof(Enumerable.Contains))
23+
.Single(mi => mi.IsGenericMethod &&
24+
mi.GetGenericArguments().Length == 1 &&
25+
mi.GetParameters()
26+
.Select(e => e.ParameterType)
27+
.SequenceEqual(
28+
[
29+
typeof(IEnumerable<>).MakeGenericType(mi.GetGenericArguments()[0]),
30+
mi.GetGenericArguments()[0]
31+
]
32+
)
33+
);
34+
35+
public virtual SqlExpression? Translate(
36+
SqlExpression? instance,
37+
MethodInfo method,
38+
IReadOnlyList<SqlExpression> arguments,
39+
IDiagnosticsLogger<DbLoggerCategory.Query> logger
40+
)
41+
{
42+
if (method.IsGenericMethod
43+
&& method.GetGenericMethodDefinition().Equals(Contains)
44+
&& arguments[0].Type == typeof(byte[]))
45+
{
46+
var source = arguments[0];
47+
48+
var value = arguments[1] is SqlConstantExpression constantValue
49+
? _sqlExpressionFactory.Constant(new[] { (byte)constantValue.Value! }, source.TypeMapping)
50+
: _sqlExpressionFactory.Function(
51+
"ToBytes",
52+
[arguments[1]],
53+
nullable: false,
54+
argumentsPropagateNullability: ArrayUtil.TrueArrays[1],
55+
typeof(string));
56+
57+
return _sqlExpressionFactory.IsNotNull(
58+
_sqlExpressionFactory.Function(
59+
"FIND",
60+
[source, value],
61+
nullable: true,
62+
argumentsPropagateNullability: ArrayUtil.FalseArrays[2],
63+
typeof(int)
64+
)
65+
);
66+
}
67+
return null;
68+
}
69+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Reflection;
5+
using EntityFrameworkCore.Ydb.Storage.Internal;
6+
using EntityFrameworkCore.Ydb.Storage.Internal.Mapping;
7+
using EntityFrameworkCore.Ydb.Utilities;
8+
using Microsoft.EntityFrameworkCore;
9+
using Microsoft.EntityFrameworkCore.Diagnostics;
10+
using Microsoft.EntityFrameworkCore.Query;
11+
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
12+
using Microsoft.EntityFrameworkCore.Storage;
13+
14+
namespace EntityFrameworkCore.Ydb.Query.Internal.Translators;
15+
16+
public class YdbDateTimeMemberTranslator : IMemberTranslator
17+
{
18+
private readonly IRelationalTypeMappingSource _typeMappingSource;
19+
private readonly YdbSqlExpressionFactory _sqlExpressionFactory;
20+
private readonly RelationalTypeMapping _timestampMapping;
21+
22+
public YdbDateTimeMemberTranslator(
23+
IRelationalTypeMappingSource typeMappingSource,
24+
YdbSqlExpressionFactory sqlExpressionFactory
25+
)
26+
{
27+
_typeMappingSource = typeMappingSource;
28+
_timestampMapping = typeMappingSource.FindMapping(typeof(DateTime), "TimeStamp")!;
29+
_sqlExpressionFactory = sqlExpressionFactory;
30+
}
31+
32+
public virtual SqlExpression? Translate(
33+
SqlExpression? instance,
34+
MemberInfo member,
35+
Type returnType,
36+
IDiagnosticsLogger<DbLoggerCategory.Query> logger
37+
)
38+
{
39+
var declaringType = member.DeclaringType;
40+
41+
if (declaringType == typeof(TimeOnly))
42+
{
43+
throw new InvalidOperationException("Ydb doesn't support TimeOnly right now");
44+
}
45+
46+
if (declaringType != typeof(DateTime) && declaringType != typeof(DateOnly))
47+
{
48+
return null;
49+
}
50+
51+
if (member.Name == nameof(DateTime.Date))
52+
{
53+
switch (instance)
54+
{
55+
case { TypeMapping: YdbDateTimeTypeMapping }:
56+
case { Type: var type } when type == typeof(DateTime):
57+
return _sqlExpressionFactory.Convert(
58+
_sqlExpressionFactory.Convert(instance, typeof(DateOnly)),
59+
typeof(DateTime)
60+
);
61+
case { TypeMapping: YdbDateOnlyTypeMapping }:
62+
case { Type: var type } when type == typeof(DateOnly):
63+
return instance;
64+
default:
65+
return null;
66+
}
67+
}
68+
69+
return member.Name switch
70+
{
71+
// TODO: Find out how to add
72+
// nameof(DateTime.Now) => ???,
73+
// nameof(DateTime.Today) => ???
74+
75+
nameof(DateTime.UtcNow) => UtcNow(),
76+
77+
nameof(DateTime.Year) => DatePart(instance!, "GetYear"),
78+
nameof(DateTime.Month) => DatePart(instance!, "GetMonth"),
79+
nameof(DateTime.Day) => DatePart(instance!, "GetDayOfMonth"),
80+
nameof(DateTime.Hour) => DatePart(instance!, "GetHour"),
81+
nameof(DateTime.Minute) => DatePart(instance!, "GetMinute"),
82+
nameof(DateTime.Second) => DatePart(instance!, "GetSecond"),
83+
nameof(DateTime.Millisecond) => DatePart(instance!, "GetMillisecondOfSecond"),
84+
85+
nameof(DateTime.DayOfYear) => DatePart(instance!, "GetDayOfYear"),
86+
nameof(DateTime.DayOfWeek) => DatePart(instance!, "GetDayOfWeek"),
87+
88+
// TODO: Research if it's possible to implement
89+
nameof(DateTime.Ticks) => null,
90+
_ => null
91+
};
92+
93+
SqlExpression UtcNow()
94+
=> _sqlExpressionFactory.Function(
95+
"CurrentUtc" + returnType.Name == "DateOnly" ? "Date" : returnType.Name,
96+
[],
97+
nullable: false,
98+
argumentsPropagateNullability: ArrayUtil.TrueArrays[0],
99+
returnType,
100+
_typeMappingSource.FindMapping(returnType)
101+
);
102+
}
103+
104+
private SqlExpression? DatePart(SqlExpression instance, string partName)
105+
{
106+
var result = _sqlExpressionFactory.Function(
107+
$"DateTime::{partName}",
108+
[instance],
109+
nullable: true,
110+
argumentsPropagateNullability: ArrayUtil.TrueArrays[1],
111+
typeof(short) // Doesn't matter because we cast it to int in next line anyway
112+
);
113+
114+
return _sqlExpressionFactory.Convert(result, typeof(int));
115+
}
116+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq.Expressions;
4+
using System.Reflection;
5+
using EntityFrameworkCore.Ydb.Utilities;
6+
using Microsoft.EntityFrameworkCore;
7+
using Microsoft.EntityFrameworkCore.Diagnostics;
8+
using Microsoft.EntityFrameworkCore.Query;
9+
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
10+
using Microsoft.EntityFrameworkCore.Storage;
11+
12+
namespace EntityFrameworkCore.Ydb.Query.Internal.Translators;
13+
14+
public class YdbDateTimeMethodTranslator : IMethodCallTranslator
15+
{
16+
private static readonly Dictionary<MethodInfo, string> MethodInfoDatePartMapping = new()
17+
{
18+
{ typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddYears), [typeof(int)])!, " Years" },
19+
{ typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddMonths), [typeof(int)])!, " Months" },
20+
{ typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddDays), [typeof(int)])!, " Days" },
21+
22+
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddYears), [typeof(int)])!, "Years" },
23+
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMonths), [typeof(int)])!, "Months" },
24+
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddDays), [typeof(double)])!, "Days" },
25+
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddHours), [typeof(double)])!, "Hours" },
26+
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMinutes), [typeof(double)])!, "Mins" },
27+
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddSeconds), [typeof(double)])!, "Secs" },
28+
29+
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddYears), [typeof(int)])!, "Years" },
30+
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMonths), [typeof(int)])!, "Months" },
31+
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddDays), [typeof(double)])!, "Days" },
32+
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddHours), [typeof(double)])!, "Hours" },
33+
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMinutes), [typeof(double)])!, "Mins" },
34+
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddSeconds), [typeof(double)])!, "Secs" },
35+
};
36+
37+
private readonly YdbSqlExpressionFactory _sqlExpressionFactory;
38+
39+
public YdbDateTimeMethodTranslator(YdbSqlExpressionFactory sqlExpressionFactory)
40+
{
41+
_sqlExpressionFactory = sqlExpressionFactory;
42+
}
43+
44+
45+
public virtual SqlExpression? Translate(
46+
SqlExpression? instance,
47+
MethodInfo method,
48+
IReadOnlyList<SqlExpression> arguments,
49+
IDiagnosticsLogger<DbLoggerCategory.Query> logger
50+
) => TranslateDatePart(instance, method, arguments);
51+
52+
private SqlExpression? TranslateDatePart(
53+
SqlExpression? instance,
54+
MethodInfo method,
55+
IReadOnlyList<SqlExpression> arguments
56+
)
57+
{
58+
if (
59+
instance is not null
60+
&& MethodInfoDatePartMapping.TryGetValue(method, out var datePart))
61+
{
62+
var shiftDatePartFunction = _sqlExpressionFactory.Function(
63+
"DateTime::Shift" + datePart,
64+
[instance, arguments[0]],
65+
nullable: true,
66+
ArrayUtil.TrueArrays[2],
67+
returnType: typeof(DateTime)
68+
);
69+
70+
return _sqlExpressionFactory.Function(
71+
"DateTime::MakeDate",
72+
arguments: [shiftDatePartFunction],
73+
nullable: true,
74+
ArrayUtil.TrueArrays[1],
75+
returnType: typeof(DateTime)
76+
);
77+
}
78+
79+
return null;
80+
}
81+
}

0 commit comments

Comments
 (0)