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

Map IPNetwork <-> cidr #3331

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
125 changes: 125 additions & 0 deletions src/EFCore.PG/Storage/Internal/Mapping/NpgsqlCidrLegacyTypeMapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System.Net;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;

/// <summary>
/// The type mapping for the PostgreSQL cidr type.
/// </summary>
/// <remarks>
/// See: https://www.postgresql.org/docs/current/static/datatype-net-types.html#DATATYPE-CIDR
/// </remarks>
public class NpgsqlLegacyCidrTypeMapping : NpgsqlTypeMapping
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static NpgsqlLegacyCidrTypeMapping Default { get; } = new();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public NpgsqlLegacyCidrTypeMapping()
: base("cidr", typeof(NpgsqlCidr), NpgsqlDbType.Cidr, JsonCidrLegacyReaderWriter.Instance)
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected NpgsqlLegacyCidrTypeMapping(RelationalTypeMappingParameters parameters)
: base(parameters, NpgsqlDbType.Cidr)
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
=> new NpgsqlLegacyCidrTypeMapping(parameters);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected override string GenerateNonNullSqlLiteral(object value)
{
var cidr = (NpgsqlCidr)value;
return $"CIDR '{cidr.Address}/{cidr.Netmask}'";
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override Expression GenerateCodeLiteral(object value)
{
var cidr = (NpgsqlCidr)value;
return Expression.New(
NpgsqlCidrConstructor,
Expression.Call(ParseMethod, Expression.Constant(cidr.Address.ToString())),
Expression.Constant(cidr.Netmask));
}

private static readonly MethodInfo ParseMethod = typeof(IPAddress).GetMethod("Parse", [typeof(string)])!;

private static readonly ConstructorInfo NpgsqlCidrConstructor =
typeof(NpgsqlCidr).GetConstructor([typeof(IPAddress), typeof(byte)])!;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public sealed class JsonCidrLegacyReaderWriter : JsonValueReaderWriter<NpgsqlCidr>
{
private static readonly PropertyInfo InstanceProperty = typeof(JsonCidrLegacyReaderWriter).GetProperty(nameof(Instance))!;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static JsonCidrLegacyReaderWriter Instance { get; } = new();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override NpgsqlCidr FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
=> new(manager.CurrentReader.GetString()!);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void ToJsonTyped(Utf8JsonWriter writer, NpgsqlCidr value)
=> writer.WriteStringValue(value.ToString());

/// <inheritdoc />
public override Expression ConstructorExpression => Expression.Property(null, InstanceProperty);
}
}
24 changes: 12 additions & 12 deletions src/EFCore.PG/Storage/Internal/Mapping/NpgsqlCidrTypeMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class NpgsqlCidrTypeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public NpgsqlCidrTypeMapping()
: base("cidr", typeof(NpgsqlCidr), NpgsqlDbType.Cidr, JsonCidrReaderWriter.Instance)
: base("cidr", typeof(IPNetwork), NpgsqlDbType.Cidr, JsonCidrReaderWriter.Instance)
{
}

Expand Down Expand Up @@ -59,8 +59,8 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p
/// </summary>
protected override string GenerateNonNullSqlLiteral(object value)
{
var cidr = (NpgsqlCidr)value;
return $"CIDR '{cidr.Address}/{cidr.Netmask}'";
var ipNetwork = (IPNetwork)value;
return $"CIDR '{ipNetwork.BaseAddress}/{ipNetwork.PrefixLength}'";
}

/// <summary>
Expand All @@ -71,25 +71,25 @@ protected override string GenerateNonNullSqlLiteral(object value)
/// </summary>
public override Expression GenerateCodeLiteral(object value)
{
var cidr = (NpgsqlCidr)value;
var cidr = (IPNetwork)value;
return Expression.New(
NpgsqlCidrConstructor,
Expression.Call(ParseMethod, Expression.Constant(cidr.Address.ToString())),
Expression.Constant(cidr.Netmask));
Expression.Call(ParseMethod, Expression.Constant(cidr.BaseAddress.ToString())),
Expression.Constant(cidr.PrefixLength));
}

private static readonly MethodInfo ParseMethod = typeof(IPAddress).GetMethod("Parse", [typeof(string)])!;

private static readonly ConstructorInfo NpgsqlCidrConstructor =
typeof(NpgsqlCidr).GetConstructor([typeof(IPAddress), typeof(byte)])!;
typeof(IPNetwork).GetConstructor([typeof(IPAddress), typeof(int)])!;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public sealed class JsonCidrReaderWriter : JsonValueReaderWriter<NpgsqlCidr>
public sealed class JsonCidrReaderWriter : JsonValueReaderWriter<IPNetwork>
{
private static readonly PropertyInfo InstanceProperty = typeof(JsonCidrReaderWriter).GetProperty(nameof(Instance))!;

Expand All @@ -107,17 +107,17 @@ public sealed class JsonCidrReaderWriter : JsonValueReaderWriter<NpgsqlCidr>
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override NpgsqlCidr FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
=> new(manager.CurrentReader.GetString()!);
public override IPNetwork FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
=> IPNetwork.Parse(manager.CurrentReader.GetString()!);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void ToJsonTyped(Utf8JsonWriter writer, NpgsqlCidr value)
=> writer.WriteStringValue(value.ToString());
public override void ToJsonTyped(Utf8JsonWriter writer, IPNetwork ipNetwork)
=> writer.WriteStringValue(ipNetwork.ToString());

/// <inheritdoc />
public override Expression ConstructorExpression => Expression.Property(null, InstanceProperty);
Expand Down
8 changes: 5 additions & 3 deletions src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ static NpgsqlTypeMappingSource()
private readonly NpgsqlMacaddr8TypeMapping _macaddr8 = NpgsqlMacaddr8TypeMapping.Default;
private readonly NpgsqlInetTypeMapping _inetAsIPAddress = NpgsqlInetTypeMapping.Default;
private readonly NpgsqlInetTypeMapping _inetAsNpgsqlInet = new(typeof(NpgsqlInet));
private readonly NpgsqlCidrTypeMapping _cidr = NpgsqlCidrTypeMapping.Default;
private readonly NpgsqlCidrTypeMapping _cidrAsIPNetwork = NpgsqlCidrTypeMapping.Default;
private readonly NpgsqlLegacyCidrTypeMapping _cidrAsNpgsqlCidr = NpgsqlLegacyCidrTypeMapping.Default;

// Built-in geometric types
private readonly NpgsqlPointTypeMapping _point = NpgsqlPointTypeMapping.Default;
Expand Down Expand Up @@ -257,7 +258,7 @@ public NpgsqlTypeMappingSource(
{ "macaddr", [_macaddr] },
{ "macaddr8", [_macaddr8] },
{ "inet", [_inetAsIPAddress, _inetAsNpgsqlInet] },
{ "cidr", [_cidr] },
{ "cidr", [_cidrAsIPNetwork, _cidrAsNpgsqlCidr] },
{ "point", [_point] },
{ "box", [_box] },
{ "line", [_line] },
Expand Down Expand Up @@ -320,7 +321,8 @@ public NpgsqlTypeMappingSource(
{ typeof(PhysicalAddress), _macaddr },
{ typeof(IPAddress), _inetAsIPAddress },
{ typeof(NpgsqlInet), _inetAsNpgsqlInet },
{ typeof(NpgsqlCidr), _cidr },
{ typeof(IPNetwork), _cidrAsIPNetwork },
{ typeof(NpgsqlCidr), _cidrAsNpgsqlCidr },
{ typeof(BitArray), _varbit },
{ typeof(ImmutableDictionary<string, string>), _immutableHstore },
{ typeof(Dictionary<string, string>), _hstore },
Expand Down
7 changes: 7 additions & 0 deletions test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Net;
using System.Net.NetworkInformation;
using Microsoft.EntityFrameworkCore.Storage.Json;
using NetTopologySuite.Geometries;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
Expand Down Expand Up @@ -41,6 +43,7 @@ public class NpgsqlTypeMappingSourceTest
[InlineData("geometry(POLYGONM)", typeof(Polygon), null, null, null, false)]
[InlineData("xid", typeof(uint), null, null, null, false)]
[InlineData("xid8", typeof(ulong), null, null, null, false)]
[InlineData("cidr", typeof(IPNetwork), null, null, null, false)]
public void By_StoreType(string typeName, Type type, int? size, int? precision, int? scale, bool fixedLength)
{
var mapping = CreateTypeMappingSource().FindMapping(typeName);
Expand Down Expand Up @@ -114,6 +117,10 @@ public void Timestamp_without_time_zone_Array_5()
[InlineData(typeof(List<NpgsqlRange<int>>), "int4multirange")]
[InlineData(typeof(Geometry), "geometry")]
[InlineData(typeof(Point), "geometry")]
[InlineData(typeof(IPAddress), "inet")]
[InlineData(typeof(IPNetwork), "cidr")]
[InlineData(typeof(NpgsqlCidr), "cidr")] // legacy
[InlineData(typeof(PhysicalAddress), "macaddr")]
public void By_ClrType(Type clrType, string expectedStoreType)
{
var mapping = CreateTypeMappingSource().FindMapping(clrType);
Expand Down
10 changes: 10 additions & 0 deletions test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,20 @@ public void GenerateCodeLiteral_returns_inet_literal()

[Fact]
public void GenerateSqlLiteral_returns_cidr_literal()
=> Assert.Equal("CIDR '192.168.1.0/24'", GetMapping("cidr").GenerateSqlLiteral(new IPNetwork(IPAddress.Parse("192.168.1.0"), 24)));

[Fact]
public void GenerateSqlLiteral_returns_legacy_cidr_literal()
=> Assert.Equal("CIDR '192.168.1.0/24'", GetMapping("cidr").GenerateSqlLiteral(new NpgsqlCidr(IPAddress.Parse("192.168.1.0"), 24)));

[Fact]
public void GenerateCodeLiteral_returns_cidr_literal()
=> Assert.Equal(
"""new System.Net.IPNetwork(System.Net.IPAddress.Parse("192.168.1.0"), 24)""",
CodeLiteral(new IPNetwork(IPAddress.Parse("192.168.1.0"), 24)));

[Fact]
public void GenerateCodeLiteral_returns_legacy_cidr_literal()
=> Assert.Equal(
"""new NpgsqlTypes.NpgsqlCidr(System.Net.IPAddress.Parse("192.168.1.0"), (byte)24)""",
CodeLiteral(new NpgsqlCidr(IPAddress.Parse("192.168.1.0"), 24)));
Expand Down
Loading