Skip to content

Commit

Permalink
Adds support for PostgreSQL network address (inet/cidr/macaddr) opera…
Browse files Browse the repository at this point in the history
…tors.
  • Loading branch information
austindrenski committed May 15, 2018
1 parent 5655b1c commit 5842fad
Show file tree
Hide file tree
Showing 6 changed files with 521 additions and 1 deletion.
77 changes: 77 additions & 0 deletions src/EFCore.PG/NpgsqlNetworkAddressExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#region License

// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

#endregion

using System;
using System.Net;
using NpgsqlTypes;

namespace Npgsql.EntityFrameworkCore.PostgreSQL
{
/// <summary>
/// Provides extension methods supporting PostgreSQL network address operator translation.
/// </summary>
public static class NpgsqlNetworkAddressExtensions
{
/// <summary>
/// Determines whether an <see cref="IPAddress"/> contains another <see cref="IPAddress"/>.
/// </summary>
/// <param name="ipAddress">The IP address to search.</param>
/// <param name="other">The IP address to locate.</param>
/// <returns>
/// <value>true</value> if the <see cref="IPAddress"/> contains the other <see cref="IPAddress"/>; otherwise, <value>false</value>.
/// </returns>
public static bool Contains(this IPAddress ipAddress, IPAddress other) => throw new NotImplementedException();

/// <summary>
/// Determines whether an <see cref="NpgsqlInet"/> contains another <see cref="NpgsqlInet"/>.
/// </summary>
/// <param name="inet">The inet to search.</param>
/// <param name="other">The inet to locate.</param>
/// <returns>
/// <value>true</value> if the <see cref="NpgsqlInet"/> contains the other <see cref="NpgsqlInet"/>; otherwise, <value>false</value>.
/// </returns>
public static bool Contains(this NpgsqlInet inet, NpgsqlInet other) => throw new NotImplementedException();

/// <summary>
/// Determines whether an <see cref="IPAddress"/> contains or is equal to another <see cref="IPAddress"/>.
/// </summary>
/// <param name="ipAddress">The IP address to search.</param>
/// <param name="other">The IP address to locate.</param>
/// <returns>
/// <value>true</value> if the <see cref="IPAddress"/> contains or is equal to the other <see cref="IPAddress"/>; otherwise, <value>false</value>.
/// </returns>
public static bool ContainsOrEquals(this IPAddress ipAddress, IPAddress other) => throw new NotImplementedException();

/// <summary>
/// Determines whether an <see cref="NpgsqlInet"/> contains or is equal to another <see cref="NpgsqlInet"/>.
/// </summary>
/// <param name="inet">The inet to search.</param>
/// <param name="other">The inet to locate.</param>
/// <returns>
/// <value>true</value> if the <see cref="NpgsqlInet"/> contains or is equal to the other <see cref="NpgsqlInet"/>; otherwise, <value>false</value>.
/// </returns>
public static bool ContainsOrEquals(this NpgsqlInet inet, NpgsqlInet other) => throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ public class NpgsqlCompositeMethodCallTranslator : RelationalCompositeMethodCall
new NpgsqlStringTrimEndTranslator(),
new NpgsqlStringTrimStartTranslator(),
new NpgsqlRegexIsMatchTranslator(),
new NpgsqlFullTextSearchMethodTranslator()
new NpgsqlFullTextSearchMethodTranslator(),
new NpgsqlNetworkAddressTranslator()
};

public NpgsqlCompositeMethodCallTranslator(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#region License

// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

#endregion

using System;
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal
{
/// <summary>
/// Provides translation services for PostgreSQL network address (inet, cidr) operators.
/// </summary>
/// <remarks>
/// See: https://www.postgresql.org/docs/current/static/functions-net.html
/// </remarks>
public class NpgsqlNetworkAddressTranslator : IMethodCallTranslator
{
/// <inheritdoc />
[CanBeNull]
public Expression Translate(MethodCallExpression methodCallExpression) =>
TryTranslateOperator(methodCallExpression);

/// <summary>
/// Attempts to translate the <see cref="MethodCallExpression"/> as a PostgreSQL network address operator.
/// </summary>
/// <param name="expression">The <see cref="MethodCallExpression"/> to be translated.</param>
/// <returns>
/// The expression if successful; otherwise, null.
/// </returns>
[CanBeNull]
static Expression TryTranslateOperator([NotNull] MethodCallExpression expression)
{
switch (expression.Method.Name)
{
case nameof(NpgsqlNetworkAddressExtensions.Contains):
return MakeBinaryExpression(expression, ">>", typeof(bool));

case nameof(NpgsqlNetworkAddressExtensions.ContainsOrEquals):
return MakeBinaryExpression(expression, ">>=", typeof(bool));

// case nameof(NpgsqlNetworkAddressExtensions.Overlaps):
// return MakeBinaryExpression(expression, "&&", typeof(bool));
//
// case nameof(NpgsqlNetworkAddressExtensions.IsStrictlyLeftOf):
// return MakeBinaryExpression(expression, "<<", typeof(bool));
//
// case nameof(NpgsqlNetworkAddressExtensions.IsStrictlyRightOf):
// return MakeBinaryExpression(expression, ">>", typeof(bool));
//
// case nameof(NpgsqlNetworkAddressExtensions.DoesNotExtendRightOf):
// return MakeBinaryExpression(expression, "&<", typeof(bool));
//
// case nameof(NpgsqlNetworkAddressExtensions.DoesNotExtendLeftOf):
// return MakeBinaryExpression(expression, "&>", typeof(bool));
//
// case nameof(NpgsqlNetworkAddressExtensions.IsAdjacentTo):
// return MakeBinaryExpression(expression, "-|-", typeof(bool));

default:
return null;
}
}

/// <summary>
/// Constructs a <see cref="CustomBinaryExpression"/>.
/// </summary>
/// <param name="expression">The <see cref="MethodCallExpression"/> containing two parameters.</param>
/// <param name="symbol">The symbolic operator for PostgreSQL.</param>
/// <param name="returnType">The return type of the operator.</param>
/// <returns>
/// A <see cref="CustomBinaryExpression"/>.
/// </returns>
[NotNull]
static Expression MakeBinaryExpression([NotNull] MethodCallExpression expression, [NotNull] string symbol, [NotNull] Type returnType) =>
new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], symbol, returnType);

// [NpgsqlBinaryOperator(Symbol = "<", ReturnType = typeof(bool))] LessThan,
// [NpgsqlBinaryOperator(Symbol = "<=", ReturnType = typeof(bool))] LessThanOrEqual,
// [NpgsqlBinaryOperator(Symbol = "=", ReturnType = typeof(bool))] Equal,
// [NpgsqlBinaryOperator(Symbol = ">=", ReturnType = typeof(bool))] GreaterThanOrEqual,
// [NpgsqlBinaryOperator(Symbol = ">", ReturnType = typeof(bool))] GreaterThan,
// [NpgsqlBinaryOperator(Symbol = "<>", ReturnType = typeof(bool))] NotEqual,
// [NpgsqlBinaryOperator(Symbol = "<<", ReturnType = typeof(bool))] ContainedWithin,
// [NpgsqlBinaryOperator(Symbol = "<<=", ReturnType = typeof(bool))] ContainedWithinOrEquals,
// [NpgsqlBinaryOperator(Symbol = "~", ReturnType = typeof(bool))] Not,
// [NpgsqlBinaryOperator(Symbol = "&", ReturnType = typeof(bool))] And,
// [NpgsqlBinaryOperator(Symbol = "|", ReturnType = typeof(bool))] Or,
// [NpgsqlBinaryOperator(Symbol = "+", ReturnType = typeof(bool))] Addition,
// [NpgsqlBinaryOperator(Symbol = "-", ReturnType = typeof(bool))] Subtraction,
}
}
1 change: 1 addition & 0 deletions src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ public NpgsqlTypeMappingSource([NotNull] TypeMappingSourceDependencies dependenc
{ typeof(DateTimeOffset), _timestamptzDto },
{ typeof(PhysicalAddress), _macaddr },
{ typeof(IPAddress), _inet },
{ typeof(NpgsqlInet), _cidr },
{ typeof(BitArray), _varbit },
{ typeof(Dictionary<string, string>), _hstore },
{ typeof(NpgsqlPoint), _point },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Net;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
using NpgsqlTypes;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query
{
/// <summary>
/// Represents a fixture suitable for testing network address operators.
/// </summary>
public class NetworkAddressQueryNpgsqlFixture : IDisposable
{
/// <summary>
/// The <see cref="NpgsqlTestStore"/> used for testing.
/// </summary>
private readonly NpgsqlTestStore _testStore;

/// <summary>
/// The <see cref="DbContextOptions"/> used for testing.
/// </summary>
private readonly DbContextOptions _options;

/// <summary>
/// The logger factory used for testing.
/// </summary>
public TestSqlLoggerFactory TestSqlLoggerFactory { get; }

/// <summary>
/// Initializes a <see cref="NetworkAddressQueryNpgsqlFixture"/>.
/// </summary>
// ReSharper disable once UnusedMember.Global
public NetworkAddressQueryNpgsqlFixture()
{
TestSqlLoggerFactory = new TestSqlLoggerFactory();

_testStore = NpgsqlTestStore.CreateScratch();

_options =
new DbContextOptionsBuilder()
.UseNpgsql(_testStore.ConnectionString, b => b.ApplyConfiguration())
.UseInternalServiceProvider(
new ServiceCollection()
.AddEntityFrameworkNpgsql()
.AddSingleton<ILoggerFactory>(TestSqlLoggerFactory)
.BuildServiceProvider())
.Options;

using (NetContext context = CreateContext())
{
context.Database.EnsureCreated();

context.NetTestEntities
.AddRange(
new NetTestEntity { Id = 1, InetMappedToIPAddress = new IPAddress(1), CidrMappedToNpgsqlInet = new IPAddress(1) },
new NetTestEntity { Id = 2, InetMappedToIPAddress = new IPAddress(2), CidrMappedToNpgsqlInet = new IPAddress(2) },
new NetTestEntity { Id = 3, InetMappedToIPAddress = new IPAddress(3), CidrMappedToNpgsqlInet = new IPAddress(3) },
new NetTestEntity { Id = 4, InetMappedToIPAddress = new IPAddress(4), CidrMappedToNpgsqlInet = new IPAddress(4) },
new NetTestEntity { Id = 5, InetMappedToIPAddress = new IPAddress(5), CidrMappedToNpgsqlInet = new IPAddress(5) },
new NetTestEntity { Id = 6, InetMappedToIPAddress = new IPAddress(6), CidrMappedToNpgsqlInet = new IPAddress(6) },
new NetTestEntity { Id = 7, InetMappedToIPAddress = new IPAddress(7), CidrMappedToNpgsqlInet = new IPAddress(7) },
new NetTestEntity { Id = 8, InetMappedToIPAddress = new IPAddress(8), CidrMappedToNpgsqlInet = new IPAddress(8) },
new NetTestEntity { Id = 9, InetMappedToIPAddress = new IPAddress(9), CidrMappedToNpgsqlInet = new IPAddress(9) },
new NetTestEntity { Id = 10, InetMappedToIPAddress = new IPAddress(10), CidrMappedToNpgsqlInet = new IPAddress(10) });

context.SaveChanges();
}
}

/// <summary>
/// Creates a new <see cref="NetContext"/>.
/// </summary>
/// <returns>
/// A <see cref="NetContext"/> for testing.
/// </returns>
public NetContext CreateContext()
{
return new NetContext(_options);
}

/// <inheritdoc />
public void Dispose()
{
_testStore.Dispose();
}
}

/// <summary>
/// Represents an entity suitable for testing network address operators.
/// </summary>
public class NetTestEntity
{
/// <summary>
/// The primary key.
/// </summary>
[Key]
public int Id { get; set; }

/// <summary>
/// The network address.
/// </summary>
[Column(TypeName = "inet")]
public IPAddress InetMappedToIPAddress { get; set; }

/// <summary>
/// The network address.
/// </summary>
[Column(TypeName = "cidr")]
public NpgsqlInet CidrMappedToNpgsqlInet { get; set; }
}

/// <summary>
/// Represents a database suitable for testing network address operators.
/// </summary>
public class NetContext : DbContext
{
/// <summary>
/// Represents a set of entities with <see cref="IPAddress"/> properties.
/// </summary>
public DbSet<NetTestEntity> NetTestEntities { get; set; }

/// <summary>
/// Initializes a <see cref="NetContext"/>.
/// </summary>
/// <param name="options">
/// The options to be used for configuration.
/// </param>
public NetContext(DbContextOptions options) : base(options) { }
}
}
Loading

0 comments on commit 5842fad

Please sign in to comment.