Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Translation for to_date and to_timestamp as DBFunctions extensions #2936

Merged
merged 13 commits into from
Mar 20, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,24 @@ public static int Distance(this DbFunctions _, DateOnly a, DateOnly b)
/// </remarks>
public static TimeSpan Distance(this DbFunctions _, DateTime a, DateTime b)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance)));

/// <summary>
/// Converts string to date according to the given format.
/// </summary>
/// <param name="_">The <see cref="DbFunctions"/> instance.</param>
/// <param name="value">The string to be converted.</param>
/// <param name="format">The format of the input date.</param>
/// <see href="https://www.postgresql.org/docs/current/functions-formatting.html" />
public static DateOnly? ToDate(this DbFunctions _, string? value, string? format)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(ToDate)));

/// <summary>
/// Converts string to time stamp according to the given format.
/// </summary>
/// <param name="_">The <see cref="DbFunctions"/> instance.</param>
/// <param name="value">The string to be converted</param>
/// <param name="format">The format of the input date</param>
/// <see href="https://www.postgresql.org/docs/current/functions-formatting.html" />
public static DateTime? ToTimestamp(this DbFunctions _, string? value, string? format)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(ToTimestamp)));
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ public class NpgsqlStringMethodTranslator : IMethodCallTranslator
private static readonly MethodInfo StringToArrayNullString = typeof(NpgsqlDbFunctionsExtensions).GetRuntimeMethod(
nameof(NpgsqlDbFunctionsExtensions.StringToArray), [typeof(DbFunctions), typeof(string), typeof(string), typeof(string)])!;

private static readonly MethodInfo ToDate = typeof(NpgsqlDbFunctionsExtensions).GetRuntimeMethod(
nameof(NpgsqlDbFunctionsExtensions.ToDate), [typeof(DbFunctions), typeof(string), typeof(string)])!;

private static readonly MethodInfo ToTimestamp = typeof(NpgsqlDbFunctionsExtensions).GetRuntimeMethod(
nameof(NpgsqlDbFunctionsExtensions.ToTimestamp), [typeof(DbFunctions), typeof(string), typeof(string)])!;

private static readonly MethodInfo FirstOrDefaultMethodInfoWithoutArgs
= typeof(Enumerable).GetRuntimeMethods().Single(
m => m.Name == nameof(Enumerable.FirstOrDefault)
Expand Down Expand Up @@ -425,6 +431,30 @@ public NpgsqlStringMethodTranslator(NpgsqlTypeMappingSource typeMappingSource, I
_typeMappingSource.FindMapping(typeof(string[])));
}

if (method == ToDate)
{
return _sqlExpressionFactory.Function(
"to_date",
new[] { arguments[1], arguments[2] },
nullable: true,
argumentsPropagateNullability: new[] { true, true },
typeof(DateOnly?),
_typeMappingSource.FindMapping(typeof(DateOnly))
);
}

if (method == ToTimestamp)
{
return _sqlExpressionFactory.Function(
"to_timestamp",
new[] { arguments[1], arguments[2] },
nullable: true,
argumentsPropagateNullability: new[] { true, true },
typeof(DateTime?),
_typeMappingSource.FindMapping(typeof(DateTime))
);
}

return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,34 @@ WHERE string_to_array(c."ContactName", ' ', 'Maria') = ARRAY[NULL,'Anders']::tex
""");
}

[Fact]
public void ToDate()
{
using var context = CreateContext();
var count = context.Orders.Count(c => EF.Functions.ToDate(c.OrderDate.ToString(), "YYYY-MM-DD") < new DateOnly(2000, 01, 01));
Assert.Equal(830, count);
AssertSql(
"""
SELECT count(*)::int
FROM "Orders" AS o
WHERE to_date(o."OrderDate"::text, 'YYYY-MM-DD') < DATE '2000-01-01'
""");
}

[Fact]
public void ToTimestamp()
{
using var context = CreateContext();
var count = context.Orders.Count(c => EF.Functions.ToTimestamp(c.OrderDate.ToString(), "YYYY-MM-DD") < new DateTime(2000, 01, 01, 0,0,0, DateTimeKind.Utc));
Assert.Equal(830, count);
AssertSql(
"""
SELECT count(*)::int
FROM "Orders" AS o
WHERE to_timestamp(o."OrderDate"::text, 'YYYY-MM-DD') < TIMESTAMPTZ '2000-01-01T00:00:00Z'
""");
}

#endregion

private void AssertSql(params string[] expected)
Expand Down
Loading