Skip to content
Merged
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
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<EFCoreVersion>10.0.0-preview.5.25277.114</EFCoreVersion>
<MicrosoftExtensionsVersion>10.0.0-preview.5.25277.114</MicrosoftExtensionsVersion>
<EFCoreVersion>10.0.0-preview.6.25314.101</EFCoreVersion>
<MicrosoftExtensionsVersion>10.0.0-preview.6.25314.101</MicrosoftExtensionsVersion>
<NpgsqlVersion>9.0.3</NpgsqlVersion>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ public NpgsqlNetTopologySuiteMethodCallTranslatorPlugin(
/// </summary>
public class NpgsqlGeometryMethodTranslator : IMethodCallTranslator
{
private static readonly MethodInfo _collectionItem = typeof(GeometryCollection).GetRuntimeProperty("Item")!.GetMethod!;

private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory;
private readonly IRelationalTypeMappingSource _typeMappingSource;

Expand Down Expand Up @@ -77,11 +75,31 @@ public NpgsqlGeometryMethodTranslator(
MethodInfo method,
IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
=> method.DeclaringType == typeof(NpgsqlNetTopologySuiteDbFunctionsExtensions)
? TranslateDbFunction(method, arguments)
: instance is not null && typeof(Geometry).IsAssignableFrom(method.DeclaringType)
? TranslateGeometryMethod(instance, method, arguments)
: null;
=> method.DeclaringType switch
{
var t when typeof(Geometry).IsAssignableFrom(t) && instance is not null
=> TranslateGeometryMethod(instance, method, arguments),

var t when t == typeof(NpgsqlNetTopologySuiteDbFunctionsExtensions)
=> TranslateDbFunction(method, arguments),

// This handles the collection indexer (geom_collection[x] -> ST_GeometryN(geom_collection, x + 1))
// This is needed as a special case because EF transforms the indexer into a call to Enumerable.ElementAt
var t when t == typeof(Enumerable)
&& method.Name is nameof(Enumerable.ElementAt)
&& method.ReturnType == typeof(Geometry)
&& arguments is [var collection, var index]
&& _typeMappingSource.FindMapping(typeof(Geometry), collection.TypeMapping!.StoreType) is RelationalTypeMapping geometryTypeMapping
=> _sqlExpressionFactory.Function(
"ST_GeometryN",
[collection, OneBased(index)],
nullable: true,
argumentsPropagateNullability: TrueArrays[2],
method.ReturnType,
geometryTypeMapping),

_ => null
};

private SqlExpression? TranslateDbFunction(
MethodInfo method,
Expand Down Expand Up @@ -143,17 +161,6 @@ public NpgsqlGeometryMethodTranslator(

arguments = typeMappedArguments;

if (Equals(method, _collectionItem))
{
return _sqlExpressionFactory.Function(
"ST_GeometryN",
[instance, OneBased(arguments[0])],
nullable: true,
argumentsPropagateNullability: TrueArrays[2],
method.ReturnType,
_typeMappingSource.FindMapping(typeof(Geometry), instance.TypeMapping!.StoreType));
}

return method.Name switch
{
nameof(Geometry.AsBinary)
Expand Down Expand Up @@ -226,16 +233,16 @@ SqlExpression Function(string name, SqlExpression[] arguments, Type returnType,
nullable: true, argumentsPropagateNullability: TrueArrays[arguments.Length],
returnType, typeMapping);

// NetTopologySuite uses 0-based indexing, but PostGIS uses 1-based
SqlExpression OneBased(SqlExpression arg)
=> arg is SqlConstantExpression constant
? _sqlExpressionFactory.Constant((int)constant.Value! + 1, constant.TypeMapping)
: _sqlExpressionFactory.Add(arg, _sqlExpressionFactory.Constant(1));

RelationalTypeMapping ResultGeometryMapping()
{
Debug.Assert(typeof(Geometry).IsAssignableFrom(method.ReturnType));
return _typeMappingSource.FindMapping(method.ReturnType, storeType)!;
}
}

// NetTopologySuite uses 0-based indexing, but PostGIS uses 1-based
private SqlExpression OneBased(SqlExpression arg)
=> arg is SqlConstantExpression constant
? _sqlExpressionFactory.Constant((int)constant.Value! + 1, constant.TypeMapping)
: _sqlExpressionFactory.Add(arg, _sqlExpressionFactory.Constant(1));
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ WHERE floor(date_part('dow', b."DateOnly"))::int = 6
""");
}

public override async Task DayNumber(bool async)
{
await base.DayNumber(async);

AssertSql(
"""
SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan"
FROM "BasicTypesEntities" AS b
WHERE b."DateOnly" - DATE '0001-01-01' = 726780
""");
}

public override async Task AddYears(bool async)
{
await base.AddYears(async);
Expand Down Expand Up @@ -105,6 +117,20 @@ public override async Task AddDays(bool async)
""");
}

public override async Task DayNumber_subtraction(bool async)
{
await base.DayNumber_subtraction(async);

AssertSql(
"""
@DayNumber='726775'

SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan"
FROM "BasicTypesEntities" AS b
WHERE (b."DateOnly" - DATE '0001-01-01') - @DayNumber = 5
""");
}

public override async Task FromDateTime(bool async)
{
await base.FromDateTime(async);
Expand Down