-
Notifications
You must be signed in to change notification settings - Fork 223
/
NpgsqlNodaTimeMethodCallTranslatorPlugin.cs
114 lines (105 loc) · 4.9 KB
/
NpgsqlNodaTimeMethodCallTranslatorPlugin.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
using System;
using System.Collections.Generic;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using NodaTime;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal;
// ReSharper disable once CheckNamespace
namespace Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime
{
/// <summary>
/// Provides translation services for <see cref="NodaTime"/> members.
/// </summary>
/// <remarks>
/// See: https://www.postgresql.org/docs/current/static/functions-datetime.html
/// </remarks>
public class NpgsqlNodaTimeMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
{
public NpgsqlNodaTimeMethodCallTranslatorPlugin(ISqlExpressionFactory sqlExpressionFactory)
{
Translators = new IMethodCallTranslator[]
{
new NpgsqlNodaTimeMethodCallTranslator((NpgsqlSqlExpressionFactory)sqlExpressionFactory),
};
}
public virtual IEnumerable<IMethodCallTranslator> Translators { get; }
}
/// <summary>
/// Provides translation services for NodaTime method calls.
/// </summary>
public class NpgsqlNodaTimeMethodCallTranslator : IMethodCallTranslator
{
readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory;
/// <summary>
/// The static method info for <see cref="T:SystemClock.GetCurrentInstant()"/>.
/// </summary>
[NotNull] static readonly MethodInfo GetCurrentInstant =
typeof(SystemClock).GetRuntimeMethod(nameof(SystemClock.GetCurrentInstant), Type.EmptyTypes);
/// <summary>
/// The mapping of supported method translations.
/// </summary>
[NotNull] static readonly Dictionary<MethodInfo, string> PeriodMethodMap = new Dictionary<MethodInfo, string>
{
{ typeof(Period).GetRuntimeMethod(nameof(Period.FromYears), new[] { typeof(int) }), "years" },
{ typeof(Period).GetRuntimeMethod(nameof(Period.FromMonths), new[] { typeof(int) }), "months" },
{ typeof(Period).GetRuntimeMethod(nameof(Period.FromWeeks), new[] { typeof(int) }), "weeks" },
{ typeof(Period).GetRuntimeMethod(nameof(Period.FromDays), new[] { typeof(int) }), "days" },
{ typeof(Period).GetRuntimeMethod(nameof(Period.FromHours), new[] { typeof(long) }), "hours" },
{ typeof(Period).GetRuntimeMethod(nameof(Period.FromMinutes), new[] { typeof(long) }), "mins" },
{ typeof(Period).GetRuntimeMethod(nameof(Period.FromSeconds), new[] { typeof(long) }), "secs" },
//{ typeof(Period).GetRuntimeMethod(nameof(Period.FromMilliseconds), new[] { typeof(long) }), "" },
//{ typeof(Period).GetRuntimeMethod(nameof(Period.FromNanoseconds), new[] { typeof(long) }), "" },
};
static readonly bool[][] TrueArrays =
{
Array.Empty<bool>(),
new[] { true },
new[] { true, true },
};
public NpgsqlNodaTimeMethodCallTranslator(NpgsqlSqlExpressionFactory sqlExpressionFactory)
=> _sqlExpressionFactory = sqlExpressionFactory;
#pragma warning disable EF1001
/// <inheritdoc />
public SqlExpression Translate(
SqlExpression instance,
MethodInfo method,
IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
if (method == GetCurrentInstant)
{
return _sqlExpressionFactory.AtTimeZone(
_sqlExpressionFactory.Function(
"NOW",
Array.Empty<SqlExpression>(),
nullable: false,
argumentsPropagateNullability: Array.Empty<bool>(),
method.ReturnType),
_sqlExpressionFactory.Constant("UTC"),
method.ReturnType);
}
var declaringType = method.DeclaringType;
if (declaringType == typeof(Period))
{
return PeriodMethodMap.TryGetValue(method, out var datePart)
? PostgresFunctionExpression.CreateWithNamedArguments(
"MAKE_INTERVAL",
new[] { _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0]) },
new[] { datePart },
nullable: true,
argumentsPropagateNullability: TrueArrays[1],
builtIn: true,
typeof(Period),
typeMapping: null)
: null;
}
return null;
}
#pragma warning restore EF1001
}
}